mirror of
https://github.com/RGBCube/serenity
synced 2025-10-22 21:12:07 +00:00

To accomplish this, we add another VeilState which is called LockedInherited. The idea is to apply exec unveil data, similar to execpromises of the pledge syscall, on the current exec'ed program during the execve sequence. When applying the forced unveil data, the veil state is set to be locked but the special state of LockedInherited ensures that if the new program tries to unveil paths, the request will silently be ignored, so the program will continue running without receiving an error, but is still can only use the paths that were unveiled before the exec syscall. This in turn, allows us to use the unveil syscall with a special utility to sandbox other userland programs in terms of what is visible to them on the filesystem, and is usable on both programs that use or don't use the unveil syscall in their code.
165 lines
7.5 KiB
C++
165 lines
7.5 KiB
C++
/*
|
|
* Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#include <AK/JsonObjectSerializer.h>
|
|
#include <AK/Try.h>
|
|
#include <Kernel/FileSystem/SysFS/Subsystems/Kernel/Processes.h>
|
|
#include <Kernel/Process.h>
|
|
#include <Kernel/Scheduler.h>
|
|
#include <Kernel/Sections.h>
|
|
#include <Kernel/TTY/TTY.h>
|
|
|
|
namespace Kernel {
|
|
|
|
UNMAP_AFTER_INIT SysFSOverallProcesses::SysFSOverallProcesses(SysFSDirectory const& parent_directory)
|
|
: SysFSGlobalInformation(parent_directory)
|
|
{
|
|
}
|
|
|
|
UNMAP_AFTER_INIT NonnullLockRefPtr<SysFSOverallProcesses> SysFSOverallProcesses::must_create(SysFSDirectory const& parent_directory)
|
|
{
|
|
return adopt_lock_ref_if_nonnull(new (nothrow) SysFSOverallProcesses(parent_directory)).release_nonnull();
|
|
}
|
|
|
|
ErrorOr<void> SysFSOverallProcesses::try_generate(KBufferBuilder& builder)
|
|
{
|
|
auto json = TRY(JsonObjectSerializer<>::try_create(builder));
|
|
|
|
// Keep this in sync with CProcessStatistics.
|
|
auto build_process = [&](JsonArraySerializer<KBufferBuilder>& array, Process const& process) -> ErrorOr<void> {
|
|
auto process_object = TRY(array.add_object());
|
|
|
|
if (process.is_user_process()) {
|
|
StringBuilder pledge_builder;
|
|
|
|
#define __ENUMERATE_PLEDGE_PROMISE(promise) \
|
|
if (process.has_promised(Pledge::promise)) \
|
|
TRY(pledge_builder.try_append(#promise " "sv));
|
|
ENUMERATE_PLEDGE_PROMISES
|
|
#undef __ENUMERATE_PLEDGE_PROMISE
|
|
|
|
TRY(process_object.add("pledge"sv, pledge_builder.string_view()));
|
|
|
|
switch (process.veil_state()) {
|
|
case VeilState::None:
|
|
TRY(process_object.add("veil"sv, "None"));
|
|
break;
|
|
case VeilState::Dropped:
|
|
TRY(process_object.add("veil"sv, "Dropped"));
|
|
break;
|
|
case VeilState::Locked:
|
|
TRY(process_object.add("veil"sv, "Locked"));
|
|
break;
|
|
case VeilState::LockedInherited:
|
|
// Note: We don't reveal if the locked state is either by our choice
|
|
// or someone else applied it.
|
|
TRY(process_object.add("veil"sv, "Locked"));
|
|
break;
|
|
}
|
|
} else {
|
|
TRY(process_object.add("pledge"sv, ""sv));
|
|
TRY(process_object.add("veil"sv, ""sv));
|
|
}
|
|
|
|
TRY(process_object.add("pid"sv, process.pid().value()));
|
|
TRY(process_object.add("pgid"sv, process.tty() ? process.tty()->pgid().value() : 0));
|
|
TRY(process_object.add("pgp"sv, process.pgid().value()));
|
|
TRY(process_object.add("sid"sv, process.sid().value()));
|
|
auto credentials = process.credentials();
|
|
TRY(process_object.add("uid"sv, credentials->uid().value()));
|
|
TRY(process_object.add("gid"sv, credentials->gid().value()));
|
|
TRY(process_object.add("ppid"sv, process.ppid().value()));
|
|
if (process.tty()) {
|
|
auto tty_pseudo_name = TRY(process.tty()->pseudo_name());
|
|
TRY(process_object.add("tty"sv, tty_pseudo_name->view()));
|
|
} else {
|
|
TRY(process_object.add("tty"sv, ""));
|
|
}
|
|
TRY(process_object.add("nfds"sv, process.fds().with_shared([](auto& fds) { return fds.open_count(); })));
|
|
TRY(process_object.add("name"sv, process.name()));
|
|
TRY(process_object.add("executable"sv, process.executable() ? TRY(process.executable()->try_serialize_absolute_path())->view() : ""sv));
|
|
|
|
size_t amount_virtual = 0;
|
|
size_t amount_resident = 0;
|
|
size_t amount_dirty_private = 0;
|
|
size_t amount_clean_inode = 0;
|
|
size_t amount_shared = 0;
|
|
size_t amount_purgeable_volatile = 0;
|
|
size_t amount_purgeable_nonvolatile = 0;
|
|
|
|
TRY(process.address_space().with([&](auto& space) -> ErrorOr<void> {
|
|
amount_virtual = space->amount_virtual();
|
|
amount_resident = space->amount_resident();
|
|
amount_dirty_private = space->amount_dirty_private();
|
|
amount_clean_inode = TRY(space->amount_clean_inode());
|
|
amount_shared = space->amount_shared();
|
|
amount_purgeable_volatile = space->amount_purgeable_volatile();
|
|
amount_purgeable_nonvolatile = space->amount_purgeable_nonvolatile();
|
|
return {};
|
|
}));
|
|
|
|
TRY(process_object.add("amount_virtual"sv, amount_virtual));
|
|
TRY(process_object.add("amount_resident"sv, amount_resident));
|
|
TRY(process_object.add("amount_dirty_private"sv, amount_dirty_private));
|
|
TRY(process_object.add("amount_clean_inode"sv, amount_clean_inode));
|
|
TRY(process_object.add("amount_shared"sv, amount_shared));
|
|
TRY(process_object.add("amount_purgeable_volatile"sv, amount_purgeable_volatile));
|
|
TRY(process_object.add("amount_purgeable_nonvolatile"sv, amount_purgeable_nonvolatile));
|
|
TRY(process_object.add("dumpable"sv, process.is_dumpable()));
|
|
TRY(process_object.add("kernel"sv, process.is_kernel_process()));
|
|
auto thread_array = TRY(process_object.add_array("threads"sv));
|
|
TRY(process.try_for_each_thread([&](const Thread& thread) -> ErrorOr<void> {
|
|
SpinlockLocker locker(thread.get_lock());
|
|
auto thread_object = TRY(thread_array.add_object());
|
|
#if LOCK_DEBUG
|
|
TRY(thread_object.add("lock_count"sv, thread.lock_count()));
|
|
#endif
|
|
TRY(thread_object.add("tid"sv, thread.tid().value()));
|
|
TRY(thread_object.add("name"sv, thread.name()));
|
|
TRY(thread_object.add("times_scheduled"sv, thread.times_scheduled()));
|
|
TRY(thread_object.add("time_user"sv, thread.time_in_user()));
|
|
TRY(thread_object.add("time_kernel"sv, thread.time_in_kernel()));
|
|
TRY(thread_object.add("state"sv, thread.state_string()));
|
|
TRY(thread_object.add("cpu"sv, thread.cpu()));
|
|
TRY(thread_object.add("priority"sv, thread.priority()));
|
|
TRY(thread_object.add("syscall_count"sv, thread.syscall_count()));
|
|
TRY(thread_object.add("inode_faults"sv, thread.inode_faults()));
|
|
TRY(thread_object.add("zero_faults"sv, thread.zero_faults()));
|
|
TRY(thread_object.add("cow_faults"sv, thread.cow_faults()));
|
|
TRY(thread_object.add("file_read_bytes"sv, thread.file_read_bytes()));
|
|
TRY(thread_object.add("file_write_bytes"sv, thread.file_write_bytes()));
|
|
TRY(thread_object.add("unix_socket_read_bytes"sv, thread.unix_socket_read_bytes()));
|
|
TRY(thread_object.add("unix_socket_write_bytes"sv, thread.unix_socket_write_bytes()));
|
|
TRY(thread_object.add("ipv4_socket_read_bytes"sv, thread.ipv4_socket_read_bytes()));
|
|
TRY(thread_object.add("ipv4_socket_write_bytes"sv, thread.ipv4_socket_write_bytes()));
|
|
|
|
TRY(thread_object.finish());
|
|
return {};
|
|
}));
|
|
TRY(thread_array.finish());
|
|
TRY(process_object.finish());
|
|
return {};
|
|
};
|
|
|
|
{
|
|
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::for_each_in_same_jail([&](Process& process) -> ErrorOr<void> {
|
|
TRY(build_process(array, process));
|
|
return {};
|
|
}));
|
|
TRY(array.finish());
|
|
}
|
|
|
|
auto total_time_scheduled = Scheduler::get_total_time_scheduled();
|
|
TRY(json.add("total_time"sv, total_time_scheduled.total));
|
|
TRY(json.add("total_time_kernel"sv, total_time_scheduled.total_kernel));
|
|
TRY(json.finish());
|
|
return {};
|
|
}
|
|
|
|
}
|