mirror of
https://github.com/RGBCube/serenity
synced 2025-07-24 21:37:34 +00:00
ProcFS: Fix /proc/PID/* hardening bypass
This enabled trivial ASLR bypass for non-dumpable programs by simply opening /proc/PID/vm before exec'ing. We now hold the target process's ptrace lock across the refresh/write operations, and deny access if the process is non-dumpable. The lock is necessary to prevent a TOCTOU race on Process::is_dumpable() while the target is exec'ing. Fixes #5270.
This commit is contained in:
parent
7142562310
commit
37d8faf1b4
2 changed files with 40 additions and 0 deletions
|
@ -28,6 +28,7 @@
|
||||||
#include <AK/JsonObject.h>
|
#include <AK/JsonObject.h>
|
||||||
#include <AK/JsonObjectSerializer.h>
|
#include <AK/JsonObjectSerializer.h>
|
||||||
#include <AK/JsonValue.h>
|
#include <AK/JsonValue.h>
|
||||||
|
#include <AK/ScopeGuard.h>
|
||||||
#include <Kernel/Arch/i386/CPU.h>
|
#include <Kernel/Arch/i386/CPU.h>
|
||||||
#include <Kernel/Arch/i386/ProcessorInfo.h>
|
#include <Kernel/Arch/i386/ProcessorInfo.h>
|
||||||
#include <Kernel/CommandLine.h>
|
#include <Kernel/CommandLine.h>
|
||||||
|
@ -1048,11 +1049,32 @@ ProcFSInode::~ProcFSInode()
|
||||||
fs().m_inodes.remove(it);
|
fs().m_inodes.remove(it);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RefPtr<Process> ProcFSInode::process() const
|
||||||
|
{
|
||||||
|
return Process::from_pid(to_pid(identifier()));
|
||||||
|
}
|
||||||
|
|
||||||
KResult ProcFSInode::refresh_data(FileDescription& description) const
|
KResult ProcFSInode::refresh_data(FileDescription& description) const
|
||||||
{
|
{
|
||||||
if (Kernel::is_directory(identifier()))
|
if (Kernel::is_directory(identifier()))
|
||||||
return KSuccess;
|
return KSuccess;
|
||||||
|
|
||||||
|
// 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 = this->process();
|
||||||
|
if (process) {
|
||||||
|
process->ptrace_lock().lock();
|
||||||
|
if (!process->is_dumpable()) {
|
||||||
|
process->ptrace_lock().unlock();
|
||||||
|
return EPERM;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ScopeGuard guard = [&] {
|
||||||
|
if (process)
|
||||||
|
process->ptrace_lock().unlock();
|
||||||
|
};
|
||||||
|
|
||||||
auto& cached_data = description.data();
|
auto& cached_data = description.data();
|
||||||
auto* directory_entry = fs().get_directory_entry(identifier());
|
auto* directory_entry = fs().get_directory_entry(identifier());
|
||||||
|
|
||||||
|
@ -1440,6 +1462,22 @@ void ProcFSInode::flush_metadata()
|
||||||
|
|
||||||
ssize_t ProcFSInode::write_bytes(off_t offset, ssize_t size, const UserOrKernelBuffer& buffer, FileDescription*)
|
ssize_t ProcFSInode::write_bytes(off_t offset, ssize_t size, const UserOrKernelBuffer& buffer, FileDescription*)
|
||||||
{
|
{
|
||||||
|
// For process-specific inodes, hold the process's ptrace lock across the write
|
||||||
|
// and refuse to write at all 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 = this->process();
|
||||||
|
if (process) {
|
||||||
|
process->ptrace_lock().lock();
|
||||||
|
if (!process->is_dumpable()) {
|
||||||
|
process->ptrace_lock().unlock();
|
||||||
|
return EPERM;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ScopeGuard guard = [&] {
|
||||||
|
if (process)
|
||||||
|
process->ptrace_lock().unlock();
|
||||||
|
};
|
||||||
|
|
||||||
auto result = prepare_to_write_data();
|
auto result = prepare_to_write_data();
|
||||||
if (result.is_error())
|
if (result.is_error())
|
||||||
return result;
|
return result;
|
||||||
|
|
|
@ -114,6 +114,8 @@ private:
|
||||||
|
|
||||||
KResult refresh_data(FileDescription&) const;
|
KResult refresh_data(FileDescription&) const;
|
||||||
|
|
||||||
|
RefPtr<Process> process() const;
|
||||||
|
|
||||||
ProcFS& fs() { return static_cast<ProcFS&>(Inode::fs()); }
|
ProcFS& fs() { return static_cast<ProcFS&>(Inode::fs()); }
|
||||||
const ProcFS& fs() const { return static_cast<const ProcFS&>(Inode::fs()); }
|
const ProcFS& fs() const { return static_cast<const ProcFS&>(Inode::fs()); }
|
||||||
ProcFSInode(ProcFS&, InodeIndex);
|
ProcFSInode(ProcFS&, InodeIndex);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue