mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-21 23:20:20 +00:00
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:
parent
d69a0380e1
commit
5e062414c1
Notes:
sideshowbarker
2024-07-17 04:45:46 +09:00
Author: https://github.com/supercomputer7 Commit: https://github.com/SerenityOS/serenity/commit/5e062414c1 Pull-request: https://github.com/SerenityOS/serenity/pull/15895 Reviewed-by: https://github.com/ADKaster ✅ Reviewed-by: https://github.com/LucasChollet Reviewed-by: https://github.com/tslater2006
35 changed files with 609 additions and 160 deletions
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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));
|
||||
|
|
38
Kernel/FileSystem/SysFS/Subsystems/Kernel/Jails.cpp
Normal file
38
Kernel/FileSystem/SysFS/Subsystems/Kernel/Jails.cpp
Normal 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 {};
|
||||
}
|
||||
|
||||
}
|
28
Kernel/FileSystem/SysFS/Subsystems/Kernel/Jails.h
Normal file
28
Kernel/FileSystem/SysFS/Subsystems/Kernel/Jails.h
Normal 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;
|
||||
};
|
||||
|
||||
}
|
|
@ -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());
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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 };
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 };
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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
34
Kernel/Jail.cpp
Normal 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
48
Kernel/Jail.h
Normal 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
76
Kernel/JailManagement.cpp
Normal 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
42
Kernel/JailManagement.h
Normal 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 };
|
||||
};
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
109
Kernel/Process.h
109
Kernel/Process.h
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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
59
Kernel/Syscalls/jail.cpp
Normal 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, ¶ms));
|
||||
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;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -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));
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 {};
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue