1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-20 17:45:07 +00:00

Kernel: Move select Process members into protected memory

Process member variable like m_euid are very valuable targets for
kernel exploits and until now they have been writable at all times.

This patch moves m_euid along with a whole bunch of other members
into a new Process::ProtectedData struct. This struct is remapped
as read-only memory whenever we don't need to write to it.

This means that a kernel write primitive is no longer enough to
overwrite a process's effective UID, you must first unprotect the
protected data where the UID is stored. :^)
This commit is contained in:
Andreas Kling 2021-03-10 19:59:46 +01:00
parent 839d2d70a4
commit cbcf891040
12 changed files with 190 additions and 130 deletions

View file

@ -25,7 +25,6 @@
*/
#include <AK/Demangle.h>
#include <AK/QuickSort.h>
#include <AK/StdLibExtras.h>
#include <AK/StringBuilder.h>
#include <AK/Time.h>
@ -38,7 +37,6 @@
#include <Kernel/FileSystem/Custody.h>
#include <Kernel/FileSystem/FileDescription.h>
#include <Kernel/FileSystem/VirtualFileSystem.h>
#include <Kernel/Heap/kmalloc.h>
#include <Kernel/KBufferBuilder.h>
#include <Kernel/KSyms.h>
#include <Kernel/Module.h>
@ -51,7 +49,6 @@
#include <Kernel/VM/AnonymousVMObject.h>
#include <Kernel/VM/PageDirectory.h>
#include <Kernel/VM/PrivateInodeVMObject.h>
#include <Kernel/VM/ProcessPagingScope.h>
#include <Kernel/VM/SharedInodeVMObject.h>
#include <LibC/errno_numbers.h>
#include <LibC/limits.h>
@ -113,7 +110,7 @@ NonnullRefPtrVector<Process> Process::all_processes()
bool Process::in_group(gid_t gid) const
{
return m_gid == gid || m_extra_gids.contains_slow(gid);
return this->gid() == gid || m_extra_gids.contains_slow(gid);
}
void Process::kill_threads_except_self()
@ -212,15 +209,31 @@ RefPtr<Process> Process::create_kernel_process(RefPtr<Thread>& first_thread, Str
return process;
}
const Process::ProtectedData& Process::protected_data() const
{
return *reinterpret_cast<const ProtectedData*>(m_protected_data->data());
}
void Process::protect_data()
{
auto& region = m_protected_data->impl().region();
if (!region.is_writable())
return;
region.set_writable(false);
region.remap();
}
void Process::unprotect_data()
{
auto& region = m_protected_data->impl().region();
if (region.is_writable())
return;
region.set_writable(true);
region.remap();
}
Process::Process(RefPtr<Thread>& first_thread, const String& name, uid_t uid, gid_t gid, ProcessID ppid, bool is_kernel_process, RefPtr<Custody> cwd, RefPtr<Custody> executable, TTY* tty, Process* fork_parent)
: m_name(move(name))
, m_pid(allocate_pid())
, m_euid(uid)
, m_egid(gid)
, m_uid(uid)
, m_gid(gid)
, m_suid(uid)
, m_sgid(gid)
, m_is_kernel_process(is_kernel_process)
, m_executable(move(executable))
, m_cwd(move(cwd))
@ -228,7 +241,21 @@ Process::Process(RefPtr<Thread>& first_thread, const String& name, uid_t uid, gi
, m_ppid(ppid)
, m_wait_block_condition(*this)
{
dbgln_if(PROCESS_DEBUG, "Created new process {}({})", m_name, m_pid.value());
m_protected_data = KBuffer::try_create_with_size(sizeof(ProtectedData));
VERIFY(m_protected_data);
{
MutableProtectedData protected_data { *this };
protected_data->pid = allocate_pid();
protected_data->uid = uid;
protected_data->gid = gid;
protected_data->euid = uid;
protected_data->egid = gid;
protected_data->suid = uid;
protected_data->sgid = gid;
}
dbgln_if(PROCESS_DEBUG, "Created new process {}({})", m_name, this->pid().value());
m_space = Space::create(*this, fork_parent ? &fork_parent->space() : nullptr);
@ -433,8 +460,8 @@ bool Process::dump_core()
{
VERIFY(is_dumpable());
VERIFY(should_core_dump());
dbgln("Generating coredump for pid: {}", m_pid.value());
auto coredump_path = String::formatted("/tmp/coredump/{}_{}_{}", name(), m_pid.value(), RTC::now());
dbgln("Generating coredump for pid: {}", pid().value());
auto coredump_path = String::formatted("/tmp/coredump/{}_{}_{}", name(), pid().value(), RTC::now());
auto coredump = CoreDump::create(*this, coredump_path);
if (!coredump)
return false;
@ -445,8 +472,8 @@ bool Process::dump_perfcore()
{
VERIFY(is_dumpable());
VERIFY(m_perf_event_buffer);
dbgln("Generating perfcore for pid: {}", m_pid.value());
auto description_or_error = VFS::the().open(String::formatted("perfcore.{}", m_pid.value()), O_CREAT | O_EXCL, 0400, current_directory(), UidAndGid { m_uid, m_gid });
dbgln("Generating perfcore for pid: {}", pid().value());
auto description_or_error = VFS::the().open(String::formatted("perfcore.{}", pid().value()), O_CREAT | O_EXCL, 0400, current_directory(), UidAndGid { uid(), gid() });
if (description_or_error.is_error())
return false;
auto& description = description_or_error.value();
@ -547,7 +574,7 @@ void Process::die()
ScopedSpinLock lock(g_processes_lock);
for (auto* process = g_processes->head(); process;) {
auto* next_process = process->next();
if (process->has_tracee_thread(m_pid)) {
if (process->has_tracee_thread(pid())) {
dbgln_if(PROCESS_DEBUG, "Process {} ({}) is attached by {} ({}) which will exit", process->name(), process->pid(), name(), pid());
process->stop_tracing();
auto err = process->send_signal(SIGSTOP, this);
@ -576,7 +603,7 @@ void Process::terminate_due_to_signal(u8 signal)
KResult Process::send_signal(u8 signal, Process* sender)
{
// Try to send it to the "obvious" main thread:
auto receiver_thread = Thread::from_tid(m_pid.value());
auto receiver_thread = Thread::from_tid(pid().value());
// If the main thread has died, there may still be other threads:
if (!receiver_thread) {
// The first one should be good enough.