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
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(inode_watcher_remove_watch, NeedsBigProcessLock::Yes) \
S(ioctl, NeedsBigProcessLock::Yes) \ S(ioctl, NeedsBigProcessLock::Yes) \
S(join_thread, NeedsBigProcessLock::Yes) \ S(join_thread, NeedsBigProcessLock::Yes) \
S(jail_create, NeedsBigProcessLock::No) \
S(jail_attach, NeedsBigProcessLock::No) \
S(kill, NeedsBigProcessLock::Yes) \ S(kill, NeedsBigProcessLock::Yes) \
S(kill_thread, NeedsBigProcessLock::Yes) \ S(kill_thread, NeedsBigProcessLock::Yes) \
S(killpg, NeedsBigProcessLock::Yes) \ S(killpg, NeedsBigProcessLock::Yes) \
@ -329,6 +331,15 @@ struct SC_setkeymap_params {
StringArgument map_name; StringArgument map_name;
}; };
struct SC_jail_create_params {
u64 index;
StringArgument name;
};
struct SC_jail_attach_params {
u64 index;
};
struct SC_getkeymap_params { struct SC_getkeymap_params {
u32* map; u32* map;
u32* shift_map; u32* shift_map;

View file

@ -129,6 +129,7 @@ extern "C" [[noreturn]] void init()
dmesgln("Initialize MMU"); dmesgln("Initialize MMU");
Memory::MemoryManager::initialize(0); Memory::MemoryManager::initialize(0);
DeviceManagement::initialize(); DeviceManagement::initialize();
JailManagement::the();
// Invoke all static global constructors in the kernel. // Invoke all static global constructors in the kernel.
// Note that we want to do this as early as possible. // 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/Console/VGATextModeConsole.h>
#include <Kernel/Graphics/GraphicsManagement.h> #include <Kernel/Graphics/GraphicsManagement.h>
#include <Kernel/Heap/kmalloc.h> #include <Kernel/Heap/kmalloc.h>
#include <Kernel/JailManagement.h>
#include <Kernel/KSyms.h> #include <Kernel/KSyms.h>
#include <Kernel/Memory/MemoryManager.h> #include <Kernel/Memory/MemoryManager.h>
#include <Kernel/Multiboot.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>(); __stack_chk_guard = get_fast_random<size_t>();
ProcFSComponentRegistry::initialize(); ProcFSComponentRegistry::initialize();
JailManagement::the();
Process::initialize(); Process::initialize();
Scheduler::initialize(); Scheduler::initialize();

View file

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

View file

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

View file

@ -13,6 +13,7 @@
#include <Kernel/FileSystem/SysFS/Subsystems/Kernel/DiskUsage.h> #include <Kernel/FileSystem/SysFS/Subsystems/Kernel/DiskUsage.h>
#include <Kernel/FileSystem/SysFS/Subsystems/Kernel/GlobalInformation.h> #include <Kernel/FileSystem/SysFS/Subsystems/Kernel/GlobalInformation.h>
#include <Kernel/FileSystem/SysFS/Subsystems/Kernel/Interrupts.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/Keymap.h>
#include <Kernel/FileSystem/SysFS/Subsystems/Kernel/LoadBase.h> #include <Kernel/FileSystem/SysFS/Subsystems/Kernel/LoadBase.h>
#include <Kernel/FileSystem/SysFS/Subsystems/Kernel/Log.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(SysFSProfile::must_create(*global_kernel_stats_directory));
list.append(SysFSKernelLoadBase::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(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(SysFSGlobalNetworkStatsDirectory::must_create(*global_kernel_stats_directory));
list.append(SysFSGlobalKernelVariablesDirectory::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)); 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(build_process(array, *Scheduler::colonel()));
TRY(Process::all_instances().with([&](auto& processes) -> ErrorOr<void> { TRY(Process::for_each_in_same_jail([&](Process& process) -> ErrorOr<void> {
for (auto& process : processes) TRY(build_process(array, process));
TRY(build_process(array, process));
return {}; return {};
})); }));
TRY(array.finish()); TRY(array.finish());

View file

@ -5,6 +5,7 @@
*/ */
#include <Kernel/FileSystem/SysFS/Subsystems/Kernel/Variables/BooleanVariable.h> #include <Kernel/FileSystem/SysFS/Subsystems/Kernel/Variables/BooleanVariable.h>
#include <Kernel/Process.h>
#include <Kernel/Sections.h> #include <Kernel/Sections.h>
namespace Kernel { 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*) 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); 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; char value = 0;
TRY(buffer.read(&value, 1)); TRY(buffer.read(&value, 1));
if (value == '0')
set_value(false); return Process::current().jail().with([&](auto& my_jail) -> ErrorOr<size_t> {
else if (value == '1') // Note: If we are in a jail, don't let the current process to change the variable.
set_value(true); if (my_jail)
else return Error::from_errno(EPERM);
return EINVAL; if (count != 1)
return 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) ErrorOr<void> SysFSSystemBoolean::truncate(u64 size)

View file

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

View file

@ -9,6 +9,7 @@
#include <AK/Types.h> #include <AK/Types.h>
#include <Kernel/FileSystem/SysFS/Subsystems/Kernel/Variables/BooleanVariable.h> #include <Kernel/FileSystem/SysFS/Subsystems/Kernel/Variables/BooleanVariable.h>
#include <Kernel/Library/LockRefPtr.h> #include <Kernel/Library/LockRefPtr.h>
#include <Kernel/Locking/Spinlock.h>
#include <Kernel/UserOrKernelBuffer.h> #include <Kernel/UserOrKernelBuffer.h>
namespace Kernel { namespace Kernel {
@ -24,7 +25,7 @@ private:
explicit SysFSCapsLockRemap(SysFSDirectory const&); 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 bool SysFSDumpKmallocStacks::value() const
{ {
MutexLocker locker(m_lock); SpinlockLocker locker(m_lock);
return g_dump_kmalloc_stacks; return g_dump_kmalloc_stacks;
} }
void SysFSDumpKmallocStacks::set_value(bool new_value) void SysFSDumpKmallocStacks::set_value(bool new_value)
{ {
MutexLocker locker(m_lock); SpinlockLocker locker(m_lock);
g_dump_kmalloc_stacks = new_value; g_dump_kmalloc_stacks = new_value;
} }

View file

@ -9,6 +9,7 @@
#include <AK/Types.h> #include <AK/Types.h>
#include <Kernel/FileSystem/SysFS/Subsystems/Kernel/Variables/BooleanVariable.h> #include <Kernel/FileSystem/SysFS/Subsystems/Kernel/Variables/BooleanVariable.h>
#include <Kernel/Library/LockRefPtr.h> #include <Kernel/Library/LockRefPtr.h>
#include <Kernel/Locking/Spinlock.h>
#include <Kernel/UserOrKernelBuffer.h> #include <Kernel/UserOrKernelBuffer.h>
namespace Kernel { namespace Kernel {
@ -24,7 +25,7 @@ private:
explicit SysFSDumpKmallocStacks(SysFSDirectory const&); 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 Inode;
class InodeIdentifier; class InodeIdentifier;
class InodeWatcher; class InodeWatcher;
class Jail;
class KBuffer; class KBuffer;
class KString; class KString;
class LocalSocket; 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 }; return CommittedPhysicalPageSet { {}, page_count };
}); });
if (result.is_error()) { 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_resident = 0;
size_t amount_shared = 0; size_t amount_shared = 0;
size_t amount_virtual = 0; size_t amount_virtual = 0;

View file

@ -63,6 +63,95 @@ SpinlockProtected<Process::List>& Process::all_instances()
return *s_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() ProcessID Process::allocate_pid()
{ {
// Overflow is UB, and negative PIDs wreck havoc. // 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(); 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> { return all_instances().with([&](auto const& list) -> LockRefPtr<Process> {
for (auto const& process : list) { for (auto const& process : list) {
@ -657,20 +772,25 @@ void Process::finalize()
m_fds.with_exclusive([](auto& fds) { fds.clear(); }); m_fds.with_exclusive([](auto& fds) { fds.clear(); });
m_tty = nullptr; m_tty = nullptr;
m_executable.with([](auto& executable) { executable = nullptr; }); m_executable.with([](auto& executable) { executable = nullptr; });
m_attached_jail.with([](auto& jail) {
if (jail)
jail->detach({});
jail = nullptr;
});
m_arguments.clear(); m_arguments.clear();
m_environment.clear(); m_environment.clear();
m_state.store(State::Dead, AK::MemoryOrder::memory_order_release); 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) 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); (void)parent_process->send_signal(SIGCHLD, this);
} }
} }
if (!!ppid()) { 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_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; 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; LockRefPtr<Process> waiter_process;
if (auto* my_tracer = tracer()) 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 else
waiter_process = Process::from_pid(ppid()); waiter_process = Process::from_pid_ignoring_jails(ppid());
if (waiter_process) if (waiter_process)
waiter_process->m_wait_blocker_set.unblock(*this, flags, signal); waiter_process->m_wait_blocker_set.unblock(*this, flags, signal);

View file

@ -24,6 +24,7 @@
#include <Kernel/FileSystem/UnveilNode.h> #include <Kernel/FileSystem/UnveilNode.h>
#include <Kernel/Forward.h> #include <Kernel/Forward.h>
#include <Kernel/FutexQueue.h> #include <Kernel/FutexQueue.h>
#include <Kernel/Jail.h>
#include <Kernel/Library/LockWeakPtr.h> #include <Kernel/Library/LockWeakPtr.h>
#include <Kernel/Library/LockWeakable.h> #include <Kernel/Library/LockWeakable.h>
#include <Kernel/Library/NonnullLockRefPtrVector.h> #include <Kernel/Library/NonnullLockRefPtrVector.h>
@ -70,6 +71,7 @@ Time kgettimeofday();
__ENUMERATE_PLEDGE_PROMISE(prot_exec) \ __ENUMERATE_PLEDGE_PROMISE(prot_exec) \
__ENUMERATE_PLEDGE_PROMISE(map_fixed) \ __ENUMERATE_PLEDGE_PROMISE(map_fixed) \
__ENUMERATE_PLEDGE_PROMISE(getkeymap) \ __ENUMERATE_PLEDGE_PROMISE(getkeymap) \
__ENUMERATE_PLEDGE_PROMISE(jail) \
__ENUMERATE_PLEDGE_PROMISE(no_error) __ENUMERATE_PLEDGE_PROMISE(no_error)
enum class Pledge : u32 { enum class Pledge : u32 {
@ -213,7 +215,8 @@ public:
bool is_kernel_process() const { return m_is_kernel_process; } bool is_kernel_process() const { return m_is_kernel_process; }
bool is_user_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); static SessionID get_sid_from_pgid(ProcessGroupID pgid);
StringView name() const { return m_name->view(); } StringView name() const { return m_name->view(); }
@ -233,6 +236,8 @@ public:
return with_protected_data([](auto& protected_data) { return protected_data.ppid; }); return with_protected_data([](auto& protected_data) { return protected_data.ppid; });
} }
SpinlockProtected<RefPtr<Jail>>& jail() { return m_attached_jail; }
NonnullRefPtr<Credentials> credentials() const; NonnullRefPtr<Credentials> credentials() const;
bool is_dumpable() const bool is_dumpable() const
@ -248,11 +253,11 @@ public:
// Breakable iteration functions // Breakable iteration functions
template<IteratorFunction<Process&> Callback> template<IteratorFunction<Process&> Callback>
static void for_each(Callback); static void for_each_ignoring_jails(Callback);
template<IteratorFunction<Process&> Callback>
static void for_each_in_pgrp(ProcessGroupID, Callback); static ErrorOr<void> for_each_in_same_jail(Function<ErrorOr<void>(Process&)>);
template<IteratorFunction<Process&> Callback> ErrorOr<void> for_each_in_pgrp_in_same_jail(ProcessGroupID, Function<ErrorOr<void>(Process&)>);
void for_each_child(Callback); ErrorOr<void> for_each_child_in_same_jail(Function<ErrorOr<void>(Process&)>);
template<IteratorFunction<Thread&> Callback> template<IteratorFunction<Thread&> Callback>
IterationDecision for_each_thread(Callback); IterationDecision for_each_thread(Callback);
@ -262,11 +267,7 @@ public:
// Non-breakable iteration functions // Non-breakable iteration functions
template<VoidFunction<Process&> Callback> template<VoidFunction<Process&> Callback>
static void for_each(Callback); static void for_each_ignoring_jails(Callback);
template<VoidFunction<Process&> Callback>
static void for_each_in_pgrp(ProcessGroupID, Callback);
template<VoidFunction<Process&> Callback>
void for_each_child(Callback);
template<VoidFunction<Thread&> Callback> template<VoidFunction<Thread&> Callback>
IterationDecision for_each_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$statvfs(Userspace<Syscall::SC_statvfs_params const*> user_params);
ErrorOr<FlatPtr> sys$fstatvfs(int fd, statvfs* buf); ErrorOr<FlatPtr> sys$fstatvfs(int fd, statvfs* buf);
ErrorOr<FlatPtr> sys$map_time_page(); 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> template<bool sockname, typename Params>
ErrorOr<void> get_sock_or_peer_name(Params const&); ErrorOr<void> get_sock_or_peer_name(Params const&);
@ -862,6 +865,10 @@ private:
LockRefPtr<TTY> m_tty; LockRefPtr<TTY> m_tty;
LockWeakPtr<Memory::Region> m_master_tls_region; 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_size { 0 };
size_t m_master_tls_alignment { 0 }; size_t m_master_tls_alignment { 0 };
@ -914,48 +921,6 @@ static_assert(AssertSize<Process, (PAGE_SIZE * 2)>());
extern RecursiveSpinlock g_profiling_lock; 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> template<IteratorFunction<Thread&> Callback>
inline IterationDecision Process::for_each_thread(Callback 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> 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) { Process::all_instances().with([&](auto const& list) {
for (auto it = list.begin(); it != list.end();) { for (auto it = list.begin(); it != list.end();) {
auto& process = *it; auto& process = *it;
++it; ++it;
if (!process.is_dead() && process.pgid() == pgid) { if (callback(process) == IterationDecision::Break)
if (callback(process) == IterationDecision::Break) break;
break;
}
} }
}); });
} }
template<VoidFunction<Process&> Callback> template<IteratorFunction<Thread&> Callback>
inline void Process::for_each(Callback callback) inline IterationDecision Process::for_each_thread(Callback callback) const
{ {
return for_each([&](auto& item) { return thread_list().with([&](auto& thread_list) -> IterationDecision {
callback(item); for (auto& thread : thread_list) {
return IterationDecision::Continue; IterationDecision decision = callback(thread);
}); if (decision != IterationDecision::Continue)
} return decision;
}
template<VoidFunction<Process&> Callback>
inline void Process::for_each_child(Callback callback)
{
return for_each_child([&](auto& item) {
callback(item);
return IterationDecision::Continue; return IterationDecision::Continue;
}); });
} }
@ -1031,15 +989,6 @@ inline IterationDecision Process::for_each_thread(Callback callback)
return IterationDecision::Continue; 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 inline ProcessID Thread::pid() const
{ {
return m_process->pid(); 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 })); TRY(callback({ component.name(), identifier, 0 }));
} }
return Process::all_instances().with([&](auto& list) -> ErrorOr<void> { return Process::for_each_in_same_jail([&](Process& process) -> ErrorOr<void> {
for (auto& process : list) { VERIFY(!(process.pid() < 0));
VERIFY(!(process.pid() < 0)); u64 process_id = (u64)process.pid().value();
u64 process_id = (u64)process.pid().value(); InodeIdentifier identifier = { fsid, static_cast<InodeIndex>(process_id << 36) };
InodeIdentifier identifier = { fsid, static_cast<InodeIndex>(process_id << 36) }; auto process_id_string = TRY(KString::formatted("{:d}", process_id));
auto process_id_string = TRY(KString::formatted("{:d}", process_id)); TRY(callback({ process_id_string->view(), identifier, 0 }));
TRY(callback({ process_id_string->view(), identifier, 0 }));
}
return {}; return {};
}); });
} }
@ -234,7 +232,7 @@ ErrorOr<NonnullLockRefPtr<ProcFSExposedComponent>> ProcFSRootDirectory::lookup(S
return ESRCH; return ESRCH;
auto actual_pid = pid.value(); 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 maybe_process->procfs_traits();
return ENOENT; 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, SegmentedProcFSIndex::build_segmented_index_for_sub_directory(pid(), SegmentedProcFSIndex::ProcessSubDirectory::Children) }, 0 }));
TRY(callback({ ".."sv, { fsid, m_procfs_traits->component_index() }, 0 })); TRY(callback({ ".."sv, { fsid, m_procfs_traits->component_index() }, 0 }));
return Process::all_instances().with([&](auto& processes) -> ErrorOr<void> { return Process::for_each_in_same_jail([&](Process& process) -> ErrorOr<void> {
for (auto& process : processes) { if (process.ppid() == pid()) {
if (process.ppid() == pid()) { StringBuilder builder;
StringBuilder builder; builder.appendff("{}", process.pid());
builder.appendff("{}", process.pid()); TRY(callback({ builder.string_view(), { fsid, SegmentedProcFSIndex::build_segmented_index_for_children(pid(), process.pid()) }, DT_LNK }));
TRY(callback({ builder.string_view(), { fsid, SegmentedProcFSIndex::build_segmented_index_for_children(pid(), process.pid()) }, DT_LNK }));
}
} }
return {}; return {};
}); });
@ -103,7 +101,7 @@ ErrorOr<NonnullLockRefPtr<Inode>> Process::lookup_children_directory(ProcFS cons
if (!maybe_pid.has_value()) if (!maybe_pid.has_value())
return ENOENT; 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()) if (!child_process || child_process->ppid() != pid())
return ENOENT; return ENOENT;

