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 <andypython@protonmail.com>
This commit is contained in:
@@ -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);
|
||||
|
||||
+2
-2
@@ -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();
|
||||
}
|
||||
|
||||
@@ -112,6 +112,46 @@ impl Tcb {
|
||||
Some(&mut *GenericTcb::<OsSpecific>::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 {
|
||||
|
||||
Reference in New Issue
Block a user