Skip to content

Commit a8e5617

Browse files
Kernel+LibC: Fix posix_spawn file action alignment and fd handling
- Explicitly close existing file descriptors in Dup2/Open actions per POSIX dup2() semantics - Fix alignment by padding variable-length records to ensure proper struct alignment without [[gnu::packed]] - Styling improvements
1 parent 9ebc383 commit a8e5617

File tree

3 files changed

+50
-12
lines changed

3 files changed

+50
-12
lines changed

Kernel/API/POSIX/spawn.h

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
#pragma once
88

9+
#include <AK/StdLibExtras.h>
910
#include <AK/Types.h>
1011
#include <sys/types.h>
1112

@@ -19,18 +20,18 @@ enum class SpawnFileActionType : u8 {
1920
Fchdir = 4,
2021
};
2122

22-
struct [[gnu::packed]] SpawnFileActionHeader {
23+
struct SpawnFileActionHeader {
2324
SpawnFileActionType type;
2425
u16 record_length;
2526
};
2627

27-
struct [[gnu::packed]] SpawnFileActionDup2 {
28+
struct SpawnFileActionDup2 {
2829
SpawnFileActionHeader header;
2930
i32 old_fd;
3031
i32 new_fd;
3132
};
3233

33-
struct [[gnu::packed]] SpawnFileActionOpen {
34+
struct SpawnFileActionOpen {
3435
SpawnFileActionHeader header;
3536
i32 fd;
3637
i32 flags;
@@ -39,20 +40,27 @@ struct [[gnu::packed]] SpawnFileActionOpen {
3940
char path[];
4041
};
4142

42-
struct [[gnu::packed]] SpawnFileActionClose {
43+
struct SpawnFileActionClose {
4344
SpawnFileActionHeader header;
4445
i32 fd;
4546
};
4647

47-
struct [[gnu::packed]] SpawnFileActionChdir {
48+
struct SpawnFileActionChdir {
4849
SpawnFileActionHeader header;
4950
u16 path_length;
5051
char path[];
5152
};
5253

53-
struct [[gnu::packed]] SpawnFileActionFchdir {
54+
struct SpawnFileActionFchdir {
5455
SpawnFileActionHeader header;
5556
i32 fd;
5657
};
5758

59+
// Maximum alignment across all SpawnFileAction structs.
60+
// Used to ensure proper alignment when serializing variable-length records.
61+
inline constexpr size_t SpawnFileActionAlignment = max(
62+
max(alignof(SpawnFileActionHeader), alignof(SpawnFileActionDup2)),
63+
max(max(alignof(SpawnFileActionOpen), alignof(SpawnFileActionClose)),
64+
max(alignof(SpawnFileActionChdir), alignof(SpawnFileActionFchdir))));
65+
5866
}

Kernel/Syscalls/posix_spawn.cpp

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,23 @@ ErrorOr<void> Process::execute_file_actions(ReadonlyBytes file_actions_data)
4141
case SpawnFileActionType::Dup2: {
4242
if (header->record_length < sizeof(SpawnFileActionDup2))
4343
return EINVAL;
44+
4445
auto const* action = reinterpret_cast<SpawnFileActionDup2 const*>(header);
4546
TRY(m_fds.with_exclusive([&](auto& fds) -> ErrorOr<void> {
47+
if (action->new_fd < 0 || static_cast<size_t>(action->new_fd) >= fds.max_open())
48+
return EINVAL;
49+
4650
auto description = TRY(fds.open_file_description(action->old_fd));
4751
if (action->old_fd != action->new_fd) {
48-
if (action->new_fd < 0 || static_cast<size_t>(action->new_fd) >= fds.max_open())
49-
return EINVAL;
50-
if (!fds.m_fds_metadatas[action->new_fd].is_allocated())
52+
53+
if (fds.m_fds_metadatas[action->new_fd].is_allocated()) {
54+
if (auto* old_description = fds[action->new_fd].description())
55+
(void)old_description->close();
56+
fds[action->new_fd].clear();
57+
} else {
5158
fds.m_fds_metadatas[action->new_fd].allocate();
59+
}
60+
5261
fds[action->new_fd].set(move(description));
5362
}
5463
return {};
@@ -58,6 +67,7 @@ ErrorOr<void> Process::execute_file_actions(ReadonlyBytes file_actions_data)
5867
case SpawnFileActionType::Close: {
5968
if (header->record_length < sizeof(SpawnFileActionClose))
6069
return EINVAL;
70+
6171
auto const* action = reinterpret_cast<SpawnFileActionClose const*>(header);
6272
TRY(m_fds.with_exclusive([&](auto& fds) -> ErrorOr<void> {
6373
auto description = TRY(fds.open_file_description(action->fd));
@@ -70,9 +80,11 @@ ErrorOr<void> Process::execute_file_actions(ReadonlyBytes file_actions_data)
7080
case SpawnFileActionType::Open: {
7181
if (header->record_length < sizeof(SpawnFileActionOpen))
7282
return EINVAL;
83+
7384
auto const* action = reinterpret_cast<SpawnFileActionOpen const*>(header);
7485
if (header->record_length < sizeof(SpawnFileActionOpen) + action->path_length)
7586
return EINVAL;
87+
7688
auto path = TRY(KString::try_create(StringView { action->path, action->path_length }));
7789
CustodyBase base(AT_FDCWD, path->view());
7890
auto description = TRY(VirtualFileSystem::open(
@@ -85,20 +97,30 @@ ErrorOr<void> Process::execute_file_actions(ReadonlyBytes file_actions_data)
8597
TRY(m_fds.with_exclusive([&](auto& fds) -> ErrorOr<void> {
8698
if (action->fd < 0 || static_cast<size_t>(action->fd) >= fds.max_open())
8799
return EINVAL;
88-
if (!fds.m_fds_metadatas[action->fd].is_allocated())
100+
101+
if (fds.m_fds_metadatas[action->fd].is_allocated()) {
102+
if (auto* old_description = fds[action->fd].description())
103+
(void)old_description->close();
104+
fds[action->fd].clear();
105+
} else {
89106
fds.m_fds_metadatas[action->fd].allocate();
107+
}
108+
90109
u32 fd_flags = (action->flags & O_CLOEXEC) ? FD_CLOEXEC : 0;
91110
fds[action->fd].set(move(description), fd_flags);
92111
return {};
93112
}));
94113
break;
95114
}
115+
96116
case SpawnFileActionType::Chdir: {
97117
if (header->record_length < sizeof(SpawnFileActionChdir))
98118
return EINVAL;
119+
99120
auto const* action = reinterpret_cast<SpawnFileActionChdir const*>(header);
100121
if (header->record_length < sizeof(SpawnFileActionChdir) + action->path_length)
101122
return EINVAL;
123+
102124
auto path = TRY(KString::try_create(StringView { action->path, action->path_length }));
103125
auto new_directory = TRY(VirtualFileSystem::open_directory(
104126
vfs_root_context(), credentials(), path->view(), current_directory()));
@@ -110,12 +132,16 @@ ErrorOr<void> Process::execute_file_actions(ReadonlyBytes file_actions_data)
110132
case SpawnFileActionType::Fchdir: {
111133
if (header->record_length < sizeof(SpawnFileActionFchdir))
112134
return EINVAL;
135+
113136
auto const* action = reinterpret_cast<SpawnFileActionFchdir const*>(header);
114137
auto description = TRY(open_file_description(action->fd));
115138
if (!description->is_directory())
116139
return ENOTDIR;
140+
141+
// Check for search (+x) permission on the directory.
117142
if (!description->metadata().may_execute(credentials()))
118143
return EACCES;
144+
119145
m_current_directory.with([&](auto& current_directory) {
120146
current_directory = description->custody();
121147
});
@@ -183,9 +209,9 @@ ErrorOr<FlatPtr> Process::sys$posix_spawn(Userspace<Syscall::SC_posix_spawn_para
183209
Vector<NonnullOwnPtr<KString>> environment;
184210
TRY(copy_user_strings(params.environment, environment));
185211

186-
auto const& creds = this->credentials();
212+
auto const& credentials = this->credentials();
187213
VERIFY(!m_is_kernel_process);
188-
auto [child, child_first_thread] = TRY(Process::create_spawned(creds->uid(), creds->gid(), pid(), vfs_root_context(), hostname_context(), current_directory(), tty()));
214+
auto [child, child_first_thread] = TRY(Process::create_spawned(credentials->uid(), credentials->gid(), pid(), vfs_root_context(), hostname_context(), current_directory(), tty()));
189215

190216
ArmedScopeGuard thread_finalizer_guard = [&child_first_thread]() {
191217
SpinlockLocker lock(g_scheduler_lock);

Userland/Libraries/LibC/spawn.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,8 @@ int posix_spawn_file_actions_addchdir(posix_spawn_file_actions_t* actions, char
282282
size_t path_len = strlen(path);
283283
size_t record_size = sizeof(SpawnFileActionChdir) + path_len + 1;
284284

285+
record_size = align_up_to(record_size, SpawnFileActionAlignment);
286+
285287
auto buffer_or_error = ByteBuffer::create_uninitialized(record_size);
286288
if (buffer_or_error.is_error())
287289
return ENOMEM;
@@ -344,6 +346,8 @@ int posix_spawn_file_actions_addopen(posix_spawn_file_actions_t* actions, int wa
344346
size_t path_len = strlen(path);
345347
size_t record_size = sizeof(SpawnFileActionOpen) + path_len + 1;
346348

349+
record_size = align_up_to(record_size, SpawnFileActionAlignment);
350+
347351
auto buffer_or_error = ByteBuffer::create_uninitialized(record_size);
348352
if (buffer_or_error.is_error())
349353
return ENOMEM;

0 commit comments

Comments
 (0)