View file

@ -12,7 +12,7 @@ ErrorOr<FlatPtr> Process::sys$disown(ProcessID pid)
{ {
VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this); VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this);
TRY(require_promise(Pledge::proc)); TRY(require_promise(Pledge::proc));
auto process = Process::from_pid(pid); auto process = Process::from_pid_in_same_jail(pid);
if (!process) if (!process)
return ESRCH; return ESRCH;
if (process->ppid() != this->pid()) 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 old_credentials = this->credentials();
auto new_credentials = old_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; 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_executable.with([&](auto& executable) { executable = main_program_description->custody(); });
m_arguments = move(arguments); m_arguments = move(arguments);
m_attached_jail.with([&](auto& jail) {
jail = old_process_attached_jail;
});
m_environment = move(environment); m_environment = move(environment);
TRY(m_unveil_data.with([&](auto& unveil_data) -> ErrorOr<void> { 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) { TRY(child->m_fds.with_exclusive([&](auto& child_fds) {
return m_fds.with_exclusive([&](auto& parent_fds) { return m_fds.with_exclusive([&](auto& parent_fds) {
return child_fds.try_clone(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; bool any_succeeded = false;
ErrorOr<void> error; 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; group_was_empty = false;
ErrorOr<void> res = do_kill(process, signal); ErrorOr<void> res = do_kill(process, signal);
@ -50,7 +50,8 @@ ErrorOr<void> Process::do_killpg(ProcessGroupID pgrp, int signal)
any_succeeded = true; any_succeeded = true;
else else
error = move(res); error = move(res);
}); return {};
}));
if (group_was_empty) if (group_was_empty)
return ESRCH; return ESRCH;
@ -67,7 +68,7 @@ ErrorOr<void> Process::do_killall(int signal)
ErrorOr<void> error; ErrorOr<void> error;
// Send the signal to all processes we have access to for. // 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; ErrorOr<void> res;
if (process.pid() == pid()) if (process.pid() == pid())
res = do_killself(signal); res = do_killself(signal);
@ -78,7 +79,8 @@ ErrorOr<void> Process::do_killall(int signal)
any_succeeded = true; any_succeeded = true;
else else
error = move(res); error = move(res);
}); return {};
}));
if (any_succeeded) if (any_succeeded)
return {}; return {};
@ -122,7 +124,7 @@ ErrorOr<FlatPtr> Process::sys$kill(pid_t pid_or_pgid, int signal)
return 0; return 0;
} }
VERIFY(pid_or_pgid >= 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) if (!peer)
return ESRCH; return ESRCH;
TRY(do_kill(*peer, signal)); TRY(do_kill(*peer, signal));

