diff --git a/redox-rt/src/proc.rs b/redox-rt/src/proc.rs
index cb84a0688d73b3d480b4d7843cdd76bb07c58fca..edd90eb1c6f3b30bc9ec1dae279001e33481f4ad 100644
--- a/redox-rt/src/proc.rs
+++ b/redox-rt/src/proc.rs
@@ -733,7 +733,10 @@ pub fn fork_inner(initial_rsp: *mut usize) -> Result<usize> {
     let (cur_filetable_fd, new_pid_fd, new_pid);
 
     {
-        let cur_pid_fd = FdGuard::new(syscall::open("/scheme/thisproc/current/open_via_dup", O_CLOEXEC)?);
+        let cur_pid_fd = FdGuard::new(syscall::open(
+            "/scheme/thisproc/current/open_via_dup",
+            O_CLOEXEC,
+        )?);
         (new_pid_fd, new_pid) = new_child_process()?;
 
         copy_str(*cur_pid_fd, *new_pid_fd, "name")?;
@@ -861,7 +864,10 @@ pub fn fork_inner(initial_rsp: *mut usize) -> Result<usize> {
 
 pub fn new_child_process() -> Result<(FdGuard, usize)> {
     // Create a new context (fields such as uid/gid will be inherited from the current context).
-    let fd = FdGuard::new(syscall::open("/scheme/thisproc/new/open_via_dup", O_CLOEXEC)?);
+    let fd = FdGuard::new(syscall::open(
+        "/scheme/thisproc/new/open_via_dup",
+        O_CLOEXEC,
+    )?);
 
     // Extract pid.
     let mut buffer = [0_u8; 64];
diff --git a/src/header/stdlib/mod.rs b/src/header/stdlib/mod.rs
index 25255a8487e4da24d9e8cc5d80b9eeb309fca587..747525e2f05103671540989c7eb0834e8fa8d2d9 100644
--- a/src/header/stdlib/mod.rs
+++ b/src/header/stdlib/mod.rs
@@ -416,16 +416,15 @@ pub extern "C" fn grantpt(fildes: c_int) -> c_int {
     0
 }
 
+// Ported from musl
 #[no_mangle]
 pub unsafe extern "C" fn initstate(seed: c_uint, state: *mut c_char, size: size_t) -> *mut c_char {
-    // Ported from musl
-
     if size < 8 {
         ptr::null_mut()
     } else {
-        // TODO: lock?
-        let old_state = random::save_state();
-        random::N = match size {
+        let mut random_state = random::state_lock();
+        let old_state = random_state.save();
+        random_state.n = match size {
             0..=7 => unreachable!(), // ensured above
             8..=31 => 0,
             32..=63 => 7,
@@ -434,10 +433,9 @@ pub unsafe extern "C" fn initstate(seed: c_uint, state: *mut c_char, size: size_
             _ => 63,
         };
 
-        random::X_PTR = (state.cast::<[u8; 4]>()).offset(1);
-        random::seed(seed);
-        random::save_state();
-        // TODO: unlock?
+        random_state.x_ptr = (state.cast::<[u8; 4]>()).offset(1);
+        random_state.seed(seed);
+        random_state.save();
 
         old_state.cast::<_>()
     }
@@ -905,39 +903,39 @@ pub unsafe extern "C" fn rand_r(seed: *mut c_uint) -> c_int {
     }
 }
 
+// Ported from musl
 #[no_mangle]
 pub unsafe extern "C" fn random() -> c_long {
-    // Ported from musl
+    let mut random_state = random::state_lock();
 
     let k: u32;
-    // TODO: lock?
-    random::ensure_x_ptr_init();
 
-    if random::N == 0 {
-        let x_old = u32::from_ne_bytes(*random::X_PTR);
+    random_state.ensure_x_ptr_init();
+
+    if random_state.n == 0 {
+        let x_old = u32::from_ne_bytes(*random_state.x_ptr);
         let x_new = random::lcg31_step(x_old);
-        *random::X_PTR = x_new.to_ne_bytes();
+        *random_state.x_ptr = x_new.to_ne_bytes();
         k = x_new;
     } else {
         // The non-u32-aligned way of saying x[i] += x[j]...
-        let x_i_old = u32::from_ne_bytes(*random::X_PTR.add(usize::from(random::I)));
-        let x_j = u32::from_ne_bytes(*random::X_PTR.add(usize::from(random::J)));
+        let x_i_old = u32::from_ne_bytes(*random_state.x_ptr.add(usize::from(random_state.i)));
+        let x_j = u32::from_ne_bytes(*random_state.x_ptr.add(usize::from(random_state.j)));
         let x_i_new = x_i_old.wrapping_add(x_j);
-        *random::X_PTR.add(usize::from(random::I)) = x_i_new.to_ne_bytes();
+        *random_state.x_ptr.add(usize::from(random_state.i)) = x_i_new.to_ne_bytes();
 
         k = x_i_new >> 1;
 
-        random::I += 1;
-        if random::I == random::N {
-            random::I = 0;
+        random_state.i += 1;
+        if random_state.i == random_state.n {
+            random_state.i = 0;
         }
 
-        random::J += 1;
-        if random::J == random::N {
-            random::J = 0;
+        random_state.j += 1;
+        if random_state.j == random_state.n {
+            random_state.j = 0;
         }
     }
-    // TODO: unlock?
 
     /* Both branches of this function result in a "u31", which will
      * always fit in a c_long. */
@@ -1064,14 +1062,13 @@ pub unsafe extern "C" fn setkey(key: *const c_char) {
     unimplemented!();
 }
 
+// Ported from musl. The state parameter is no longer const in newer versions of POSIX.
 #[no_mangle]
 pub unsafe extern "C" fn setstate(state: *mut c_char) -> *mut c_char {
-    /* Ported from musl. The state parameter is no longer const in newer
-     * versions of POSIX. */
+    let mut random_state = random::state_lock();
 
-    // TODO: lock?
-    let old_state = random::save_state();
-    random::load_state(state.cast::<_>());
+    let old_state = random_state.save();
+    random_state.load(state.cast::<_>());
     // TODO: unlock?
     old_state.cast::<_>()
 }
@@ -1095,13 +1092,12 @@ pub extern "C" fn srand48(seedval: c_long) {
         .unwrap();
 }
 
+// Ported from musl
 #[no_mangle]
 pub unsafe extern "C" fn srandom(seed: c_uint) {
-    // Ported from musl
+    let mut random_state = random::state_lock();
 
-    // TODO: lock?
-    random::seed(seed);
-    // TODO: unlock?
+    random_state.seed(seed);
 }
 
 #[no_mangle]
diff --git a/src/header/stdlib/random.rs b/src/header/stdlib/random.rs
index f08155924ea6993478352a69833d46f14e4b374b..85c22b2e661df75789b61b8c81335bc0163e46ba 100644
--- a/src/header/stdlib/random.rs
+++ b/src/header/stdlib/random.rs
@@ -1,97 +1,117 @@
 //! Helper functions for random() and friends, see https://pubs.opengroup.org/onlinepubs/7908799/xsh/initstate.html
-/* Ported from musl's implementation (src/prng/random.c). Does not
- * currently implement locking, though. */
+// Ported from musl's implementation (src/prng/random.c)
 
-use crate::platform::types::*;
+use crate::{
+    platform::types::*,
+    sync::{Mutex, MutexGuard},
+};
 use core::{convert::TryFrom, ptr};
 
-#[rustfmt::skip]
-static mut X_INIT: [u32; 32] = [
-    0x00000000, 0x5851f42d, 0xc0b18ccf, 0xcbb5f646,
-    0xc7033129, 0x30705b04, 0x20fd5db4, 0x9a8b7f78,
-    0x502959d8, 0xab894868, 0x6c0356a7, 0x88cdb7ff,
-    0xb477d43f, 0x70a3a52b, 0xa8e4baf1, 0xfd8341fc,
-    0x8ae16fd9, 0x742d2f7a, 0x0d1f0796, 0x76035e09,
-    0x40f7702c, 0x6fa72ca5, 0xaaa84157, 0x58a0df74,
-    0xc74a0364, 0xae533cc4, 0x04185faf, 0x6de3b115,
-    0x0cab8628, 0xf043bfa4, 0x398150e9, 0x37521657,
-];
-
-/* N needs to accommodate values up to 63, corresponding to the maximum
- * state array size of 256 bytes. I and J must be able to accommodate
- * values less than or equal to N. */
-pub static mut N: u8 = 31;
-pub static mut I: u8 = 3;
-pub static mut J: u8 = 0;
-
-/* As such, random() and related functions work on u32 values, but POSIX
- * allows the user to supply a custom state data array as a `char *`
- * with no requirements on alignment. Thus, we must assume the worst in
- * terms of alignment and convert back and forth from [u8; 4].
- *
- * Also, unlike in C, we can't take the address of the initializing
- * array outside of a function. */
-pub static mut X_PTR: *mut [u8; 4] = ptr::null_mut();
-
-// To be called in any function that may read from X_PTR
-pub unsafe fn ensure_x_ptr_init() {
-    if X_PTR.is_null() {
-        let x_u32_ptr: *mut u32 = &mut X_INIT[1];
-        X_PTR = x_u32_ptr.cast::<[u8; 4]>();
-    }
+pub struct State {
+    pub x_init: [u32; 32],
+    pub x_ptr: *mut [u8; 4],
+    pub n: u8,
+    pub i: u8,
+    pub j: u8,
 }
 
-pub fn lcg31_step(x: u32) -> u32 {
-    1103515245_u32.wrapping_mul(x).wrapping_add(12345_u32) & 0x7fffffff
-}
+// Necessary because raw pointers are not Send
+unsafe impl Send for State {}
 
-pub fn lcg64_step(x: u64) -> u64 {
-    6364136223846793005_u64.wrapping_mul(x).wrapping_add(1_u64)
-}
+impl State {
+    /// To be called in any function that may read from X_PTR
+    pub fn ensure_x_ptr_init(&mut self) {
+        if self.x_ptr.is_null() {
+            let x_u32_ptr: *mut u32 = &mut self.x_init[1];
+            self.x_ptr = x_u32_ptr.cast::<[u8; 4]>();
+        }
+    }
 
-pub unsafe fn save_state() -> *mut [u8; 4] {
-    ensure_x_ptr_init();
+    pub unsafe fn save(&mut self) -> *mut [u8; 4] {
+        self.ensure_x_ptr_init();
 
-    let stash_value: u32 = (u32::from(N) << 16) | (u32::from(I) << 8) | u32::from(J);
-    *X_PTR.offset(-1) = stash_value.to_ne_bytes();
-    X_PTR.offset(-1)
-}
+        let stash_value: u32 =
+            (u32::from(self.n) << 16) | (u32::from(self.i) << 8) | u32::from(self.j);
+        *self.x_ptr.offset(-1) = stash_value.to_ne_bytes();
+        self.x_ptr.offset(-1)
+    }
 
-pub unsafe fn load_state(state_ptr: *mut [u8; 4]) {
-    let stash_value = u32::from_ne_bytes(*state_ptr);
-    X_PTR = state_ptr.offset(1);
+    pub unsafe fn load(&mut self, state_ptr: *mut [u8; 4]) {
+        let stash_value = u32::from_ne_bytes(*state_ptr);
+        self.x_ptr = state_ptr.offset(1);
 
-    /* This calculation of N does not have a bit mask in the musl
-     * original, in principle resulting in a u16, but obtaining a value
-     * larger than 63 can probably be dismissed as pathological. */
-    N = u8::try_from((stash_value >> 16) & 0xff).unwrap();
+        /* This calculation of n does not have a bit mask in the musl
+         * original, in principle resulting in a u16, but obtaining a value
+         * larger than 63 can probably be dismissed as pathological. */
+        self.n = u8::try_from((stash_value >> 16) & 0xff).unwrap();
 
-    // I and J calculations are straight from musl
-    I = u8::try_from((stash_value >> 8) & 0xff).unwrap();
-    J = u8::try_from(stash_value & 0xff).unwrap();
-}
+        // i and j calculations are straight from musl
+        self.i = u8::try_from((stash_value >> 8) & 0xff).unwrap();
+        self.j = u8::try_from(stash_value & 0xff).unwrap();
+    }
 
-pub unsafe fn seed(seed: c_uint) {
-    ensure_x_ptr_init();
+    pub unsafe fn seed(&mut self, seed: c_uint) {
+        self.ensure_x_ptr_init();
 
-    let mut s = seed as u64;
+        let mut s = seed as u64;
 
-    if N == 0 {
-        *X_PTR = (s as u32).to_ne_bytes();
-    } else {
-        I = if N == 31 || N == 7 { 3 } else { 1 };
+        if self.n == 0 {
+            *self.x_ptr = (s as u32).to_ne_bytes();
+        } else {
+            self.i = if self.n == 31 || self.n == 7 { 3 } else { 1 };
 
-        J = 0;
+            self.j = 0;
 
-        for k in 0..usize::from(N) {
-            s = lcg64_step(s);
+            for k in 0..usize::from(self.n) {
+                s = lcg64_step(s);
 
-            // Conversion will always succeed (value is a 32-bit right-
-            // shift of a 64-bit integer).
-            *X_PTR.add(k) = u32::try_from(s >> 32).unwrap().to_ne_bytes();
-        }
+                // Conversion will always succeed (value is a 32-bit right-
+                // shift of a 64-bit integer).
+                *self.x_ptr.add(k) = u32::try_from(s >> 32).unwrap().to_ne_bytes();
+            }
 
-        // ensure X contains at least one odd number
-        *X_PTR = (u32::from_ne_bytes(*X_PTR) | 1).to_ne_bytes();
+            // ensure X contains at least one odd number
+            *self.x_ptr = (u32::from_ne_bytes(*self.x_ptr) | 1).to_ne_bytes();
+        }
     }
 }
+
+pub fn state_lock<'a>() -> MutexGuard<'a, State> {
+    static STATE: Mutex<State> = Mutex::new(State {
+        #[rustfmt::skip]
+        x_init: [
+            0x00000000, 0x5851f42d, 0xc0b18ccf, 0xcbb5f646,
+            0xc7033129, 0x30705b04, 0x20fd5db4, 0x9a8b7f78,
+            0x502959d8, 0xab894868, 0x6c0356a7, 0x88cdb7ff,
+            0xb477d43f, 0x70a3a52b, 0xa8e4baf1, 0xfd8341fc,
+            0x8ae16fd9, 0x742d2f7a, 0x0d1f0796, 0x76035e09,
+            0x40f7702c, 0x6fa72ca5, 0xaaa84157, 0x58a0df74,
+            0xc74a0364, 0xae533cc4, 0x04185faf, 0x6de3b115,
+            0x0cab8628, 0xf043bfa4, 0x398150e9, 0x37521657,
+        ],
+        /* As such, random() and related functions work on u32 values, but POSIX
+         * allows the user to supply a custom state data array as a `char *`
+         * with no requirements on alignment. Thus, we must assume the worst in
+         * terms of alignment and convert back and forth from [u8; 4].
+         *
+         * Also, unlike in C, we can't take the address of the initializing
+         * array outside of a function. */
+        x_ptr: ptr::null_mut(),
+        /* N needs to accommodate values up to 63, corresponding to the maximum
+         * state array size of 256 bytes. I and J must be able to accommodate
+         * values less than or equal to N. */
+        n: 31,
+        i: 3,
+        j: 0,
+    });
+
+    STATE.try_lock().expect("unable to acquire PRNG lock")
+}
+
+pub fn lcg31_step(x: u32) -> u32 {
+    1103515245_u32.wrapping_mul(x).wrapping_add(12345_u32) & 0x7fffffff
+}
+
+pub fn lcg64_step(x: u64) -> u64 {
+    6364136223846793005_u64.wrapping_mul(x).wrapping_add(1_u64)
+}
diff --git a/src/platform/redox/exec.rs b/src/platform/redox/exec.rs
index 81ac0de70f9ec5cfd1547aad0c8d2aa66fb3be4d..edc7304d37045e1eb0119f624cfaaf5d8bedb465 100644
--- a/src/platform/redox/exec.rs
+++ b/src/platform/redox/exec.rs
@@ -252,7 +252,8 @@ pub fn execve(
         // scenarios. While execve() is undefined according to POSIX if there exist sibling
         // threads, it could still be allowed by keeping certain file descriptors and instead
         // set the active file table.
-        let files_fd = File::new(syscall::open("/scheme/thisproc/current/filetable", O_RDONLY)? as c_int);
+        let files_fd =
+            File::new(syscall::open("/scheme/thisproc/current/filetable", O_RDONLY)? as c_int);
         for line in BufReader::new(files_fd).lines() {
             let line = match line {
                 Ok(l) => l,
diff --git a/src/platform/redox/ptrace.rs b/src/platform/redox/ptrace.rs
index a5c4f84b8819e2bfff51940dfc7c9a14fe5fcc47..e35f24b7e1dc44df74ecd1ad57e2b8467649a086 100644
--- a/src/platform/redox/ptrace.rs
+++ b/src/platform/redox/ptrace.rs
@@ -82,11 +82,15 @@ pub fn get_session(
                         NEW_FLAGS,
                     )?,
                     regs: File::open(
-                        CStr::borrow(&CString::new(format!("/scheme/proc/{}/regs/int", pid)).unwrap()),
+                        CStr::borrow(
+                            &CString::new(format!("/scheme/proc/{}/regs/int", pid)).unwrap(),
+                        ),
                         NEW_FLAGS,
                     )?,
                     fpregs: File::open(
-                        CStr::borrow(&CString::new(format!("/scheme/proc/{}/regs/float", pid)).unwrap()),
+                        CStr::borrow(
+                            &CString::new(format!("/scheme/proc/{}/regs/float", pid)).unwrap(),
+                        ),
                         NEW_FLAGS,
                     )?,
                 }))
@@ -134,7 +138,9 @@ fn inner_ptrace(
         // Mark this child as traced, parent will check for this marker file
         let pid = Sys::getpid();
         mem::forget(File::open(
-            CStr::borrow(&CString::new(format!("/scheme/chan/ptrace-relibc/{}/traceme", pid)).unwrap()),
+            CStr::borrow(
+                &CString::new(format!("/scheme/chan/ptrace-relibc/{}/traceme", pid)).unwrap(),
+            ),
             fcntl::O_CREAT | fcntl::O_PATH | fcntl::O_EXCL,
         )?);
         return Ok(0);