shadow_shim/
reinit_auxvec_random.rs

1use core::{ffi::c_ulong, ptr};
2
3use linux_api::auxvec::AuxVecTag;
4
5/// Analogous to libc's `getauxval(3)`, but reads from `/proc/self/auxv` (see
6/// `proc(5)`) instead of using libc.
7fn getauxval(tag: AuxVecTag) -> Option<c_ulong> {
8    let r = rustix::fs::open(
9        "/proc/self/auxv",
10        rustix::fs::OFlags::RDONLY,
11        rustix::fs::Mode::empty(),
12    );
13    let Ok(auxv_file) = r else {
14        log::warn!("Couldn't open /proc/self/auxv: {r:?}");
15        return None;
16    };
17    // The auxv data are (tag, value) pairs of core::ffi::c_ulong.
18    // Experimentally, on my system this is 368 bytes (`wc -c /proc/self/auxv`).
19    // Leave some room for future growth.
20    let mut auxv_data = [0u8; 368 * 2];
21    let r = rustix::io::read(&auxv_file, &mut auxv_data);
22    let Ok(bytes_read) = r else {
23        log::warn!("Couldn't read /proc/self/auxv: {r:?}");
24        return None;
25    };
26    // Intentionally shadow array with a slice of the initd part.
27    let auxv_data = &auxv_data[..bytes_read];
28    // We should have gotten it all in one read, so we should get 0 bytes here.
29    let r = rustix::io::read(auxv_file, &mut [0; 1]);
30    if r != Ok(0) {
31        log::warn!("Expected EOF reading /proc/self/auxv. Instead got: {r:?}");
32        return None;
33    };
34    let mut tag_val_iter = auxv_data
35        .chunks_exact(2 * size_of::<c_ulong>())
36        .map(|chunk| {
37            let tag = c_ulong::from_ne_bytes(chunk[..size_of::<c_ulong>()].try_into().unwrap());
38            let value = c_ulong::from_ne_bytes(chunk[size_of::<c_ulong>()..].try_into().unwrap());
39            (tag, value)
40        });
41    tag_val_iter
42        .find(|(this_tag, _val)| *this_tag == u64::from(tag))
43        .map(|(_tag, value)| value)
44}
45
46/// Returns a pointer to the `AT_RANDOM` data as provided in the auxiliary
47/// vector.  We locate this data via `/proc/self/auxv` (see proc(5)). For more
48/// about this data itself see `getauxval(3)`.
49fn get_auxvec_random() -> *mut [u8; 16] {
50    let Some(val) = getauxval(AuxVecTag::AT_RANDOM) else {
51        log::warn!("Couldn't find AT_RANDOM");
52        return ptr::null_mut();
53    };
54    val as *mut [u8; 16]
55}
56
57/// (Re)initialize the 16 random "`AT_RANDOM`" bytes that the kernel provides
58/// via the auxiliary vector.  See `getauxval(3)`
59///
60/// # Safety
61///
62/// There must be no concurrent access to the `AT_RANDOM` data, including:
63///
64/// * There must be no live rust reference to that data.
65/// * This function must not be called in parallel, e.g. from another thread.
66/// * The data must be writable. (This isn't explicitly guaranteed by the Linux
67///   docs, but seems to be the case).
68/// * Overwriting this process-global value must not violate safety requirements
69///   of other code running in the same address-space, such as they dynamic
70///   linker/loader and other dynamically linked libraries. The best way to ensure
71///   this is to call this before other such code gets a chance to run.
72///
73/// Because this data is a process-wide global initialized by the kernel, code
74/// outside of this library may access it. The above safety requirements likely
75/// apply to that code as well. Additionally, changing this data after some code
76/// has already read it might violate assumptions in that code.
77pub unsafe fn reinit_auxvec_random(data: &[u8; 16]) {
78    let auxv = get_auxvec_random();
79    if auxv.is_null() {
80        log::warn!(
81            "Couldn't find auxvec AT_RANDOM to overwrite. May impact simulation determinism."
82        );
83    } else {
84        unsafe { get_auxvec_random().write(*data) }
85    }
86}
87
88#[cfg(test)]
89mod test {
90    use linux_api::auxvec::AuxVecTag;
91
92    #[test]
93    // Can't call libc::getauxval from miri
94    #[cfg(not(miri))]
95    fn test_getauxvec() {
96        // Test consistency with libc
97        assert_eq!(super::getauxval(AuxVecTag::AT_RANDOM).unwrap(), unsafe {
98            libc::getauxval(libc::AT_RANDOM)
99        });
100        assert_eq!(
101            super::getauxval(AuxVecTag::AT_MINSIGSTKSZ).unwrap(),
102            unsafe { libc::getauxval(libc::AT_MINSIGSTKSZ) }
103        );
104    }
105}