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}