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        let tags = [
98            (AuxVecTag::AT_NULL, libc::AT_NULL),
99            (AuxVecTag::AT_IGNORE, libc::AT_IGNORE),
100            (AuxVecTag::AT_EXECFD, libc::AT_EXECFD),
101            (AuxVecTag::AT_PHDR, libc::AT_PHDR),
102            (AuxVecTag::AT_PHENT, libc::AT_PHENT),
103            (AuxVecTag::AT_PHNUM, libc::AT_PHNUM),
104            (AuxVecTag::AT_PAGESZ, libc::AT_PAGESZ),
105            (AuxVecTag::AT_BASE, libc::AT_BASE),
106            (AuxVecTag::AT_FLAGS, libc::AT_FLAGS),
107            (AuxVecTag::AT_ENTRY, libc::AT_ENTRY),
108            (AuxVecTag::AT_NOTELF, libc::AT_NOTELF),
109            (AuxVecTag::AT_UID, libc::AT_UID),
110            (AuxVecTag::AT_EUID, libc::AT_EUID),
111            (AuxVecTag::AT_GID, libc::AT_GID),
112            (AuxVecTag::AT_EGID, libc::AT_EGID),
113            (AuxVecTag::AT_PLATFORM, libc::AT_PLATFORM),
114            // glibc appears to get this elsewhere, *and* experimentally
115            // these values don't match.
116            // <https://github.com/bminor/glibc/blob/12a497c716f0a06be5946cabb8c3ec22a079771e/misc/getauxval.c#L32>
117            // (AuxVecTag::AT_HWCAP, libc::AT_HWCAP),
118            (AuxVecTag::AT_CLKTCK, libc::AT_CLKTCK),
119            (AuxVecTag::AT_SECURE, libc::AT_SECURE),
120            (AuxVecTag::AT_BASE_PLATFORM, libc::AT_BASE_PLATFORM),
121            (AuxVecTag::AT_RANDOM, libc::AT_RANDOM),
122            // glibc handles AT_HWCAP2 specially. Experimentally it seems to
123            // ultimately return the same value, but unclear whether that'll
124            // always be the case.
125            // <https://github.com/bminor/glibc/blob/12a497c716f0a06be5946cabb8c3ec22a079771e/misc/getauxval.c#L37>
126            // (AuxVecTag::AT_HWCAP2, libc::AT_HWCAP2),
127
128            // These constants don't exist in the libc crate:
129            /*
130            (AuxVecTag::AT_RSEQ_FEATURE_SIZE, libc::AT_RSEQ_FEATURE_SIZE),
131            (AuxVecTag::AT_RSEQ_ALIGN, libc::AT_RSEQ_ALIGN),
132            (AuxVecTag::AT_HWCAP3, libc::AT_HWCAP3),
133            (AuxVecTag::AT_HWCAP4, libc::AT_HWCAP4),
134            */
135            (AuxVecTag::AT_EXECFN, libc::AT_EXECFN),
136            (AuxVecTag::AT_MINSIGSTKSZ, libc::AT_MINSIGSTKSZ),
137        ];
138        for (linux_tag, libc_tag) in tags {
139            // Ensure errno *isn't* set to ENOENT so that we can distinguish
140            // between libc returning value 0 and ENOENT.
141            unsafe { *libc::__errno_location() = libc::ENOENT + 1 };
142
143            let linux_res = super::getauxval(linux_tag);
144            let libc_res = unsafe { libc::getauxval(libc_tag) };
145            let libc_errno = unsafe { *libc::__errno_location() };
146            println!("{linux_tag:?}: {linux_res:?}");
147            match linux_res {
148                Some(v) => {
149                    assert_eq!(v, libc_res, "for {linux_tag:?}")
150                }
151                None => {
152                    assert_eq!(libc_res, 0, "for {linux_tag:?}");
153                    assert_eq!(libc_errno, libc::ENOENT, "for {linux_tag:?}");
154                }
155            }
156        }
157    }
158}