Commit 5c8393d5 authored by Peter Limkilde Svendsen's avatar Peter Limkilde Svendsen Committed by Jeremy Soller
Browse files

Don't assume u32 alignment of random() state buffer

parent 954f010c
......@@ -388,12 +388,12 @@ pub unsafe extern "C" fn initstate(seed: c_uint, state: *mut c_char, size: size_
_ => 63,
};
random::X_PTR = (state as *mut u32).offset(1);
random::X_PTR = (state.cast::<[u8; 4]>()).offset(1);
random::seed(seed);
random::save_state();
// TODO: unlock?
old_state as _
old_state.cast::<_>()
}
}
......@@ -763,12 +763,18 @@ pub unsafe extern "C" fn random() -> c_long {
random::ensure_x_ptr_init();
if random::N == 0 {
*random::X_PTR = random::lcg31_step(*random::X_PTR);
k = *random::X_PTR;
let x_old = u32::from_ne_bytes(*random::X_PTR);
let x_new = random::lcg31_step(x_old);
*random::X_PTR = x_new.to_ne_bytes();
k = x_new;
} else {
*random::X_PTR.add(usize::from(random::I)) += *random::X_PTR.add(usize::from(random::J));
// 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_new = x_i_old.wrapping_add(x_j);
*random::X_PTR.add(usize::from(random::I)) = x_i_new.to_ne_bytes();
k = *random::X_PTR.add(usize::from(random::I)) >> 1;
k = x_i_new >> 1;
random::I += 1;
if random::I == random::N {
......@@ -916,9 +922,9 @@ pub unsafe extern "C" fn setstate(state: *mut c_char) -> *mut c_char {
// TODO: lock?
let old_state = random::save_state();
random::load_state(state as *mut u32);
random::load_state(state.cast::<_>());
// TODO: unlock?
old_state as _
old_state.cast::<_>()
}
#[no_mangle]
......
......@@ -24,14 +24,20 @@ pub static mut N: u8 = 31;
pub static mut I: u8 = 3;
pub static mut J: u8 = 0;
/* Unlike in C, we can't take the address of the initializing array
* outside of a function. */
pub static mut X_PTR: *mut u32 = ptr::null_mut();
/* 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() {
X_PTR = &mut X_INIT[1];
let x_u32_ptr: *mut u32 = &mut X_INIT[1];
X_PTR = x_u32_ptr.cast::<[u8; 4]>();
}
}
......@@ -43,24 +49,26 @@ pub fn lcg64_step(x: u64) -> u64 {
6364136223846793005_u64.wrapping_mul(x).wrapping_add(1_u64)
}
pub unsafe fn save_state() -> *mut u32 {
pub unsafe fn save_state() -> *mut [u8; 4] {
ensure_x_ptr_init();
*X_PTR.offset(-1) = (u32::from(N) << 16) | (u32::from(I) << 8) | u32::from(J);
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)
}
pub unsafe fn load_state(state_ptr: *mut u32) {
let prev_x = *state_ptr;
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);
/* 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((prev_x >> 16) & 0xff).unwrap();
N = u8::try_from((stash_value >> 16) & 0xff).unwrap();
// I and J calculations are straight from musl
I = u8::try_from((prev_x >> 8) & 0xff).unwrap();
J = u8::try_from(prev_x & 0xff).unwrap();
I = u8::try_from((stash_value >> 8) & 0xff).unwrap();
J = u8::try_from(stash_value & 0xff).unwrap();
}
pub unsafe fn seed(seed: c_uint) {
......@@ -69,7 +77,7 @@ pub unsafe fn seed(seed: c_uint) {
let mut s = seed as u64;
if N == 0 {
*X_PTR = s as u32;
*X_PTR = (s as u32).to_ne_bytes();
} else {
I = if N == 31 || N == 7 { 3 } else { 1 };
......@@ -80,10 +88,10 @@ pub unsafe fn seed(seed: c_uint) {
// 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();
*X_PTR.add(k) = u32::try_from(s >> 32).unwrap().to_ne_bytes();
}
// ensure X contains at least one odd number
*X_PTR |= 1;
*X_PTR = (u32::from_ne_bytes(*X_PTR) | 1).to_ne_bytes();
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment