From 37dbf5cbb2a3c8bd248e3e8c613f4ffc06b1f441 Mon Sep 17 00:00:00 2001 From: Anhad Singh Date: Mon, 25 Nov 2024 16:09:06 +1100 Subject: [PATCH] fix(linker): deref uninit TCB The bug is described for `x86_64` and Linux but is the same for other architectures and on Redox. `Tcb::current()` is used to retrieve the current TCB, which is done by by reading `fs:[0x10]`. The TCB layout describes that at offset `0x10`, there is `tcb_ptr: *mut GenericTcb<...>`, which is nothing more but a pointer to itself. This is fine as otherwise a system call would be required to get the TCB (`arch_prctl(ARCH_GET_FS)` on Linux). However, this is problematic as the function may be called when the FS base is not set, and in that case the expected output of the function should be [`None`], but we don't currently handle that. To fix this, any code paths that maybe call this function on an uninitialized TCB are be switched to call `current_slow()`. Which just uses `arch_prctl(ARCH_GET_FS)` to get the FS base and check if it's non-zero. Signed-off-by: Anhad Singh --- src/header/dlfcn/mod.rs | 6 +++--- src/ld_so/linker.rs | 4 ++-- src/ld_so/tcb.rs | 40 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 5 deletions(-) diff --git a/src/header/dlfcn/mod.rs b/src/header/dlfcn/mod.rs index b592e7efe4..54d8498bdf 100644 --- a/src/header/dlfcn/mod.rs +++ b/src/header/dlfcn/mod.rs @@ -47,7 +47,7 @@ pub unsafe extern "C" fn dlopen(cfilename: *const c_char, flags: c_int) -> *mut )) }; - let tcb = match Tcb::current() { + let tcb = match Tcb::current_slow() { Some(tcb) => tcb, None => { ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst); @@ -83,7 +83,7 @@ pub unsafe extern "C" fn dlsym(handle: *mut c_void, symbol: *const c_char) -> *m let symbol_str = str::from_utf8_unchecked(CStr::from_ptr(symbol).to_bytes()); - let tcb = match Tcb::current() { + let tcb = match Tcb::current_slow() { Some(tcb) => tcb, None => { ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst); @@ -110,7 +110,7 @@ pub unsafe extern "C" fn dlsym(handle: *mut c_void, symbol: *const c_char) -> *m #[no_mangle] pub unsafe extern "C" fn dlclose(handle: *mut c_void) -> c_int { - let tcb = match Tcb::current() { + let tcb = match Tcb::current_slow() { Some(tcb) => tcb, None => { ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst); diff --git a/src/ld_so/linker.rs b/src/ld_so/linker.rs index 5bdccabe42..b8cfa30b05 100644 --- a/src/ld_so/linker.rs +++ b/src/ld_so/linker.rs @@ -167,7 +167,7 @@ impl Linker { )?; unsafe { - let tcb = match Tcb::current() { + let tcb = match Tcb::current_slow() { Some(some) => some, None => Tcb::new(self.tls_size)?, }; @@ -230,7 +230,7 @@ impl Linker { if self.next_tls_module_id == 1 { // Hack to allocate TCB on the first TLS module unsafe { - if Tcb::current().is_none() { + if Tcb::current_slow().is_none() { let tcb = Tcb::new(master.offset).expect_notls("failed to allocate TCB"); tcb.activate(); } diff --git a/src/ld_so/tcb.rs b/src/ld_so/tcb.rs index 1e05657660..27939e642f 100644 --- a/src/ld_so/tcb.rs +++ b/src/ld_so/tcb.rs @@ -112,6 +112,46 @@ impl Tcb { Some(&mut *GenericTcb::::current_ptr()?.cast()) } + // FIXME(andypython): move to platform/ + pub unsafe fn current_slow() -> Option<&'static mut Self> { + #[cfg(all(target_os = "linux", target_arch = "x86_64"))] + { + use sc::nr::ARCH_PRCTL; + const ARCH_GET_FS: usize = 0x1003; + + let mut fs_base = 0usize; + sc::syscall!(ARCH_PRCTL, ARCH_GET_FS, &mut fs_base as *mut usize); + + if fs_base == 0 { + None + } else { + Some(&mut *(fs_base as *mut Self)) + } + } + + #[cfg(all(target_os = "redox", target_arch = "x86_64"))] + { + let mut env = syscall::EnvRegisters::default(); + + let file = syscall::open( + "/scheme/thisproc/current/regs/env", + syscall::O_CLOEXEC | syscall::O_RDONLY, + ) + .expect_notls("failed to open handle for process registers"); + + let _ = syscall::read(file, &mut env).expect_notls("failed to read fsbase"); + let _ = syscall::close(file); + + if env.fsbase == 0 { + return None; + } else { + return Some(&mut *(env.fsbase as *mut Self)); + } + } + + // FIXME(andypython): Implement and test on other platforms + } + /// A slice for all of the TLS data pub unsafe fn tls(&self) -> Option<&'static mut [u8]> { if self.tls_end.is_null() || self.tls_len == 0 {