From a388b1ae4d8cfab5b37e030c53b33e60a6576ecb Mon Sep 17 00:00:00 2001
From: 4lDO2 <4lDO2@protonmail.com>
Date: Sun, 1 Aug 2021 11:05:23 +0200
Subject: [PATCH] Initialize TLS manually on Redox too.

---
 Cargo.lock          | 12 ++++++--
 Cargo.toml          |  2 +-
 src/ld_so/mod.rs    | 75 +++++++++++++++++++++++++++++++++++++--------
 src/ld_so/tcb.rs    | 48 +++++++++++++----------------
 src/lib.rs          |  1 +
 src/platform/pte.rs | 13 ++++----
 6 files changed, 103 insertions(+), 48 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index bb40e2cab..7323874ee 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -351,6 +351,14 @@ dependencies = [
  "bitflags",
 ]
 
+[[package]]
+name = "redox_syscall"
+version = "0.2.9"
+source = "git+https://gitlab.redox-os.org/4lDO2/syscall.git?branch=fsgsbase#519a09e96400309a14375c04815da00f7cf5f526"
+dependencies = [
+ "bitflags",
+]
+
 [[package]]
 name = "relibc"
 version = "0.2.5"
@@ -366,7 +374,7 @@ dependencies = [
  "posix-regex",
  "ralloc",
  "rand 0.5.6",
- "redox_syscall 0.2.9",
+ "redox_syscall 0.2.9 (git+https://gitlab.redox-os.org/4lDO2/syscall.git?branch=fsgsbase)",
  "sc",
  "spin 0.9.2",
 ]
@@ -541,7 +549,7 @@ dependencies = [
  "cfg-if",
  "libc",
  "rand 0.8.4",
- "redox_syscall 0.2.9",
+ "redox_syscall 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)",
  "remove_dir_all",
  "winapi",
 ]
diff --git a/Cargo.toml b/Cargo.toml
index 24c5e591f..182cbfd5e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -39,7 +39,7 @@ optional = true
 sc = "0.2.3"
 
 [target.'cfg(target_os = "redox")'.dependencies]
-redox_syscall = "0.2.9"
+redox_syscall = { git = "https://gitlab.redox-os.org/4lDO2/syscall.git", branch = "fsgsbase" }
 spin = "0.9.0"
 
 [features]
diff --git a/src/ld_so/mod.rs b/src/ld_so/mod.rs
index 9ee7ea532..9ea90885c 100644
--- a/src/ld_so/mod.rs
+++ b/src/ld_so/mod.rs
@@ -28,6 +28,41 @@ static mut STATIC_TCB_MASTER: Master = Master {
     offset: 0,
 };
 
