mirror of
https://github.com/HeyPuter/gex.git
synced 2026-11-13 20:12:43 -10:00
kernel, page: getpid, gettid and the 32-bit uid/gid calls answered without the kernel lock
Step 6's first part (docs/kernel-locking.md §3.1): ident_call answers them from the ids the thread's last full syscall saw, beside clock_call and under the same stand-down rules. getppid stays on the full path (asking for the parent is a call into the system-wide kernel, and a reparent would go unseen), as do the 16-bit i386 forms. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_ea9f71488b6e448da93d6c2dabf4879c
This commit is contained in:
1 parent
5892193eeb
commit
fd2ac62850
2 files changed
+111
-1
No files matched your search
@@ -950,6 +950,69 @@ pub fn clock_call(
|
||||
set_result(m, ctx_addr, 0).is_ok()
|
||||
}
|
||||
|
||||
/// What a process and thread are called, as a host last saw them under its
|
||||
/// lock: the answers [`ident_call`] gives without it.
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct Idents {
|
||||
pub pid: u32,
|
||||
pub ppid: u32,
|
||||
pub tid: u32,
|
||||
pub uid: u32,
|
||||
pub gid: u32,
|
||||
}
|
||||
|
||||
/// `getpid`, `getppid`, `gettid` and the 32-bit `get[e]uid`/`get[e]gid`
|
||||
/// answered from `ids` with no lock (docs/kernel-locking.md §3.1), as
|
||||
/// [`clock_call`] answers the clocks: a host that keeps `ids` current calls
|
||||
/// this before taking its lock, and the full syscall runs whenever it returns
|
||||
/// false. `ids.tid == 0` means the host has none yet, and `ids.ppid == 0`
|
||||
/// leaves `getppid` to the full call. The 16-bit i386 forms
|
||||
/// are left to the full call, which owns their overflow rule.
|
||||
pub fn ident_call(m: &mut GuestMem<'_>, ctx_addr: u64, compat: bool, ids: &Idents) -> bool {
|
||||
if gex_abi::PPC || ids.tid == 0 {
|
||||
return false;
|
||||
}
|
||||
let raw = if compat {
|
||||
m.read_u64(ctx_addr + ctx::x(0) as u64).unwrap_or(!0) & 0xffff_ffff
|
||||
} else {
|
||||
m.read_u64(ctx_addr + ctx::SYSCALL_NR as u64).unwrap_or(!0)
|
||||
};
|
||||
let v = if compat {
|
||||
use crate::i386::raw as x;
|
||||
match raw {
|
||||
x::GETPID => ids.pid,
|
||||
x::GETPPID if ids.ppid != 0 => ids.ppid,
|
||||
x::GETTID => ids.tid,
|
||||
x::GETUID | x::GETEUID => ids.uid,
|
||||
x::GETGID | x::GETEGID => ids.gid,
|
||||
_ => return false,
|
||||
}
|
||||
} else if gex_abi::X86 {
|
||||
use crate::x86::raw as x;
|
||||
match raw {
|
||||
x::GETPID => ids.pid,
|
||||
x::GETPPID if ids.ppid != 0 => ids.ppid,
|
||||
x::GETTID => ids.tid,
|
||||
x::GETUID | x::GETEUID => ids.uid,
|
||||
x::GETGID | x::GETEGID => ids.gid,
|
||||
_ => return false,
|
||||
}
|
||||
} else {
|
||||
match raw {
|
||||
nr::GETPID => ids.pid,
|
||||
nr::GETPPID if ids.ppid != 0 => ids.ppid,
|
||||
nr::GETTID => ids.tid,
|
||||
nr::GETUID | nr::GETEUID => ids.uid,
|
||||
nr::GETGID | nr::GETEGID => ids.gid,
|
||||
_ => return false,
|
||||
}
|
||||
};
|
||||
if gex_abi::X86 {
|
||||
let _ = m.write_u64(ctx_addr + ctx::LAST_SYSCALL as u64, raw);
|
||||
}
|
||||
set_result(m, ctx_addr, v as u64).is_ok()
|
||||
}
|
||||
|
||||
pub fn system_clock() -> (u64, u32) {
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
{
|
||||
@@ -7902,6 +7965,35 @@ mod tests {
|
||||
assert_eq!(k.unix_of(pfd).err(), Some(Errno::ENOTSOCK));
|
||||
}
|
||||
|
||||
/// `ident_call` answers the id calls as the kernel does, and declines
|
||||
/// what it was not given.
|
||||
#[test]
|
||||
fn ident_call_answers_as_the_kernel_does() {
|
||||
if gex_abi::PPC || gex_abi::X86 {
|
||||
return;
|
||||
}
|
||||
let (mut mem, mut k) = setup();
|
||||
let at = k.ctx_addr;
|
||||
let ids = Idents { pid: k.pid as u32, ppid: 0, tid: k.tid as u32, uid: k.uid, gid: k.gid };
|
||||
for n in [nr::GETPID, nr::GETTID, nr::GETUID, nr::GETEUID, nr::GETGID, nr::GETEGID] {
|
||||
let mut m = GuestMem::new(&mut mem);
|
||||
set_args(&mut m, at, n, &[]);
|
||||
k.syscall(&mut m, 0);
|
||||
let want = ret(&m, at);
|
||||
m.write_u64(at + ctx::x(0) as u64, 0x5555).unwrap();
|
||||
set_args(&mut m, at, n, &[]);
|
||||
assert!(ident_call(&mut m, at, false, &ids), "call {n}");
|
||||
assert_eq!(ret(&m, at), want, "call {n}");
|
||||
}
|
||||
let mut m = GuestMem::new(&mut mem);
|
||||
set_args(&mut m, at, nr::GETPPID, &[]);
|
||||
assert!(!ident_call(&mut m, at, false, &ids), "no parent given");
|
||||
set_args(&mut m, at, nr::GETPID, &[]);
|
||||
assert!(!ident_call(&mut m, at, false, &Idents { tid: 0, ..ids }), "no thread yet");
|
||||
set_args(&mut m, at, nr::CLOCK_GETTIME, &[0, 0x3000]);
|
||||
assert!(!ident_call(&mut m, at, false, &ids), "not an id call");
|
||||
}
|
||||
|
||||
/// `clock_call`, the lock-free answer, writes what the full call writes,
|
||||
/// in both numberings and layouts, and declines what it cannot answer.
|
||||
#[test]
|
||||
|
||||
@@ -6666,6 +6666,18 @@ pub extern "C" fn gex_syscall(ctx: i32, pc: i64) {
|
||||
// where process 1 never exits to call `gex_pin_report`).
|
||||
check_host_magic_in(h, "a syscall");
|
||||
MY_PID.store(h.kernel.pid, std::sync::atomic::Ordering::Relaxed);
|
||||
// What `ident_call` answers from until this thread's next full call.
|
||||
// No parent: asking for it is a call into the system-wide kernel, and
|
||||
// a reparent would go unseen; `getppid` takes the full path.
|
||||
IDS.with(|c| {
|
||||
c.set(gex_kernel::syscalls::Idents {
|
||||
pid: h.kernel.pid as u32,
|
||||
ppid: 0,
|
||||
tid: h.threads[cur()].kstate.tid as u32,
|
||||
uid: h.kernel.uid,
|
||||
gid: h.kernel.gid,
|
||||
})
|
||||
});
|
||||
report_new_pins_in(h);
|
||||
let mut gm = GuestMem::new(guest_bytes());
|
||||
let kstate = h.threads[cur()].kstate.clone();
|
||||
@@ -7562,6 +7574,10 @@ thread_local! {
|
||||
static FAST_SIG: Cell<i32> = const { Cell::new(i32::MIN) };
|
||||
/// Fast calls since this thread's last full syscall.
|
||||
static FAST_RUN: Cell<u32> = const { Cell::new(0) };
|
||||
/// This thread's ids as its last full syscall saw them (`ident_call`).
|
||||
static IDS: Cell<gex_kernel::syscalls::Idents> = const {
|
||||
Cell::new(gex_kernel::syscalls::Idents { pid: 0, ppid: 0, tid: 0, uid: 0, gid: 0 })
|
||||
};
|
||||
/// The canonical number of the syscall this thread just made, for
|
||||
/// `wakes_nobody`; `u64::MAX` when the kernel did not run it.
|
||||
static CANON_NR: Cell<u64> = const { Cell::new(u64::MAX) };
|
||||
@@ -7598,7 +7614,9 @@ fn clock_fast_path(ctx: i32) -> bool {
|
||||
}
|
||||
let mut gm = GuestMem::new(guest_bytes());
|
||||
let started = FAST_STARTED.load(Relaxed) as u128;
|
||||
if !gex_kernel::syscalls::clock_call(&mut gm, ctx as u32 as u64, abi == 1, browser_clock, started) {
|
||||
if !gex_kernel::syscalls::clock_call(&mut gm, ctx as u32 as u64, abi == 1, browser_clock, started)
|
||||
&& !IDS.with(|c| gex_kernel::syscalls::ident_call(&mut gm, ctx as u32 as u64, abi == 1, &c.get()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
FAST_RUN.with(|c| c.set(c.get() + 1));
|
||||
|
||||
Reference in new issue
Block a user