Kernel: Add support for jails

Our implementation for Jails resembles much of how FreeBSD jails are
working - it's essentially only a matter of using a RefPtr in the
Process class to a Jail object. Then, when we iterate over all processes
in various cases, we could ensure if either the current process is in
jail and therefore should be restricted what is visible in terms of
PID isolation, and also to be able to expose metadata about Jails in
/sys/kernel/jails node (which does not reveal anything to a process
which is in jail).

A lifetime model for the Jail object is currently plain simple - there's
simpy no way to manually delete a Jail object once it was created. Such
feature should be carefully designed to allow safe destruction of a Jail
without the possibility of releasing a process which is in Jail from the
actual jail. Each process which is attached into a Jail cannot leave it
until the end of a Process (i.e. when finalizing a Process). All jails
are kept being referenced in the JailManagement. When a last attached
process is finalized, the Jail is automatically destroyed.
This commit is contained in:
Liav A 2022-11-02 22:26:02 +02:00 committed by Andrew Kaster
parent d69a0380e1
commit 5e062414c1
Notes: sideshowbarker 2024-07-17 04:45:46 +09:00
35 changed files with 609 additions and 160 deletions

View file

@ -107,6 +107,8 @@ enum class NeedsBigProcessLock {
S(inode_watcher_remove_watch, NeedsBigProcessLock::Yes) \
S(ioctl, NeedsBigProcessLock::Yes) \
S(join_thread, NeedsBigProcessLock::Yes) \
S(jail_create, NeedsBigProcessLock::No) \
S(jail_attach, NeedsBigProcessLock::No) \
S(kill, NeedsBigProcessLock::Yes) \
S(kill_thread, NeedsBigProcessLock::Yes) \
S(killpg, NeedsBigProcessLock::Yes) \
@ -329,6 +331,15 @@ struct SC_setkeymap_params {
StringArgument map_name;
};
struct SC_jail_create_params {
u64 index;
StringArgument name;
};
struct SC_jail_attach_params {
u64 index;
};
struct SC_getkeymap_params {
u32* map;
u32* shift_map;

View file

@ -129,6 +129,7 @@ extern "C" [[noreturn]] void init()
dmesgln("Initialize MMU");
Memory::MemoryManager::initialize(0);
DeviceManagement::initialize();
JailManagement::the();
// Invoke all static global constructors in the kernel.
// Note that we want to do this as early as possible.

View file

@ -39,6 +39,7 @@
#include <Kernel/Graphics/Console/VGATextModeConsole.h>
#include <Kernel/Graphics/GraphicsManagement.h>
#include <Kernel/Heap/kmalloc.h>
#include <Kernel/JailManagement.h>
#include <Kernel/KSyms.h>
#include <Kernel/Memory/MemoryManager.h>
#include <Kernel/Multiboot.h>
@ -237,6 +238,7 @@ extern "C" [[noreturn]] UNMAP_AFTER_INIT void init(BootInfo const& boot_info)
__stack_chk_guard = get_fast_random<size_t>();
ProcFSComponentRegistry::initialize();
JailManagement::the();
Process::initialize();
Scheduler::initialize();

View file

@ -84,6 +84,8 @@ set(KERNEL_SOURCES
Graphics/VirtIOGPU/GPU3DDevice.cpp
Graphics/VirtIOGPU/GraphicsAdapter.cpp
IOWindow.cpp
Jail.cpp
JailManagement.cpp
SanCov.cpp
Storage/ATA/AHCI/Controller.cpp
Storage/ATA/AHCI/Port.cpp
@ -153,6 +155,7 @@ set(KERNEL_SOURCES
FileSystem/SysFS/Subsystems/Kernel/Interrupts.cpp
FileSystem/SysFS/Subsystems/Kernel/Processes.cpp
FileSystem/SysFS/Subsystems/Kernel/CPUInfo.cpp
FileSystem/SysFS/Subsystems/Kernel/Jails.cpp
FileSystem/SysFS/Subsystems/Kernel/Keymap.cpp
FileSystem/SysFS/Subsystems/Kernel/Profile.cpp
FileSystem/SysFS/Subsystems/Kernel/Directory.cpp
@ -262,6 +265,7 @@ set(KERNEL_SOURCES
Syscalls/getuid.cpp
Syscalls/hostname.cpp
Syscalls/ioctl.cpp
Syscalls/jail.cpp
Syscalls/keymap.cpp
Syscalls/kill.cpp
Syscalls/link.cpp

View file

@ -256,7 +256,7 @@ ErrorOr<void> ProcFSProcessDirectoryInode::attach(OpenFileDescription&)
InodeMetadata ProcFSProcessDirectoryInode::metadata() const
{
MutexLocker locker(m_inode_lock);
auto process = Process::from_pid(associated_pid());
auto process = Process::from_pid_in_same_jail(associated_pid());
if (!process)
return {};
@ -279,7 +279,7 @@ ErrorOr<size_t> ProcFSProcessDirectoryInode::read_bytes_locked(off_t, size_t, Us
ErrorOr<void> ProcFSProcessDirectoryInode::traverse_as_directory(Function<ErrorOr<void>(FileSystem::DirectoryEntryView const&)> callback) const
{
MutexLocker locker(procfs().m_lock);
auto process = Process::from_pid(associated_pid());
auto process = Process::from_pid_in_same_jail(associated_pid());
if (!process)
return EINVAL;
return process->procfs_traits()->traverse_as_directory(procfs().fsid(), move(callback));
@ -288,7 +288,7 @@ ErrorOr<void> ProcFSProcessDirectoryInode::traverse_as_directory(Function<ErrorO
ErrorOr<NonnullLockRefPtr<Inode>> ProcFSProcessDirectoryInode::lookup(StringView name)
{
MutexLocker locker(procfs().m_lock);
auto process = Process::from_pid(associated_pid());
auto process = Process::from_pid_in_same_jail(associated_pid());
if (!process)
return ESRCH;
if (name == "fd"sv)
@ -345,7 +345,7 @@ void ProcFSProcessSubDirectoryInode::did_seek(OpenFileDescription&, off_t)
InodeMetadata ProcFSProcessSubDirectoryInode::metadata() const
{
MutexLocker locker(m_inode_lock);
auto process = Process::from_pid(associated_pid());
auto process = Process::from_pid_in_same_jail(associated_pid());
if (!process)
return {};
@ -363,7 +363,7 @@ InodeMetadata ProcFSProcessSubDirectoryInode::metadata() const
ErrorOr<void> ProcFSProcessSubDirectoryInode::traverse_as_directory(Function<ErrorOr<void>(FileSystem::DirectoryEntryView const&)> callback) const
{
MutexLocker locker(procfs().m_lock);
auto process = Process::from_pid(associated_pid());
auto process = Process::from_pid_in_same_jail(associated_pid());
if (!process)
return EINVAL;
switch (m_sub_directory_type) {
@ -382,7 +382,7 @@ ErrorOr<void> ProcFSProcessSubDirectoryInode::traverse_as_directory(Function<Err
ErrorOr<NonnullLockRefPtr<Inode>> ProcFSProcessSubDirectoryInode::lookup(StringView name)
{
MutexLocker locker(procfs().m_lock);
auto process = Process::from_pid(associated_pid());
auto process = Process::from_pid_in_same_jail(associated_pid());
if (!process)
return ESRCH;
switch (m_sub_directory_type) {
@ -472,7 +472,7 @@ static mode_t determine_procfs_process_inode_mode(SegmentedProcFSIndex::ProcessS
InodeMetadata ProcFSProcessPropertyInode::metadata() const
{
MutexLocker locker(m_inode_lock);
auto process = Process::from_pid(associated_pid());
auto process = Process::from_pid_in_same_jail(associated_pid());
if (!process)
return {};
@ -499,7 +499,7 @@ ErrorOr<size_t> ProcFSProcessPropertyInode::read_bytes_locked(off_t offset, size
if (!description) {
auto builder = TRY(KBufferBuilder::try_create());
auto process = Process::from_pid(associated_pid());
auto process = Process::from_pid_in_same_jail(associated_pid());
if (!process)
return Error::from_errno(ESRCH);
TRY(try_to_acquire_data(*process, builder));
@ -585,7 +585,7 @@ ErrorOr<void> ProcFSProcessPropertyInode::refresh_data(OpenFileDescription& desc
// For process-specific inodes, hold the process's ptrace lock across refresh
// and refuse to load data if the process is not dumpable.
// Without this, files opened before a process went non-dumpable could still be used for dumping.
auto process = Process::from_pid(associated_pid());
auto process = Process::from_pid_in_same_jail(associated_pid());
if (!process)
return Error::from_errno(ESRCH);
process->ptrace_lock().lock();

View file

@ -13,6 +13,7 @@
#include <Kernel/FileSystem/SysFS/Subsystems/Kernel/DiskUsage.h>
#include <Kernel/FileSystem/SysFS/Subsystems/Kernel/GlobalInformation.h>
#include <Kernel/FileSystem/SysFS/Subsystems/Kernel/Interrupts.h>
#include <Kernel/FileSystem/SysFS/Subsystems/Kernel/Jails.h>
#include <Kernel/FileSystem/SysFS/Subsystems/Kernel/Keymap.h>
#include <Kernel/FileSystem/SysFS/Subsystems/Kernel/LoadBase.h>
#include <Kernel/FileSystem/SysFS/Subsystems/Kernel/Log.h>
@ -46,6 +47,7 @@ UNMAP_AFTER_INIT NonnullLockRefPtr<SysFSGlobalKernelStatsDirectory> SysFSGlobalK
list.append(SysFSProfile::must_create(*global_kernel_stats_directory));
list.append(SysFSKernelLoadBase::must_create(*global_kernel_stats_directory));
list.append(SysFSPowerStateSwitchNode::must_create(*global_kernel_stats_directory));
list.append(SysFSJails::must_create(*global_kernel_stats_directory));
list.append(SysFSGlobalNetworkStatsDirectory::must_create(*global_kernel_stats_directory));
list.append(SysFSGlobalKernelVariablesDirectory::must_create(*global_kernel_stats_directory));

View file

@ -0,0 +1,38 @@
/*
* Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/JsonObjectSerializer.h>
#include <Kernel/FileSystem/SysFS/Subsystems/Kernel/Jails.h>
#include <Kernel/JailManagement.h>
#include <Kernel/Sections.h>
namespace Kernel {
UNMAP_AFTER_INIT SysFSJails::SysFSJails(SysFSDirectory const& parent_directory)
: SysFSGlobalInformation(parent_directory)
{
}
UNMAP_AFTER_INIT NonnullLockRefPtr<SysFSJails> SysFSJails::must_create(SysFSDirectory const& parent_directory)
{
return adopt_lock_ref_if_nonnull(new (nothrow) SysFSJails(parent_directory)).release_nonnull();
}
ErrorOr<void> SysFSJails::try_generate(KBufferBuilder& builder)
{
auto array = TRY(JsonArraySerializer<>::try_create(builder));
TRY(JailManagement::the().for_each_in_same_jail([&array](Jail& jail) -> ErrorOr<void> {
auto obj = TRY(array.add_object());
TRY(obj.add("index"sv, jail.index().value()));
TRY(obj.add("name"sv, jail.name()));
TRY(obj.finish());
return {};
}));
TRY(array.finish());
return {};
}
}

View file

@ -0,0 +1,28 @@
/*
* Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Types.h>
#include <Kernel/FileSystem/SysFS/Subsystems/Kernel/GlobalInformation.h>
#include <Kernel/KBufferBuilder.h>
#include <Kernel/Library/LockRefPtr.h>
#include <Kernel/UserOrKernelBuffer.h>
namespace Kernel {
class SysFSJails final : public SysFSGlobalInformation {
public:
virtual StringView name() const override { return "jails"sv; }
static NonnullLockRefPtr<SysFSJails> must_create(SysFSDirectory const& parent_directory);
private:
explicit SysFSJails(SysFSDirectory const& parent_directory);
virtual ErrorOr<void> try_generate(KBufferBuilder& builder) override;
};
}

View file

@ -141,10 +141,10 @@ ErrorOr<void> SysFSOverallProcesses::try_generate(KBufferBuilder& builder)
{
auto array = TRY(json.add_array("processes"sv));
// FIXME: Do we actually want to expose the colonel process in a Jail environment?
TRY(build_process(array, *Scheduler::colonel()));
TRY(Process::all_instances().with([&](auto& processes) -> ErrorOr<void> {
for (auto& process : processes)
TRY(build_process(array, process));
TRY(Process::for_each_in_same_jail([&](Process& process) -> ErrorOr<void> {
TRY(build_process(array, process));
return {};
}));
TRY(array.finish());

View file

@ -5,6 +5,7 @@
*/
#include <Kernel/FileSystem/SysFS/Subsystems/Kernel/Variables/BooleanVariable.h>
#include <Kernel/Process.h>
#include <Kernel/Sections.h>
namespace Kernel {
@ -16,18 +17,26 @@ ErrorOr<void> SysFSSystemBoolean::try_generate(KBufferBuilder& builder)
ErrorOr<size_t> SysFSSystemBoolean::write_bytes(off_t, size_t count, UserOrKernelBuffer const& buffer, OpenFileDescription*)
{
if (count != 1)
return EINVAL;
MutexLocker locker(m_refresh_lock);
// Note: We do all of this code before taking the spinlock because then we disable
// interrupts so page faults will not work.
char value = 0;
TRY(buffer.read(&value, 1));
if (value == '0')
set_value(false);
else if (value == '1')
set_value(true);
else
return EINVAL;
return 1;
return Process::current().jail().with([&](auto& my_jail) -> ErrorOr<size_t> {
// Note: If we are in a jail, don't let the current process to change the variable.
if (my_jail)
return Error::from_errno(EPERM);
if (count != 1)
return Error::from_errno(EINVAL);
if (value == '0')
set_value(false);
else if (value == '1')
set_value(true);
else
return Error::from_errno(EINVAL);
return 1;
});
}
ErrorOr<void> SysFSSystemBoolean::truncate(u64 size)

View file

@ -22,12 +22,12 @@ UNMAP_AFTER_INIT NonnullLockRefPtr<SysFSCapsLockRemap> SysFSCapsLockRemap::must_
bool SysFSCapsLockRemap::value() const
{
MutexLocker locker(m_lock);
SpinlockLocker locker(m_lock);
return g_caps_lock_remapped_to_ctrl.load();
}
void SysFSCapsLockRemap::set_value(bool new_value)
{
MutexLocker locker(m_lock);
SpinlockLocker locker(m_lock);
g_caps_lock_remapped_to_ctrl.exchange(new_value);
}

View file

@ -9,6 +9,7 @@
#include <AK/Types.h>
#include <Kernel/FileSystem/SysFS/Subsystems/Kernel/Variables/BooleanVariable.h>
#include <Kernel/Library/LockRefPtr.h>
#include <Kernel/Locking/Spinlock.h>
#include <Kernel/UserOrKernelBuffer.h>
namespace Kernel {
@ -24,7 +25,7 @@ private:
explicit SysFSCapsLockRemap(SysFSDirectory const&);
mutable Mutex m_lock;
mutable Spinlock m_lock { LockRank::None };
};
}

View file

@ -22,13 +22,13 @@ UNMAP_AFTER_INIT NonnullLockRefPtr<SysFSDumpKmallocStacks> SysFSDumpKmallocStack
bool SysFSDumpKmallocStacks::value() const
{
MutexLocker locker(m_lock);
SpinlockLocker locker(m_lock);
return g_dump_kmalloc_stacks;
}
void SysFSDumpKmallocStacks::set_value(bool new_value)
{
MutexLocker locker(m_lock);
SpinlockLocker locker(m_lock);
g_dump_kmalloc_stacks = new_value;
}

View file

@ -9,6 +9,7 @@
#include <AK/Types.h>
#include <Kernel/FileSystem/SysFS/Subsystems/Kernel/Variables/BooleanVariable.h>
#include <Kernel/Library/LockRefPtr.h>
#include <Kernel/Locking/Spinlock.h>
#include <Kernel/UserOrKernelBuffer.h>
namespace Kernel {
@ -24,7 +25,7 @@ private:
explicit SysFSDumpKmallocStacks(SysFSDirectory const&);
mutable Mutex m_lock;
mutable Spinlock m_lock { LockRank::None };
};
}

View file

@ -28,6 +28,7 @@ class IPv4Socket;
class Inode;
class InodeIdentifier;
class InodeWatcher;
class Jail;
class KBuffer;
class KString;
class LocalSocket;

34
Kernel/Jail.cpp Normal file
View file

@ -0,0 +1,34 @@
/*
* Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <Kernel/Jail.h>
namespace Kernel {
ErrorOr<NonnullLockRefPtr<Jail>> Jail::create(Badge<JailManagement>, NonnullOwnPtr<KString> name, JailIndex index)
{
return adopt_nonnull_lock_ref_or_enomem(new (nothrow) Jail(move(name), index));
}
Jail::Jail(NonnullOwnPtr<KString> name, JailIndex index)
: m_name(move(name))
, m_index(index)
{
}
void Jail::detach(Badge<Process>)
{
VERIFY(ref_count() > 0);
m_attach_count.with([&](auto& my_attach_count) {
VERIFY(my_attach_count > 0);
my_attach_count--;
if (my_attach_count == 0) {
m_jail_list_node.remove();
}
});
}
}

48
Kernel/Jail.h Normal file
View file

@ -0,0 +1,48 @@
/*
* Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/DistinctNumeric.h>
#include <AK/Error.h>
#include <AK/IntrusiveList.h>
#include <AK/OwnPtr.h>
#include <AK/RefPtr.h>
#include <AK/Try.h>
#include <AK/Types.h>
#include <Kernel/KString.h>
#include <Kernel/Library/LockRefPtr.h>
#include <Kernel/Locking/SpinlockProtected.h>
namespace Kernel {
class JailManagement;
AK_TYPEDEF_DISTINCT_ORDERED_ID(u64, JailIndex);
class Jail : public RefCounted<Jail> {
friend class JailManagement;
public:
static ErrorOr<NonnullLockRefPtr<Jail>> create(Badge<JailManagement>, NonnullOwnPtr<KString>, JailIndex);
StringView name() const { return m_name->view(); }
JailIndex index() const { return m_index; }
void detach(Badge<Process>);
SpinlockProtected<size_t>& attach_count() { return m_attach_count; }
private:
Jail(NonnullOwnPtr<KString>, JailIndex);
NonnullOwnPtr<KString> m_name;
JailIndex const m_index;
IntrusiveListNode<Jail, NonnullLockRefPtr<Jail>> m_jail_list_node;
SpinlockProtected<size_t> m_attach_count { LockRank::None, 0 };
};
}

76
Kernel/JailManagement.cpp Normal file
View file

@ -0,0 +1,76 @@
/*
* Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Singleton.h>
#include <Kernel/JailManagement.h>
#include <Kernel/Process.h>
#include <Kernel/Sections.h>
namespace Kernel {
static Singleton<JailManagement> s_the;
static Atomic<u64> s_jail_id;
UNMAP_AFTER_INIT JailManagement::JailManagement() = default;
JailIndex JailManagement::generate_jail_id()
{
return s_jail_id.fetch_add(1);
}
JailManagement& JailManagement::the()
{
return *s_the;
}
LockRefPtr<Jail> JailManagement::find_jail_by_index(JailIndex index)
{
return m_jails.with([&](auto& list) -> LockRefPtr<Jail> {
for (auto& jail : list) {
if (jail.index() == index)
return jail;
}
return {};
});
}
ErrorOr<void> JailManagement::for_each_in_same_jail(Function<ErrorOr<void>(Jail&)> callback)
{
return Process::current().jail().with([&](auto& my_jail) -> ErrorOr<void> {
// Note: If we are in a jail, don't reveal anything about the outside world,
// not even the fact that we are in which jail...
if (my_jail)
return {};
return m_jails.with([&](auto& list) -> ErrorOr<void> {
for (auto& jail : list) {
TRY(callback(jail));
}
return {};
});
});
}
LockRefPtr<Jail> JailManagement::find_first_jail_by_name(StringView name)
{
return m_jails.with([&](auto& list) -> LockRefPtr<Jail> {
for (auto& jail : list) {
if (jail.name() == name)
return jail;
}
return {};
});
}
ErrorOr<NonnullLockRefPtr<Jail>> JailManagement::create_jail(NonnullOwnPtr<KString> name)
{
return m_jails.with([&](auto& list) -> ErrorOr<NonnullLockRefPtr<Jail>> {
auto jail = TRY(Jail::create({}, move(name), generate_jail_id()));
list.append(jail);
return jail;
});
}
}

42
Kernel/JailManagement.h Normal file
View file

@ -0,0 +1,42 @@
/*
* Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/DistinctNumeric.h>
#include <AK/Error.h>
#include <AK/IntrusiveList.h>
#include <AK/OwnPtr.h>
#include <AK/RefPtr.h>
#include <AK/Try.h>
#include <AK/Types.h>
#include <Kernel/Jail.h>
#include <Kernel/KString.h>
#include <Kernel/Library/LockRefPtr.h>
#include <Kernel/Locking/SpinlockProtected.h>
namespace Kernel {
class JailManagement {
public:
JailManagement();
static JailManagement& the();
LockRefPtr<Jail> find_jail_by_index(JailIndex);
LockRefPtr<Jail> find_first_jail_by_name(StringView);
ErrorOr<NonnullLockRefPtr<Jail>> create_jail(NonnullOwnPtr<KString> name);
ErrorOr<void> for_each_in_same_jail(Function<ErrorOr<void>(Jail&)>);
private:
JailIndex generate_jail_id();
SpinlockProtected<IntrusiveList<&Jail::m_jail_list_node>> m_jails { LockRank::None };
};
}

View file

@ -847,7 +847,7 @@ ErrorOr<CommittedPhysicalPageSet> MemoryManager::commit_physical_pages(size_t pa
return CommittedPhysicalPageSet { {}, page_count };
});
if (result.is_error()) {
Process::for_each([&](Process const& process) {
Process::for_each_ignoring_jails([&](Process const& process) {
size_t amount_resident = 0;
size_t amount_shared = 0;
size_t amount_virtual = 0;

View file

@ -63,6 +63,95 @@ SpinlockProtected<Process::List>& Process::all_instances()
return *s_all_instances;
}
ErrorOr<void> Process::for_each_in_same_jail(Function<ErrorOr<void>(Process&)> callback)
{
ErrorOr<void> result {};
Process::all_instances().with([&](auto const& list) {
Process::current().jail().with([&](auto my_jail) {
for (auto& process : list) {
if (!my_jail) {
result = callback(process);
} else {
// Note: Don't acquire the process jail spinlock twice if it's the same process
// we are currently inspecting.
if (&Process::current() == &process) {
result = callback(process);
} else {
process.jail().with([&](auto& their_jail) {
if (their_jail.ptr() == my_jail.ptr())
result = callback(process);
});
}
}
if (result.is_error())
break;
}
});
});
return result;
}
ErrorOr<void> Process::for_each_child_in_same_jail(Function<ErrorOr<void>(Process&)> callback)
{
ProcessID my_pid = pid();
ErrorOr<void> result {};
Process::all_instances().with([&](auto const& list) {
jail().with([&](auto my_jail) {
for (auto& process : list) {
if (!my_jail) {
if (process.ppid() == my_pid || process.has_tracee_thread(pid()))
result = callback(process);
} else {
// FIXME: Is it possible to have a child process being pointing to itself
// as the parent process under normal conditions?
// Note: Don't acquire the process jail spinlock twice if it's the same process
// we are currently inspecting.
if (&Process::current() == &process && (process.ppid() == my_pid || process.has_tracee_thread(pid()))) {
result = callback(process);
} else {
process.jail().with([&](auto& their_jail) {
if ((their_jail.ptr() == my_jail.ptr()) && (process.ppid() == my_pid || process.has_tracee_thread(pid())))
result = callback(process);
});
}
}
if (result.is_error())
break;
}
});
});
return result;
}
ErrorOr<void> Process::for_each_in_pgrp_in_same_jail(ProcessGroupID pgid, Function<ErrorOr<void>(Process&)> callback)
{
ErrorOr<void> result {};
Process::all_instances().with([&](auto const& list) {
jail().with([&](auto my_jail) {
for (auto& process : list) {
if (!my_jail) {
if (!process.is_dead() && process.pgid() == pgid)
result = callback(process);
} else {
// Note: Don't acquire the process jail spinlock twice if it's the same process
// we are currently inspecting.
if (&Process::current() == &process && !process.is_dead() && process.pgid() == pgid) {
result = callback(process);
} else {
process.jail().with([&](auto& their_jail) {
if ((their_jail.ptr() == my_jail.ptr()) && !process.is_dead() && process.pgid() == pgid)
result = callback(process);
});
}
}
if (result.is_error())
break;
}
});
});
return result;
}
ProcessID Process::allocate_pid()
{
// Overflow is UB, and negative PIDs wreck havoc.
@ -426,7 +515,33 @@ void Process::crash(int signal, FlatPtr ip, bool out_of_memory)
VERIFY_NOT_REACHED();
}
LockRefPtr<Process> Process::from_pid(ProcessID pid)
LockRefPtr<Process> Process::from_pid_in_same_jail(ProcessID pid)
{
return Process::current().jail().with([&](auto& my_jail) -> LockRefPtr<Process> {
return all_instances().with([&](auto const& list) -> LockRefPtr<Process> {
if (!my_jail) {
for (auto& process : list) {
if (process.pid() == pid) {
return process;
}
}
} else {
for (auto& process : list) {
if (process.pid() == pid) {
return process.jail().with([&](auto& other_process_jail) -> LockRefPtr<Process> {
if (other_process_jail.ptr() == my_jail.ptr())
return process;
return {};
});
}
}
}
return {};
});
});
}
LockRefPtr<Process> Process::from_pid_ignoring_jails(ProcessID pid)
{
return all_instances().with([&](auto const& list) -> LockRefPtr<Process> {
for (auto const& process : list) {
@ -657,20 +772,25 @@ void Process::finalize()
m_fds.with_exclusive([](auto& fds) { fds.clear(); });
m_tty = nullptr;
m_executable.with([](auto& executable) { executable = nullptr; });
m_attached_jail.with([](auto& jail) {
if (jail)
jail->detach({});
jail = nullptr;
});
m_arguments.clear();
m_environment.clear();
m_state.store(State::Dead, AK::MemoryOrder::memory_order_release);
{
if (auto parent_process = Process::from_pid(ppid())) {
if (auto parent_process = Process::from_pid_ignoring_jails(ppid())) {
if (parent_process->is_user_process() && (parent_process->m_signal_action_data[SIGCHLD].flags & SA_NOCLDWAIT) != SA_NOCLDWAIT)
(void)parent_process->send_signal(SIGCHLD, this);
}
}
if (!!ppid()) {
if (auto parent = Process::from_pid(ppid())) {
if (auto parent = Process::from_pid_ignoring_jails(ppid())) {
parent->m_ticks_in_user_for_dead_children += m_ticks_in_user + m_ticks_in_user_for_dead_children;
parent->m_ticks_in_kernel_for_dead_children += m_ticks_in_kernel + m_ticks_in_kernel_for_dead_children;
}
@ -697,9 +817,9 @@ void Process::unblock_waiters(Thread::WaitBlocker::UnblockFlags flags, u8 signal
{
LockRefPtr<Process> waiter_process;
if (auto* my_tracer = tracer())
waiter_process = Process::from_pid(my_tracer->tracer_pid());
waiter_process = Process::from_pid_ignoring_jails(my_tracer->tracer_pid());
else
waiter_process = Process::from_pid(ppid());
waiter_process = Process::from_pid_ignoring_jails(ppid());
if (waiter_process)
waiter_process->m_wait_blocker_set.unblock(*this, flags, signal);

View file

@ -24,6 +24,7 @@
#include <Kernel/FileSystem/UnveilNode.h>
#include <Kernel/Forward.h>
#include <Kernel/FutexQueue.h>
#include <Kernel/Jail.h>
#include <Kernel/Library/LockWeakPtr.h>
#include <Kernel/Library/LockWeakable.h>
#include <Kernel/Library/NonnullLockRefPtrVector.h>
@ -70,6 +71,7 @@ Time kgettimeofday();
__ENUMERATE_PLEDGE_PROMISE(prot_exec) \
__ENUMERATE_PLEDGE_PROMISE(map_fixed) \
__ENUMERATE_PLEDGE_PROMISE(getkeymap) \
__ENUMERATE_PLEDGE_PROMISE(jail) \
__ENUMERATE_PLEDGE_PROMISE(no_error)
enum class Pledge : u32 {
@ -213,7 +215,8 @@ public:
bool is_kernel_process() const { return m_is_kernel_process; }
bool is_user_process() const { return !m_is_kernel_process; }
static LockRefPtr<Process> from_pid(ProcessID);
static LockRefPtr<Process> from_pid_in_same_jail(ProcessID);
static LockRefPtr<Process> from_pid_ignoring_jails(ProcessID);
static SessionID get_sid_from_pgid(ProcessGroupID pgid);
StringView name() const { return m_name->view(); }
@ -233,6 +236,8 @@ public:
return with_protected_data([](auto& protected_data) { return protected_data.ppid; });
}
SpinlockProtected<RefPtr<Jail>>& jail() { return m_attached_jail; }
NonnullRefPtr<Credentials> credentials() const;
bool is_dumpable() const
@ -248,11 +253,11 @@ public:
// Breakable iteration functions
template<IteratorFunction<Process&> Callback>
static void for_each(Callback);
template<IteratorFunction<Process&> Callback>
static void for_each_in_pgrp(ProcessGroupID, Callback);
template<IteratorFunction<Process&> Callback>
void for_each_child(Callback);
static void for_each_ignoring_jails(Callback);
static ErrorOr<void> for_each_in_same_jail(Function<ErrorOr<void>(Process&)>);
ErrorOr<void> for_each_in_pgrp_in_same_jail(ProcessGroupID, Function<ErrorOr<void>(Process&)>);
ErrorOr<void> for_each_child_in_same_jail(Function<ErrorOr<void>(Process&)>);
template<IteratorFunction<Thread&> Callback>
IterationDecision for_each_thread(Callback);
@ -262,11 +267,7 @@ public:
// Non-breakable iteration functions
template<VoidFunction<Process&> Callback>
static void for_each(Callback);
template<VoidFunction<Process&> Callback>
static void for_each_in_pgrp(ProcessGroupID, Callback);
template<VoidFunction<Process&> Callback>
void for_each_child(Callback);
static void for_each_ignoring_jails(Callback);
template<VoidFunction<Thread&> Callback>
IterationDecision for_each_thread(Callback);
@ -437,6 +438,8 @@ public:
ErrorOr<FlatPtr> sys$statvfs(Userspace<Syscall::SC_statvfs_params const*> user_params);
ErrorOr<FlatPtr> sys$fstatvfs(int fd, statvfs* buf);
ErrorOr<FlatPtr> sys$map_time_page();
ErrorOr<FlatPtr> sys$jail_create(Userspace<Syscall::SC_jail_create_params*> user_params);
ErrorOr<FlatPtr> sys$jail_attach(Userspace<Syscall::SC_jail_attach_params const*> user_params);
template<bool sockname, typename Params>
ErrorOr<void> get_sock_or_peer_name(Params const&);
@ -862,6 +865,10 @@ private:
LockRefPtr<TTY> m_tty;
LockWeakPtr<Memory::Region> m_master_tls_region;
IntrusiveListNode<Process> m_jail_list_node;
SpinlockProtected<RefPtr<Jail>> m_attached_jail { LockRank::Process };
size_t m_master_tls_size { 0 };
size_t m_master_tls_alignment { 0 };
@ -914,48 +921,6 @@ static_assert(AssertSize<Process, (PAGE_SIZE * 2)>());
extern RecursiveSpinlock g_profiling_lock;
template<IteratorFunction<Process&> Callback>
inline void Process::for_each(Callback callback)
{
Process::all_instances().with([&](auto const& list) {
for (auto it = list.begin(); it != list.end();) {
auto& process = *it;
++it;
if (callback(process) == IterationDecision::Break)
break;
}
});
}
template<IteratorFunction<Process&> Callback>
inline void Process::for_each_child(Callback callback)
{
ProcessID my_pid = pid();
Process::all_instances().with([&](auto const& list) {
for (auto it = list.begin(); it != list.end();) {
auto& process = *it;
++it;
if (process.ppid() == my_pid || process.has_tracee_thread(pid())) {
if (callback(process) == IterationDecision::Break)
break;
}
}
});
}
template<IteratorFunction<Thread&> Callback>
inline IterationDecision Process::for_each_thread(Callback callback) const
{
return thread_list().with([&](auto& thread_list) -> IterationDecision {
for (auto& thread : thread_list) {
IterationDecision decision = callback(thread);
if (decision != IterationDecision::Continue)
return decision;
}
return IterationDecision::Continue;
});
}
template<IteratorFunction<Thread&> Callback>
inline IterationDecision Process::for_each_thread(Callback callback)
{
@ -970,34 +935,27 @@ inline IterationDecision Process::for_each_thread(Callback callback)
}
template<IteratorFunction<Process&> Callback>
inline void Process::for_each_in_pgrp(ProcessGroupID pgid, Callback callback)
inline void Process::for_each_ignoring_jails(Callback callback)
{
Process::all_instances().with([&](auto const& list) {
for (auto it = list.begin(); it != list.end();) {
auto& process = *it;
++it;
if (!process.is_dead() && process.pgid() == pgid) {
if (callback(process) == IterationDecision::Break)
break;
}
if (callback(process) == IterationDecision::Break)
break;
}
});
}
template<VoidFunction<Process&> Callback>
inline void Process::for_each(Callback callback)
template<IteratorFunction<Thread&> Callback>
inline IterationDecision Process::for_each_thread(Callback callback) const
{
return for_each([&](auto& item) {
callback(item);
return IterationDecision::Continue;
});
}
template<VoidFunction<Process&> Callback>
inline void Process::for_each_child(Callback callback)
{
return for_each_child([&](auto& item) {
callback(item);
return thread_list().with([&](auto& thread_list) -> IterationDecision {
for (auto& thread : thread_list) {
IterationDecision decision = callback(thread);
if (decision != IterationDecision::Continue)
return decision;
}
return IterationDecision::Continue;
});
}
@ -1031,15 +989,6 @@ inline IterationDecision Process::for_each_thread(Callback callback)
return IterationDecision::Continue;
}
template<VoidFunction<Process&> Callback>
inline void Process::for_each_in_pgrp(ProcessGroupID pgid, Callback callback)
{
return for_each_in_pgrp(pgid, [&](auto& item) {
callback(item);
return IterationDecision::Continue;
});
}
inline ProcessID Thread::pid() const
{
return m_process->pid();

View file

@ -206,14 +206,12 @@ ErrorOr<void> ProcFSRootDirectory::traverse_as_directory(FileSystemID fsid, Func
TRY(callback({ component.name(), identifier, 0 }));
}
return Process::all_instances().with([&](auto& list) -> ErrorOr<void> {
for (auto& process : list) {
VERIFY(!(process.pid() < 0));
u64 process_id = (u64)process.pid().value();
InodeIdentifier identifier = { fsid, static_cast<InodeIndex>(process_id << 36) };
auto process_id_string = TRY(KString::formatted("{:d}", process_id));
TRY(callback({ process_id_string->view(), identifier, 0 }));
}
return Process::for_each_in_same_jail([&](Process& process) -> ErrorOr<void> {
VERIFY(!(process.pid() < 0));
u64 process_id = (u64)process.pid().value();
InodeIdentifier identifier = { fsid, static_cast<InodeIndex>(process_id << 36) };
auto process_id_string = TRY(KString::formatted("{:d}", process_id));
TRY(callback({ process_id_string->view(), identifier, 0 }));
return {};
});
}
@ -234,7 +232,7 @@ ErrorOr<NonnullLockRefPtr<ProcFSExposedComponent>> ProcFSRootDirectory::lookup(S
return ESRCH;
auto actual_pid = pid.value();
if (auto maybe_process = Process::from_pid(actual_pid))
if (auto maybe_process = Process::from_pid_in_same_jail(actual_pid))
return maybe_process->procfs_traits();
return ENOENT;

View file

@ -85,13 +85,11 @@ ErrorOr<void> Process::traverse_children_directory(FileSystemID fsid, Function<E
{
TRY(callback({ "."sv, { fsid, SegmentedProcFSIndex::build_segmented_index_for_sub_directory(pid(), SegmentedProcFSIndex::ProcessSubDirectory::Children) }, 0 }));
TRY(callback({ ".."sv, { fsid, m_procfs_traits->component_index() }, 0 }));
return Process::all_instances().with([&](auto& processes) -> ErrorOr<void> {
for (auto& process : processes) {
if (process.ppid() == pid()) {
StringBuilder builder;
builder.appendff("{}", process.pid());
TRY(callback({ builder.string_view(), { fsid, SegmentedProcFSIndex::build_segmented_index_for_children(pid(), process.pid()) }, DT_LNK }));
}
return Process::for_each_in_same_jail([&](Process& process) -> ErrorOr<void> {
if (process.ppid() == pid()) {
StringBuilder builder;
builder.appendff("{}", process.pid());
TRY(callback({ builder.string_view(), { fsid, SegmentedProcFSIndex::build_segmented_index_for_children(pid(), process.pid()) }, DT_LNK }));
}
return {};
});
@ -103,7 +101,7 @@ ErrorOr<NonnullLockRefPtr<Inode>> Process::lookup_children_directory(ProcFS cons
if (!maybe_pid.has_value())
return ENOENT;
auto child_process = Process::from_pid(*maybe_pid);
auto child_process = Process::from_pid_in_same_jail(*maybe_pid);
if (!child_process || child_process->ppid() != pid())
return ENOENT;

View file

@ -12,7 +12,7 @@ ErrorOr<FlatPtr> Process::sys$disown(ProcessID pid)
{
VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this);
TRY(require_promise(Pledge::proc));
auto process = Process::from_pid(pid);
auto process = Process::from_pid_in_same_jail(pid);
if (!process)
return ESRCH;
if (process->ppid() != this->pid())

View file

@ -495,6 +495,7 @@ ErrorOr<void> Process::do_exec(NonnullLockRefPtr<OpenFileDescription> main_progr
auto old_credentials = this->credentials();
auto new_credentials = old_credentials;
auto old_process_attached_jail = m_attached_jail.with([&](auto& jail) -> RefPtr<Jail> { return jail; });
bool executable_is_setid = false;
@ -553,6 +554,9 @@ ErrorOr<void> Process::do_exec(NonnullLockRefPtr<OpenFileDescription> main_progr
m_executable.with([&](auto& executable) { executable = main_program_description->custody(); });
m_arguments = move(arguments);
m_attached_jail.with([&](auto& jail) {
jail = old_process_attached_jail;
});
m_environment = move(environment);
TRY(m_unveil_data.with([&](auto& unveil_data) -> ErrorOr<void> {

View file

@ -42,6 +42,26 @@ ErrorOr<FlatPtr> Process::sys$fork(RegisterState& regs)
});
}));
// Note: We take the spinlock of Process::all_instances list because we need
// to ensure that when we take the jail spinlock of two processes that we don't
// run into a deadlock situation because both processes compete over each other Jail's
// spinlock. Such pattern of taking 3 spinlocks in the same order happens in
// Process::for_each* methods.
TRY(Process::all_instances().with([&](auto const&) -> ErrorOr<void> {
TRY(m_attached_jail.with([&](auto& parent_jail) -> ErrorOr<void> {
return child->m_attached_jail.with([&](auto& child_jail) -> ErrorOr<void> {
child_jail = parent_jail;
if (child_jail) {
child_jail->attach_count().with([&](auto& attach_count) {
attach_count++;
});
}
return {};
});
}));
return {};
}));
TRY(child->m_fds.with_exclusive([&](auto& child_fds) {
return m_fds.with_exclusive([&](auto& parent_fds) {
return child_fds.try_clone(parent_fds);

59
Kernel/Syscalls/jail.cpp Normal file
View file

@ -0,0 +1,59 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Userspace.h>
#include <Kernel/Jail.h>
#include <Kernel/JailManagement.h>
#include <Kernel/Process.h>
#include <Kernel/StdLib.h>
#include <LibC/sys/ioctl_numbers.h>
namespace Kernel {
constexpr size_t jail_name_max_size = 50;
ErrorOr<FlatPtr> Process::sys$jail_create(Userspace<Syscall::SC_jail_create_params*> user_params)
{
VERIFY_NO_PROCESS_BIG_LOCK(this);
TRY(require_promise(Pledge::jail));
auto params = TRY(copy_typed_from_user(user_params));
auto jail_name = TRY(get_syscall_path_argument(params.name));
if (jail_name->length() > jail_name_max_size)
return ENAMETOOLONG;
auto jail = TRY(JailManagement::the().create_jail(move(jail_name)));
params.index = jail->index().value();
TRY(copy_to_user(user_params, &params));
return 0;
}
ErrorOr<FlatPtr> Process::sys$jail_attach(Userspace<Syscall::SC_jail_attach_params const*> user_params)
{
VERIFY_NO_PROCESS_BIG_LOCK(this);
TRY(require_promise(Pledge::jail));
auto params = TRY(copy_typed_from_user(user_params));
return m_attached_jail.with([&](auto& my_jail) -> ErrorOr<FlatPtr> {
// Note: If we are already in a jail, don't let the process escape it even if
// it knows there are other jails.
// Note: To ensure the process doesn't try to maliciously enumerate all jails
// in the system, just return EPERM before doing anything else.
if (my_jail)
return EPERM;
auto jail = JailManagement::the().find_jail_by_index(static_cast<JailIndex>(params.index));
if (!jail)
return EINVAL;
my_jail = *jail;
my_jail->attach_count().with([&](auto& attach_count) {
attach_count++;
});
return 0;
});
}
}

View file

@ -42,7 +42,7 @@ ErrorOr<void> Process::do_killpg(ProcessGroupID pgrp, int signal)
bool any_succeeded = false;
ErrorOr<void> error;
Process::for_each_in_pgrp(pgrp, [&](auto& process) {
TRY(Process::current().for_each_in_pgrp_in_same_jail(pgrp, [&](auto& process) -> ErrorOr<void> {
group_was_empty = false;
ErrorOr<void> res = do_kill(process, signal);
@ -50,7 +50,8 @@ ErrorOr<void> Process::do_killpg(ProcessGroupID pgrp, int signal)
any_succeeded = true;
else
error = move(res);
});
return {};
}));
if (group_was_empty)
return ESRCH;
@ -67,7 +68,7 @@ ErrorOr<void> Process::do_killall(int signal)
ErrorOr<void> error;
// Send the signal to all processes we have access to for.
Process::all_instances().for_each([&](auto& process) {
TRY(Process::for_each_in_same_jail([&](auto& process) -> ErrorOr<void> {
ErrorOr<void> res;
if (process.pid() == pid())
res = do_killself(signal);
@ -78,7 +79,8 @@ ErrorOr<void> Process::do_killall(int signal)
any_succeeded = true;
else
error = move(res);
});
return {};
}));
if (any_succeeded)
return {};
@ -122,7 +124,7 @@ ErrorOr<FlatPtr> Process::sys$kill(pid_t pid_or_pgid, int signal)
return 0;
}
VERIFY(pid_or_pgid >= 0);
auto peer = Process::from_pid(pid_or_pgid);
auto peer = Process::from_pid_in_same_jail(pid_or_pgid);
if (!peer)
return ESRCH;
TRY(do_kill(*peer, signal));

View file

@ -53,15 +53,15 @@ ErrorOr<FlatPtr> Process::profiling_enable(pid_t pid, u64 event_mask)
return ENOTSUP;
g_profiling_all_threads = true;
PerformanceManager::add_process_created_event(*Scheduler::colonel());
Process::for_each([](auto& process) {
TRY(Process::for_each_in_same_jail([](auto& process) -> ErrorOr<void> {
PerformanceManager::add_process_created_event(process);
return IterationDecision::Continue;
});
return {};
}));
g_profiling_event_mask = event_mask;
return 0;
}
auto process = Process::from_pid(pid);
auto process = Process::from_pid_in_same_jail(pid);
if (!process)
return ESRCH;
if (process->is_dead())
@ -101,7 +101,7 @@ ErrorOr<FlatPtr> Process::sys$profiling_disable(pid_t pid)
return 0;
}
auto process = Process::from_pid(pid);
auto process = Process::from_pid_in_same_jail(pid);
if (!process)
return ESRCH;
auto credentials = this->credentials();
@ -140,7 +140,7 @@ ErrorOr<FlatPtr> Process::sys$profiling_free_buffer(pid_t pid)
return 0;
}
auto process = Process::from_pid(pid);
auto process = Process::from_pid_in_same_jail(pid);
if (!process)
return ESRCH;
auto credentials = this->credentials();

View file

@ -38,7 +38,7 @@ ErrorOr<NonnullRefPtr<Thread>> Process::get_thread_from_pid_or_tid(pid_t pid_or_
case Syscall::SchedulerParametersMode::Process: {
auto* searched_process = this;
if (pid_or_tid != 0)
searched_process = Process::from_pid(pid_or_tid);
searched_process = Process::from_pid_in_same_jail(pid_or_tid);
if (searched_process == nullptr)
return ESRCH;

View file

@ -16,7 +16,7 @@ ErrorOr<FlatPtr> Process::sys$getsid(pid_t pid)
TRY(require_promise(Pledge::stdio));
if (pid == 0)
return sid().value();
auto process = Process::from_pid(pid);
auto process = Process::from_pid_in_same_jail(pid);
if (!process)
return ESRCH;
if (sid() != process->sid())
@ -30,10 +30,10 @@ ErrorOr<FlatPtr> Process::sys$setsid()
TRY(require_promise(Pledge::proc));
InterruptDisabler disabler;
bool found_process_with_same_pgid_as_my_pid = false;
Process::for_each_in_pgrp(pid().value(), [&](auto&) {
TRY(Process::for_each_in_pgrp_in_same_jail(pid().value(), [&](auto&) -> ErrorOr<void> {
found_process_with_same_pgid_as_my_pid = true;
return IterationDecision::Break;
});
return {};
}));
if (found_process_with_same_pgid_as_my_pid)
return EPERM;
// Create a new Session and a new ProcessGroup.
@ -52,7 +52,7 @@ ErrorOr<FlatPtr> Process::sys$getpgid(pid_t pid)
TRY(require_promise(Pledge::stdio));
if (pid == 0)
return pgid().value();
auto process = Process::from_pid(pid);
auto process = Process::from_pid_in_same_jail(pid);
if (!process)
return ESRCH;
return process->pgid().value();
@ -70,10 +70,10 @@ SessionID Process::get_sid_from_pgid(ProcessGroupID pgid)
// FIXME: This xor sys$setsid() uses the wrong locking mechanism.
SessionID sid { -1 };
Process::for_each_in_pgrp(pgid, [&](auto& process) {
MUST(Process::current().for_each_in_pgrp_in_same_jail(pgid, [&](auto& process) -> ErrorOr<void> {
sid = process.sid();
return IterationDecision::Break;
});
return {};
}));
return sid;
}
@ -87,7 +87,7 @@ ErrorOr<FlatPtr> Process::sys$setpgid(pid_t specified_pid, pid_t specified_pgid)
// The value of the pgid argument is less than 0, or is not a value supported by the implementation.
return EINVAL;
}
auto process = Process::from_pid(pid);
auto process = Process::from_pid_in_same_jail(pid);
if (!process)
return ESRCH;
if (process != this && process->ppid() != this->pid()) {

View file

@ -30,7 +30,7 @@ ErrorOr<FlatPtr> Process::sys$waitid(Userspace<Syscall::SC_waitid_params const*>
case P_ALL:
break;
case P_PID: {
auto waitee_process = Process::from_pid(params.id);
auto waitee_process = Process::from_pid_in_same_jail(params.id);
if (!waitee_process)
return ECHILD;
bool waitee_is_child = waitee_process->ppid() == Process::current().pid();

View file

@ -359,11 +359,12 @@ void TTY::generate_signal(int signal)
flush_input();
dbgln_if(TTY_DEBUG, "Send signal {} to everyone in pgrp {}", signal, pgid().value());
InterruptDisabler disabler; // FIXME: Iterate over a set of process handles instead?
Process::for_each_in_pgrp(pgid(), [&](auto& process) {
MUST(Process::current().for_each_in_pgrp_in_same_jail(pgid(), [&](auto& process) -> ErrorOr<void> {
dbgln_if(TTY_DEBUG, "Send signal {} to {}", signal, process);
// FIXME: Should this error be propagated somehow?
[[maybe_unused]] auto rc = process.send_signal(signal, nullptr);
});
return {};
}));
}
void TTY::flush_input()
@ -493,7 +494,7 @@ ErrorOr<void> TTY::ioctl(OpenFileDescription&, unsigned request, Userspace<void*
if (!process_group)
return EINVAL;
auto process = Process::from_pid(ProcessID(pgid.value()));
auto process = Process::from_pid_in_same_jail(ProcessID(pgid.value()));
SessionID new_sid = process ? process->sid() : Process::get_sid_from_pgid(pgid);
if (!new_sid || new_sid != current_process.sid())
return EPERM;
@ -502,7 +503,7 @@ ErrorOr<void> TTY::ioctl(OpenFileDescription&, unsigned request, Userspace<void*
m_pg = process_group;
if (process) {
if (auto parent = Process::from_pid(process->ppid())) {
if (auto parent = Process::from_pid_ignoring_jails(process->ppid())) {
m_original_process_parent = *parent;
return {};
}

View file

@ -978,7 +978,7 @@ DispatchSignalResult Thread::dispatch_signal(u8 signal)
auto& action = m_process->m_signal_action_data[signal];
auto sender_pid = m_signal_senders[signal];
auto sender = Process::from_pid(sender_pid);
auto sender = Process::from_pid_ignoring_jails(sender_pid);
if (!current_trap() && !action.handler_or_sigaction.is_null()) {
// We're trying dispatch a handled signal to a user process that was scheduled
@ -1303,7 +1303,7 @@ void Thread::set_state(State new_state, u8 stop_signal)
});
process.unblock_waiters(Thread::WaitBlocker::UnblockFlags::Continued);
// Tell the parent process (if any) about this change.
if (auto parent = Process::from_pid(process.ppid())) {
if (auto parent = Process::from_pid_ignoring_jails(process.ppid())) {
[[maybe_unused]] auto result = parent->send_signal(SIGCHLD, &process);
}
}
@ -1327,7 +1327,7 @@ void Thread::set_state(State new_state, u8 stop_signal)
});
process.unblock_waiters(Thread::WaitBlocker::UnblockFlags::Stopped, stop_signal);
// Tell the parent process (if any) about this change.
if (auto parent = Process::from_pid(process.ppid())) {
if (auto parent = Process::from_pid_ignoring_jails(process.ppid())) {
[[maybe_unused]] auto result = parent->send_signal(SIGCHLD, &process);
}
}