+fn panic_notls(msg: impl core::fmt::Display) -> ! {
+    eprintln!("panicked in ld.so: {}", msg);
+
+    unsafe {
+        core::intrinsics::abort();
+    }
+}
+
+pub trait ExpectTlsFree {
+    type Unwrapped;
+
+    fn expect_notls(self, msg: &str) -> Self::Unwrapped;
+}
+impl<T, E: core::fmt::Debug> ExpectTlsFree for Result<T, E> {
+    type Unwrapped = T;
+
+    fn expect_notls(self, msg: &str) -> T {
+        match self {
+            Ok(t) => t,
+            Err(err) => panic_notls(format_args!("expect failed for Result with err: {:?}", err)),
+        }
+    }
+}
+impl<T> ExpectTlsFree for Option<T> {
+    type Unwrapped = T;
+
+    fn expect_notls(self, msg: &str) -> T {
+        match self {
+            Some(t) => t,
+            None => panic_notls("expect failed for Option"),
+        }
+    }
+}
+
+#[inline(never)]
 pub fn static_init(sp: &'static Stack) {
     let mut phdr_opt = None;
     let mut phent_opt = None;
@@ -50,9 +85,9 @@ pub fn static_init(sp: &'static Stack) {
         auxv = unsafe { auxv.add(1) };
     }
 
-    let phdr = phdr_opt.expect("failed to find AT_PHDR");
-    let phent = phent_opt.expect("failed to find AT_PHENT");
-    let phnum = phnum_opt.expect("failed to find AT_PHNUM");
+    let phdr = phdr_opt.expect_notls("failed to find AT_PHDR");
+    let phent = phent_opt.expect_notls("failed to find AT_PHENT");
+    let phnum = phnum_opt.expect_notls("failed to find AT_PHNUM");
 
     for i in 0..phnum {
         let ph_addr = phdr + phent * i;
@@ -63,7 +98,7 @@ pub fn static_init(sp: &'static Stack) {
             program_header64::SIZEOF_PHDR => {
                 unsafe { *(ph_addr as *const program_header64::ProgramHeader) }.into()
             }
-            _ => panic!("unknown AT_PHENT size {}", phent),
+            _ => panic_notls(format_args!("unknown AT_PHENT size {}", phent)),
         };
 
         let page_size = Sys::getpagesize();
@@ -84,10 +119,10 @@ pub fn static_init(sp: &'static Stack) {
                     STATIC_TCB_MASTER.len = ph.p_filesz as usize;
                     STATIC_TCB_MASTER.offset = valign;
 
-                    let tcb = Tcb::new(vsize).expect("failed to allocate TCB");
+                    let tcb = Tcb::new(vsize).expect_notls("failed to allocate TCB");
                     tcb.masters_ptr = &mut STATIC_TCB_MASTER;
                     tcb.masters_len = mem::size_of::<Master>();
-                    tcb.copy_masters().expect("failed to copy TLS master data");
+                    tcb.copy_masters().expect_notls("failed to copy TLS master data");
                     tcb.activate();
                 }
 
@@ -99,19 +134,35 @@ pub fn static_init(sp: &'static Stack) {
     }
 }
 
-#[cfg(target_os = "linux")]
+#[cfg(any(target_os = "linux", target_os = "redox"))]
 pub unsafe fn init(sp: &'static Stack) {
     let mut tp = 0usize;
-    const ARCH_GET_FS: usize = 0x1003;
-    syscall!(ARCH_PRCTL, ARCH_GET_FS, &mut tp as *mut usize);
+
+    #[cfg(target_os = "linux")]
+    {
+        const ARCH_GET_FS: usize = 0x1003;
+        syscall!(ARCH_PRCTL, ARCH_GET_FS, &mut tp as *mut usize);
+    }
+    #[cfg(target_os = "redox")]
+    {
+        let mut env = syscall::EnvRegisters::default();
+
+        let file = syscall::open("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);
+
+        tp = env.fsbase as usize;
+    }
+
     if tp == 0 {
         static_init(sp);
     }
 }
 
-#[cfg(target_os = "redox")]
-pub unsafe fn init(_sp: &'static Stack) {}
-
 pub unsafe fn fini() {
     if let Some(tcb) = Tcb::current() {
         if tcb.linker_ptr != ptr::null_mut() {
diff --git a/src/ld_so/tcb.rs b/src/ld_so/tcb.rs
index e9133c849..620c10a9a 100644
--- a/src/ld_so/tcb.rs
+++ b/src/ld_so/tcb.rs
@@ -4,7 +4,7 @@ use goblin::error::{Error, Result};
 
 use crate::{
     header::sys_mman,
-    ld_so::linker::Linker,
+    ld_so::{linker::Linker, ExpectTlsFree},
     platform::{Pal, Sys},
     sync::mutex::Mutex,
 };
@@ -186,30 +186,14 @@ impl Tcb {
         Ok(slice::from_raw_parts_mut(ptr as *mut u8, size))
     }
 
-    /// OS specific code to create a new TLS and TCB - Linux
-    #[cfg(target_os = "linux")]
+    /// OS specific code to create a new TLS and TCB - Linux and Redox
+    #[cfg(any(target_os = "linux", target_os = "redox"))]
     unsafe fn os_new(size: usize) -> Result<(&'static mut [u8], &'static mut [u8])> {
         let page_size = Sys::getpagesize();
         let tls_tcb = Self::map(size + page_size)?;
         Ok(tls_tcb.split_at_mut(size))
     }
 
-    /// OS specific code to create a new TLS and TCB - Redox
-    #[cfg(target_os = "redox")]
-    unsafe fn os_new(size: usize) -> Result<(&'static mut [u8], &'static mut [u8])> {
-        use crate::header::unistd;
-        //TODO: better method of finding fs offset
-        let pid = unistd::getpid();
-        let page_size = Sys::getpagesize();
-        let tcb_addr = 0xB000_0000 + pid as usize * page_size;
-        let tls = Self::map(size)?;
-        Ok((
-            tls,
-            //TODO: Consider allocating TCB as part of TLS
-            slice::from_raw_parts_mut(tcb_addr as *mut u8, page_size),
-        ))
-    }
-
     /// Architecture specific code to read a usize from the TCB - x86_64
     #[inline(always)]
     #[cfg(target_arch = "aarch64")]
@@ -230,13 +214,12 @@ impl Tcb {
     #[cfg(target_arch = "x86_64")]
     unsafe fn arch_read(offset: usize) -> usize {
         let value;
-        llvm_asm!("
-            mov rax, fs:[rdi]
+        asm!(
             "
-            : "={rax}"(value)
-            : "{rdi}"(offset)
-            :
-            : "intel"
+            mov {}, fs:[{}]
+            ",
+            out(reg) value,
+            in(reg) offset,
         );
         value
     }
@@ -257,7 +240,20 @@ impl Tcb {
     /// OS and architecture specific code to activate TLS - Redox x86_64
     #[cfg(all(target_os = "redox", target_arch = "x86_64"))]
     unsafe fn os_arch_activate(tp: usize) {
-        //TODO: Consider setting FS offset to TCB pointer
+        let mut env = syscall::EnvRegisters::default();
+
+        let file = syscall::open("thisproc:current/regs/env", syscall::O_CLOEXEC | syscall::O_RDWR)
+            .expect_notls("failed to open handle for process registers");
+
+        let _ = syscall::read(file, &mut env)
+            .expect_notls("failed to read fsbase");
+
+        env.fsbase = tp as u64;
+
+        let _ = syscall::write(file, &env)
+            .expect_notls("failed to write fsbase");
+
+        let _ = syscall::close(file);
     }
 }
 
diff --git a/src/lib.rs b/src/lib.rs
index 9adc47796..42560052e 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -6,6 +6,7 @@
 #![feature(asm)]
 #![feature(box_into_pin)]
 #![feature(c_variadic)]
+#![feature(const_btree_new)]
 #![feature(const_raw_ptr_deref)]
 #![feature(core_intrinsics)]
 #![feature(global_asm)]
diff --git a/src/platform/pte.rs b/src/platform/pte.rs
index d69d9f82a..a7f2d8623 100644
--- a/src/platform/pte.rs
+++ b/src/platform/pte.rs
@@ -2,6 +2,7 @@
 
 use alloc::{boxed::Box, collections::BTreeMap};
 use core::{
+    cell::UnsafeCell,
     intrinsics, ptr,
     sync::atomic::{AtomicU32, Ordering},
 };
@@ -45,16 +46,14 @@ static mut pid_mutexes_lock: Mutex<()> = Mutex::new(());
 static mut pid_stacks: Option<BTreeMap<pte_osThreadHandle, (*mut c_void, size_t)>> = None;
 static mut pid_stacks_lock: Mutex<()> = Mutex::new(());
 
+// TODO: VecMap/SLOB (speed) / radix tree (speed while allowing randomization for security).
 #[thread_local]
-static mut LOCALS: *mut BTreeMap<c_uint, *mut c_void> = ptr::null_mut();
+static LOCALS: UnsafeCell<BTreeMap<c_uint, *mut c_void>> = UnsafeCell::new(BTreeMap::new());
 
 static NEXT_KEY: AtomicU32 = AtomicU32::new(0);
 
-unsafe fn locals() -> &'static mut BTreeMap<c_uint, *mut c_void> {
-    if LOCALS.is_null() {
-        LOCALS = Box::into_raw(Box::new(BTreeMap::new()));
-    }
-    &mut *LOCALS
+unsafe fn locals<'a>() -> &'a mut BTreeMap<c_uint, *mut c_void> {
+    &mut *LOCALS.get()
 }
 
 // pte_osResult pte_osInit(void)
@@ -403,7 +402,7 @@ pub unsafe extern "C" fn pte_osTlsGetValue(index: c_uint) -> *mut c_void {
 
 #[no_mangle]
 pub unsafe extern "C" fn pte_osTlsAlloc(pKey: *mut c_uint) -> pte_osResult {
-    *pKey = NEXT_KEY.fetch_add(1, Ordering::SeqCst);
+    *pKey = NEXT_KEY.fetch_add(1, Ordering::Relaxed);
     PTE_OS_OK
 }
 
-- 
GitLab