Skip to content

Commit 3e30d9b

Browse files
committed
Kernel: Make ProcessGroup a ListedRefCounted and fix two races
This closes two race windows: - ProcessGroup removed itself from the "all process groups" list in its destructor. It was possible to walk the list between the last unref() and the destructor invocation, and grab a pointer to a ProcessGroup that was about to get deleted. - sys$setsid() could end up creating a process group that already existed, as there was a race window between checking if the PGID is used, and actually creating a ProcessGroup with that PGID.
1 parent 37bfc36 commit 3e30d9b

File tree

3 files changed

+29
-36
lines changed

3 files changed

+29
-36
lines changed

Kernel/ProcessGroup.cpp

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*
22
* Copyright (c) 2020, the SerenityOS developers.
3-
* Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
3+
* Copyright (c) 2021-2023, Andreas Kling <kling@serenityos.org>
44
*
55
* SPDX-License-Identifier: BSD-2-Clause
66
*/
@@ -10,45 +10,44 @@
1010

1111
namespace Kernel {
1212

13-
static Singleton<SpinlockProtected<ProcessGroup::List, LockRank::None>> s_process_groups;
13+
static Singleton<SpinlockProtected<ProcessGroup::AllInstancesList, LockRank::None>> s_all_instances;
1414

15-
SpinlockProtected<ProcessGroup::List, LockRank::None>& process_groups()
15+
SpinlockProtected<ProcessGroup::AllInstancesList, LockRank::None>& ProcessGroup::all_instances()
1616
{
17-
return *s_process_groups;
17+
return s_all_instances;
1818
}
1919

20-
ProcessGroup::~ProcessGroup()
21-
{
22-
process_groups().with([&](auto& groups) {
23-
groups.remove(*this);
24-
});
25-
}
20+
ProcessGroup::~ProcessGroup() = default;
2621

27-
ErrorOr<NonnullRefPtr<ProcessGroup>> ProcessGroup::create(ProcessGroupID pgid)
22+
ErrorOr<NonnullRefPtr<ProcessGroup>> ProcessGroup::create_if_unused_pgid(ProcessGroupID pgid)
2823
{
29-
auto process_group = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) ProcessGroup(pgid)));
30-
process_groups().with([&](auto& groups) {
31-
groups.prepend(*process_group);
24+
return all_instances().with([&](auto& all_instances) -> ErrorOr<NonnullRefPtr<ProcessGroup>> {
25+
for (auto& process_group : all_instances) {
26+
if (process_group.pgid() == pgid)
27+
return EPERM;
28+
}
29+
auto process_group = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) ProcessGroup(pgid)));
30+
all_instances.prepend(*process_group);
31+
return process_group;
3232
});
33-
return process_group;
3433
}
3534

3635
ErrorOr<NonnullRefPtr<ProcessGroup>> ProcessGroup::find_or_create(ProcessGroupID pgid)
3736
{
38-
return process_groups().with([&](auto& groups) -> ErrorOr<NonnullRefPtr<ProcessGroup>> {
39-
for (auto& group : groups) {
37+
return all_instances().with([&](auto& all_instances) -> ErrorOr<NonnullRefPtr<ProcessGroup>> {
38+
for (auto& group : all_instances) {
4039
if (group.pgid() == pgid)
4140
return group;
4241
}
4342
auto process_group = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) ProcessGroup(pgid)));
44-
groups.prepend(*process_group);
43+
all_instances.prepend(*process_group);
4544
return process_group;
4645
});
4746
}
4847

4948
RefPtr<ProcessGroup> ProcessGroup::from_pgid(ProcessGroupID pgid)
5049
{
51-
return process_groups().with([&](auto& groups) -> RefPtr<ProcessGroup> {
50+
return all_instances().with([&](auto& groups) -> RefPtr<ProcessGroup> {
5251
for (auto& group : groups) {
5352
if (group.pgid() == pgid)
5453
return &group;

Kernel/ProcessGroup.h

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
11
/*
2-
* Copyright (c) 2020, the SerenityOS developers.
2+
* Copyright (c) 2020-2023, the SerenityOS developers.
33
*
44
* SPDX-License-Identifier: BSD-2-Clause
55
*/
66

77
#pragma once
88

9-
#include <AK/AtomicRefCounted.h>
109
#include <AK/IntrusiveList.h>
1110
#include <AK/RefPtr.h>
1211
#include <Kernel/Forward.h>
12+
#include <Kernel/Library/ListedRefCounted.h>
1313
#include <Kernel/Library/LockWeakable.h>
1414
#include <Kernel/Locking/SpinlockProtected.h>
1515
#include <Kernel/UnixTypes.h>
1616

1717
namespace Kernel {
1818

1919
class ProcessGroup
20-
: public AtomicRefCounted<ProcessGroup>
20+
: public ListedRefCounted<ProcessGroup, LockType::Spinlock>
2121
, public LockWeakable<ProcessGroup> {
2222

2323
AK_MAKE_NONMOVABLE(ProcessGroup);
@@ -26,7 +26,7 @@ class ProcessGroup
2626
public:
2727
~ProcessGroup();
2828

29-
static ErrorOr<NonnullRefPtr<ProcessGroup>> create(ProcessGroupID);
29+
static ErrorOr<NonnullRefPtr<ProcessGroup>> create_if_unused_pgid(ProcessGroupID);
3030
static ErrorOr<NonnullRefPtr<ProcessGroup>> find_or_create(ProcessGroupID);
3131
static RefPtr<ProcessGroup> from_pgid(ProcessGroupID);
3232

@@ -38,13 +38,13 @@ class ProcessGroup
3838
{
3939
}
4040

41-
IntrusiveListNode<ProcessGroup> m_list_node;
4241
ProcessGroupID m_pgid;
4342

43+
mutable IntrusiveListNode<ProcessGroup> m_list_node;
44+
4445
public:
45-
using List = IntrusiveList<&ProcessGroup::m_list_node>;
46+
using AllInstancesList = IntrusiveList<&ProcessGroup::m_list_node>;
47+
static SpinlockProtected<AllInstancesList, LockRank::None>& all_instances();
4648
};
4749

48-
SpinlockProtected<ProcessGroup::List, LockRank::None>& process_groups();
49-
5050
}

Kernel/Syscalls/setpgid.cpp

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,16 +28,10 @@ ErrorOr<FlatPtr> Process::sys$setsid()
2828
{
2929
VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this);
3030
TRY(require_promise(Pledge::proc));
31-
bool found_process_with_same_pgid_as_my_pid = false;
32-
TRY(Process::for_each_in_pgrp_in_same_jail(pid().value(), [&](auto&) -> ErrorOr<void> {
33-
found_process_with_same_pgid_as_my_pid = true;
34-
return {};
35-
}));
36-
if (found_process_with_same_pgid_as_my_pid)
37-
return EPERM;
38-
// Create a new Session and a new ProcessGroup.
3931

40-
auto process_group = TRY(ProcessGroup::create(ProcessGroupID(pid().value())));
32+
// NOTE: ProcessGroup::create_if_unused_pgid() will fail with EPERM
33+
// if a process group with the same PGID already exists.
34+
auto process_group = TRY(ProcessGroup::create_if_unused_pgid(ProcessGroupID(pid().value())));
4135
return with_mutable_protected_data([&](auto& protected_data) -> ErrorOr<FlatPtr> {
4236
protected_data.tty = nullptr;
4337
protected_data.process_group = move(process_group);

0 commit comments

Comments
 (0)