Skip to content

Commit 22b6a15

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 22b6a15

File tree

3 files changed

+47
-10
lines changed

3 files changed

+47
-10
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: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,22 @@ 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> {
4647
auto description = TRY(fds.open_file_description(action->old_fd));
4748
if (action->old_fd != action->new_fd) {
4849
if (action->new_fd < 0 || static_cast<size_t>(action->new_fd) >= fds.max_open())
4950
return EINVAL;
50-
if (!fds.m_fds_metadatas[action->new_fd].is_allocated())
51+
52+
if (fds.m_fds_metadatas[action->new_fd].is_allocated()) {
53+
if (auto* old_description = fds[action->new_fd].description())
54+
(void)old_description->close();
55+
fds[action->new_fd].clear();
56+
} else {
5157
fds.m_fds_metadatas[action->new_fd].allocate();
58+
}
59+
5260
fds[action->new_fd].set(move(description));
5361
}
5462
return {};
@@ -58,6 +66,7 @@ ErrorOr<void> Process::execute_file_actions(ReadonlyBytes file_actions_data)
5866
case SpawnFileActionType::Close: {
5967
if (header->record_length < sizeof(SpawnFileActionClose))
6068
return EINVAL;
69+
6170
auto const* action = reinterpret_cast<SpawnFileActionClose const*>(header);
6271
TRY(m_fds.with_exclusive([&](auto& fds) -> ErrorOr<void> {
6372
auto description = TRY(fds.open_file_description(action->fd));
@@ -70,9 +79,11 @@ ErrorOr<void> Process::execute_file_actions(ReadonlyBytes file_actions_data)
7079
case SpawnFileActionType::Open: {
7180
if (header->record_length < sizeof(SpawnFileActionOpen))
7281
return EINVAL;
82+
7383
auto const* action = reinterpret_cast<SpawnFileActionOpen const*>(header);
7484
if (header->record_length < sizeof(SpawnFileActionOpen) + action->path_length)
7585
return EINVAL;
86+
7687
auto path = TRY(KString::try_create(StringView { action->path, action->path_length }));
7788
CustodyBase base(AT_FDCWD, path->view());
7889
auto description = TRY(VirtualFileSystem::open(
@@ -85,20 +96,30 @@ ErrorOr<void> Process::execute_file_actions(ReadonlyBytes file_actions_data)
8596
TRY(m_fds.with_exclusive([&](auto& fds) -> ErrorOr<void> {
8697
if (action->fd < 0 || static_cast<size_t>(action->fd) >= fds.max_open())
8798
return EINVAL;
88-
if (!fds.m_fds_metadatas[action->fd].is_allocated())
99+
100+
if (fds.m_fds_metadatas[action->fd].is_allocated()) {
101+
if (auto* old_description = fds[action->fd].description())
102+
(void)old_description->close();
103+
fds[action->fd].clear();
104+
} else {
89105
fds.m_fds_metadatas[action->fd].allocate();
106+
}
107+
90108
u32 fd_flags = (action->flags & O_CLOEXEC) ? FD_CLOEXEC : 0;
91109
fds[action->fd].set(move(description), fd_flags);
92110
return {};
93111
}));
94112
break;
95113
}
114+
96115
case SpawnFileActionType::Chdir: {
97116
if (header->record_length < sizeof(SpawnFileActionChdir))
98117
return EINVAL;
118+
99119
auto const* action = reinterpret_cast<SpawnFileActionChdir const*>(header);
100120
if (header->record_length < sizeof(SpawnFileActionChdir) + action->path_length)
101121
return EINVAL;
122+
102123
auto path = TRY(KString::try_create(StringView { action->path, action->path_length }));
103124
auto new_directory = TRY(VirtualFileSystem::open_directory(
104125
vfs_root_context(), credentials(), path->view(), current_directory()));
@@ -110,12 +131,16 @@ ErrorOr<void> Process::execute_file_actions(ReadonlyBytes file_actions_data)
110131
case SpawnFileActionType::Fchdir: {
111132
if (header->record_length < sizeof(SpawnFileActionFchdir))
112133
return EINVAL;
134+
113135
auto const* action = reinterpret_cast<SpawnFileActionFchdir const*>(header);
114136
auto description = TRY(open_file_description(action->fd));
115137
if (!description->is_directory())
116138
return ENOTDIR;
139+
140+
// Check for search (+x) permission on the directory.
117141
if (!description->metadata().may_execute(credentials()))
118142
return EACCES;
143+
119144
m_current_directory.with([&](auto& current_directory) {
120145
current_directory = description->custody();
121146
});
@@ -183,9 +208,9 @@ ErrorOr<FlatPtr> Process::sys$posix_spawn(Userspace<Syscall::SC_posix_spawn_para
183208
Vector<NonnullOwnPtr<KString>> environment;
184209
TRY(copy_user_strings(params.environment, environment));
185210

186-
auto const& creds = this->credentials();
211+
auto const& credentials = this->credentials();
187212
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()));
213+
auto [child, child_first_thread] = TRY(Process::create_spawned(credentials->uid(), credentials->gid(), pid(), vfs_root_context(), hostname_context(), current_directory(), tty()));
189214

190215
ArmedScopeGuard thread_finalizer_guard = [&child_first_thread]() {
191216
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)