View file

@ -53,15 +53,15 @@ ErrorOr<FlatPtr> Process::profiling_enable(pid_t pid, u64 event_mask)
return ENOTSUP; return ENOTSUP;
g_profiling_all_threads = true; g_profiling_all_threads = true;
PerformanceManager::add_process_created_event(*Scheduler::colonel()); 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); PerformanceManager::add_process_created_event(process);
return IterationDecision::Continue; return {};
}); }));
g_profiling_event_mask = event_mask; g_profiling_event_mask = event_mask;
return 0; return 0;
} }
auto process = Process::from_pid(pid); auto process = Process::from_pid_in_same_jail(pid);
if (!process) if (!process)
return ESRCH; return ESRCH;
if (process->is_dead()) if (process->is_dead())
@ -101,7 +101,7 @@ ErrorOr<FlatPtr> Process::sys$profiling_disable(pid_t pid)
return 0; return 0;
} }
auto process = Process::from_pid(pid); auto process = Process::from_pid_in_same_jail(pid);
if (!process) if (!process)
return ESRCH; return ESRCH;
auto credentials = this->credentials(); auto credentials = this->credentials();
@ -140,7 +140,7 @@ ErrorOr<FlatPtr> Process::sys$profiling_free_buffer(pid_t pid)
return 0; return 0;
} }
auto process = Process::from_pid(pid); auto process = Process::from_pid_in_same_jail(pid);
if (!process) if (!process)
return ESRCH; return ESRCH;
auto credentials = this->credentials(); 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: { case Syscall::SchedulerParametersMode::Process: {
auto* searched_process = this; auto* searched_process = this;
if (pid_or_tid != 0) 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) if (searched_process == nullptr)
return ESRCH; return ESRCH;

View file

@ -16,7 +16,7 @@ ErrorOr<FlatPtr> Process::sys$getsid(pid_t pid)
TRY(require_promise(Pledge::stdio)); TRY(require_promise(Pledge::stdio));
if (pid == 0) if (pid == 0)
return sid().value(); return sid().value();
auto process = Process::from_pid(pid); auto process = Process::from_pid_in_same_jail(pid);
if (!process) if (!process)
return ESRCH; return ESRCH;
if (sid() != process->sid()) if (sid() != process->sid())
@ -30,10 +30,10 @@ ErrorOr<FlatPtr> Process::sys$setsid()
TRY(require_promise(Pledge::proc)); TRY(require_promise(Pledge::proc));
InterruptDisabler disabler; InterruptDisabler disabler;
bool found_process_with_same_pgid_as_my_pid = false; 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; found_process_with_same_pgid_as_my_pid = true;
return IterationDecision::Break; return {};
}); }));
if (found_process_with_same_pgid_as_my_pid) if (found_process_with_same_pgid_as_my_pid)
return EPERM; return EPERM;
// Create a new Session and a new ProcessGroup. // 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)); TRY(require_promise(Pledge::stdio));
if (pid == 0) if (pid == 0)
return pgid().value(); return pgid().value();
auto process = Process::from_pid(pid); auto process = Process::from_pid_in_same_jail(pid);
if (!process) if (!process)
return ESRCH; return ESRCH;
return process->pgid().value(); 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. // FIXME: This xor sys$setsid() uses the wrong locking mechanism.
SessionID sid { -1 }; 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(); sid = process.sid();
return IterationDecision::Break; return {};
}); }));
return sid; 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. // The value of the pgid argument is less than 0, or is not a value supported by the implementation.
return EINVAL; return EINVAL;
} }
auto process = Process::from_pid(pid); auto process = Process::from_pid_in_same_jail(pid);
if (!process) if (!process)
return ESRCH; return ESRCH;
if (process != this && process->ppid() != this->pid()) { 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: case P_ALL:
break; break;
case P_PID: { 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) if (!waitee_process)
return ECHILD; return ECHILD;
bool waitee_is_child = waitee_process->ppid() == Process::current().pid(); bool waitee_is_child = waitee_process->ppid() == Process::current().pid();

View file

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

View file

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