rustix/backend/linux_raw/param/
auxv.rs

1//! Linux auxv support.
2//!
3//! # Safety
4//!
5//! This uses raw pointers to locate and read the kernel-provided auxv array.
6#![allow(unsafe_code)]
7
8use super::super::conv::{c_int, pass_usize, ret_usize};
9use crate::backend::c;
10use crate::fd::OwnedFd;
11#[cfg(feature = "param")]
12use crate::ffi::CStr;
13use crate::fs::{Mode, OFlags};
14use crate::utils::{as_ptr, check_raw_pointer};
15#[cfg(feature = "alloc")]
16use alloc::vec::Vec;
17use core::mem::size_of;
18use core::ptr::{null_mut, read_unaligned, NonNull};
19#[cfg(feature = "runtime")]
20use core::sync::atomic::AtomicU8;
21use core::sync::atomic::Ordering::Relaxed;
22use core::sync::atomic::{AtomicPtr, AtomicUsize};
23use linux_raw_sys::elf::*;
24use linux_raw_sys::general::{
25    AT_CLKTCK, AT_EXECFN, AT_HWCAP, AT_HWCAP2, AT_MINSIGSTKSZ, AT_NULL, AT_PAGESZ, AT_SYSINFO_EHDR,
26};
27#[cfg(feature = "runtime")]
28use linux_raw_sys::general::{
29    AT_EGID, AT_ENTRY, AT_EUID, AT_GID, AT_PHDR, AT_PHENT, AT_PHNUM, AT_RANDOM, AT_SECURE, AT_UID,
30};
31#[cfg(feature = "alloc")]
32use {alloc::borrow::Cow, alloc::vec};
33
34#[cfg(feature = "param")]
35#[inline]
36pub(crate) fn page_size() -> usize {
37    let mut page_size = PAGE_SIZE.load(Relaxed);
38
39    if page_size == 0 {
40        init_auxv();
41        page_size = PAGE_SIZE.load(Relaxed);
42    }
43
44    page_size
45}
46
47#[cfg(feature = "param")]
48#[inline]
49pub(crate) fn clock_ticks_per_second() -> u64 {
50    let mut ticks = CLOCK_TICKS_PER_SECOND.load(Relaxed);
51
52    if ticks == 0 {
53        init_auxv();
54        ticks = CLOCK_TICKS_PER_SECOND.load(Relaxed);
55    }
56
57    ticks as u64
58}
59
60#[cfg(feature = "param")]
61#[inline]
62pub(crate) fn linux_hwcap() -> (usize, usize) {
63    let mut hwcap = HWCAP.load(Relaxed);
64    let mut hwcap2 = HWCAP2.load(Relaxed);
65
66    if hwcap == 0 || hwcap2 == 0 {
67        init_auxv();
68        hwcap = HWCAP.load(Relaxed);
69        hwcap2 = HWCAP2.load(Relaxed);
70    }
71
72    (hwcap, hwcap2)
73}
74
75#[cfg(feature = "param")]
76#[inline]
77pub(crate) fn linux_minsigstksz() -> usize {
78    let mut minsigstksz = MINSIGSTKSZ.load(Relaxed);
79
80    if minsigstksz == 0 {
81        init_auxv();
82        minsigstksz = MINSIGSTKSZ.load(Relaxed);
83    }
84
85    minsigstksz
86}
87
88#[cfg(feature = "param")]
89#[inline]
90pub(crate) fn linux_execfn() -> &'static CStr {
91    let mut execfn = EXECFN.load(Relaxed);
92
93    if execfn.is_null() {
94        init_auxv();
95        execfn = EXECFN.load(Relaxed);
96    }
97
98    // SAFETY: We assume the `AT_EXECFN` value provided by the kernel is a
99    // valid pointer to a valid NUL-terminated array of bytes.
100    unsafe { CStr::from_ptr(execfn.cast()) }
101}
102
103#[cfg(feature = "runtime")]
104#[inline]
105pub(crate) fn linux_secure() -> bool {
106    let mut secure = SECURE.load(Relaxed);
107
108    // 0 means not initialized yet.
109    if secure == 0 {
110        init_auxv();
111        secure = SECURE.load(Relaxed);
112    }
113
114    // 0 means not present. Libc `getauxval(AT_SECURE)` would return 0.
115    // 1 means not in secure mode.
116    // 2 means in secure mode.
117    secure > 1
118}
119
120#[cfg(feature = "runtime")]
121#[inline]
122pub(crate) fn exe_phdrs() -> (*const c::c_void, usize, usize) {
123    let mut phdr = PHDR.load(Relaxed);
124    let mut phent = PHENT.load(Relaxed);
125    let mut phnum = PHNUM.load(Relaxed);
126
127    if phdr.is_null() || phnum == 0 {
128        init_auxv();
129        phdr = PHDR.load(Relaxed);
130        phent = PHENT.load(Relaxed);
131        phnum = PHNUM.load(Relaxed);
132    }
133
134    (phdr.cast(), phent, phnum)
135}
136
137/// `AT_SYSINFO_EHDR` isn't present on all platforms in all configurations, so
138/// if we don't see it, this function returns a null pointer.
139///
140/// And, this function returns a null pointer, rather than panicking, if the
141/// auxv records can't be read.
142#[inline]
143pub(in super::super) fn sysinfo_ehdr() -> *const Elf_Ehdr {
144    let mut ehdr = SYSINFO_EHDR.load(Relaxed);
145
146    if ehdr.is_null() {
147        // Use `maybe_init_auxv` to read the aux vectors if it can, but do
148        // nothing if it can't. If it can't, then we'll get a null pointer
149        // here, which our callers are prepared to deal with.
150        maybe_init_auxv();
151
152        ehdr = SYSINFO_EHDR.load(Relaxed);
153    }
154
155    ehdr
156}
157
158#[cfg(feature = "runtime")]
159#[inline]
160pub(crate) fn entry() -> usize {
161    let mut entry = ENTRY.load(Relaxed);
162
163    if entry == 0 {
164        init_auxv();
165        entry = ENTRY.load(Relaxed);
166    }
167
168    entry
169}
170
171#[cfg(feature = "runtime")]
172#[inline]
173pub(crate) fn random() -> *const [u8; 16] {
174    let mut random = RANDOM.load(Relaxed);
175
176    if random.is_null() {
177        init_auxv();
178        random = RANDOM.load(Relaxed);
179    }
180
181    random
182}
183
184static PAGE_SIZE: AtomicUsize = AtomicUsize::new(0);
185static CLOCK_TICKS_PER_SECOND: AtomicUsize = AtomicUsize::new(0);
186static HWCAP: AtomicUsize = AtomicUsize::new(0);
187static HWCAP2: AtomicUsize = AtomicUsize::new(0);
188static MINSIGSTKSZ: AtomicUsize = AtomicUsize::new(0);
189static EXECFN: AtomicPtr<c::c_char> = AtomicPtr::new(null_mut());
190static SYSINFO_EHDR: AtomicPtr<Elf_Ehdr> = AtomicPtr::new(null_mut());
191#[cfg(feature = "runtime")]
192static SECURE: AtomicU8 = AtomicU8::new(0);
193#[cfg(feature = "runtime")]
194static PHDR: AtomicPtr<Elf_Phdr> = AtomicPtr::new(null_mut());
195#[cfg(feature = "runtime")]
196static PHENT: AtomicUsize = AtomicUsize::new(0);
197#[cfg(feature = "runtime")]
198static PHNUM: AtomicUsize = AtomicUsize::new(0);
199#[cfg(feature = "runtime")]
200static ENTRY: AtomicUsize = AtomicUsize::new(0);
201#[cfg(feature = "runtime")]
202static RANDOM: AtomicPtr<[u8; 16]> = AtomicPtr::new(null_mut());
203
204const PR_GET_AUXV: c::c_int = 0x4155_5856;
205
206/// Use Linux ≥ 6.4's [`PR_GET_AUXV`] to read the aux records, into a provided
207/// statically-sized buffer. Return:
208///  - `Ok(…)` if the buffer is big enough.
209///  - `Err(Ok(len))` if we need a buffer of length `len`.
210///  - `Err(Err(err))` if we failed with `err`.
211///
212///  [`PR_GET_AUXV`]: https://www.man7.org/linux/man-pages/man2/PR_GET_AUXV.2const.html
213#[cold]
214fn pr_get_auxv_static(buffer: &mut [u8; 512]) -> Result<&mut [u8], crate::io::Result<usize>> {
215    let len = unsafe {
216        ret_usize(syscall_always_asm!(
217            __NR_prctl,
218            c_int(PR_GET_AUXV),
219            buffer.as_mut_ptr(),
220            pass_usize(buffer.len()),
221            pass_usize(0),
222            pass_usize(0)
223        ))
224        .map_err(Err)?
225    };
226    if len <= buffer.len() {
227        return Ok(&mut buffer[..len]);
228    }
229    Err(Ok(len))
230}
231
232/// Use Linux ≥ 6.4's [`PR_GET_AUXV`] to read the aux records, using a
233/// provided statically-sized buffer if possible, or a dynamically allocated
234/// buffer otherwise. Return:
235///  - Ok(…) on success.
236///  - Err(err) on failure.
237///
238///  [`PR_GET_AUXV`]: https://www.man7.org/linux/man-pages/man2/PR_GET_AUXV.2const.html
239#[cfg(feature = "alloc")]
240#[cold]
241fn pr_get_auxv_dynamic(buffer: &mut [u8; 512]) -> crate::io::Result<Cow<'_, [u8]>> {
242    // First try use the static buffer.
243    let len = match pr_get_auxv_static(buffer) {
244        Ok(buffer) => return Ok(Cow::Borrowed(buffer)),
245        Err(Ok(len)) => len,
246        Err(Err(err)) => return Err(err),
247    };
248
249    // If that indicates it needs a bigger buffer, allocate one.
250    let mut buffer = vec![0_u8; len];
251    let len = unsafe {
252        ret_usize(syscall_always_asm!(
253            __NR_prctl,
254            c_int(PR_GET_AUXV),
255            buffer.as_mut_ptr(),
256            pass_usize(buffer.len()),
257            pass_usize(0),
258            pass_usize(0)
259        ))?
260    };
261    assert_eq!(len, buffer.len());
262    Ok(Cow::Owned(buffer))
263}
264
265/// Read the auxv records and initialize the various static variables. Panic
266/// if an error is encountered.
267#[cold]
268fn init_auxv() {
269    init_auxv_impl().unwrap();
270}
271
272/// Like `init_auxv`, but don't panic if an error is encountered. The caller
273/// must be prepared for initialization to be skipped.
274#[cold]
275fn maybe_init_auxv() {
276    let _ = init_auxv_impl();
277}
278
279/// If we don't have "use-explicitly-provided-auxv" or "use-libc-auxv", we
280/// read the aux vector via the `prctl` `PR_GET_AUXV`, with a fallback to
281/// /proc/self/auxv for kernels that don't support `PR_GET_AUXV`.
282#[cold]
283fn init_auxv_impl() -> Result<(), ()> {
284    // 512 bytes of AUX elements ought to be enough for anybody…
285    let mut buffer = [0_u8; 512];
286
287    // If we don't have "alloc", just try to read into our statically-sized
288    // buffer. This might fail due to the buffer being insufficient; we're
289    // prepared to cope, though we may do suboptimal things.
290    #[cfg(not(feature = "alloc"))]
291    let result = pr_get_auxv_static(&mut buffer);
292
293    // If we do have "alloc" then read into our statically-sized buffer if
294    // it fits, or fall back to a dynamically-allocated buffer.
295    #[cfg(feature = "alloc")]
296    let result = pr_get_auxv_dynamic(&mut buffer);
297
298    if let Ok(buffer) = result {
299        // SAFETY: We assume the kernel returns a valid auxv.
300        unsafe {
301            init_from_aux_iter(AuxPointer(buffer.as_ptr().cast())).unwrap();
302        }
303        return Ok(());
304    }
305
306    // If `PR_GET_AUXV` is unavailable, or if we don't have "alloc" and
307    // the aux records don't fit in our static buffer, then fall back to trying
308    // to open "/proc/self/auxv". We don't use `proc_self_fd` because its extra
309    // checking breaks on QEMU.
310    if let Ok(file) = crate::fs::open("/proc/self/auxv", OFlags::RDONLY, Mode::empty()) {
311        #[cfg(feature = "alloc")]
312        init_from_auxv_file(file).unwrap();
313
314        #[cfg(not(feature = "alloc"))]
315        unsafe {
316            init_from_aux_iter(AuxFile(file)).unwrap();
317        }
318
319        return Ok(());
320    }
321
322    Err(())
323}
324
325/// Process auxv entries from the open file `auxv`.
326#[cfg(feature = "alloc")]
327#[cold]
328#[must_use]
329fn init_from_auxv_file(auxv: OwnedFd) -> Option<()> {
330    let mut buffer = Vec::<u8>::with_capacity(512);
331    loop {
332        let cur = buffer.len();
333
334        // Request one extra byte; `Vec` will often allocate more.
335        buffer.reserve(1);
336
337        // Use all the space it allocated.
338        buffer.resize(buffer.capacity(), 0);
339
340        // Read up to that many bytes.
341        let n = match crate::io::read(&auxv, &mut buffer[cur..]) {
342            Err(crate::io::Errno::INTR) => 0,
343            Err(_err) => panic!(),
344            Ok(0) => break,
345            Ok(n) => n,
346        };
347
348        // Account for the number of bytes actually read.
349        buffer.resize(cur + n, 0_u8);
350    }
351
352    // SAFETY: We loaded from an auxv file into the buffer.
353    unsafe { init_from_aux_iter(AuxPointer(buffer.as_ptr().cast())) }
354}
355
356/// Process auxv entries from the auxv array pointed to by `auxp`.
357///
358/// # Safety
359///
360/// This must be passed a pointer to an auxv array.
361///
362/// The buffer contains `Elf_aux_t` elements, though it need not be aligned;
363/// function uses `read_unaligned` to read from it.
364#[cold]
365#[must_use]
366unsafe fn init_from_aux_iter(aux_iter: impl Iterator<Item = Elf_auxv_t>) -> Option<()> {
367    let mut pagesz = 0;
368    let mut clktck = 0;
369    let mut hwcap = 0;
370    let mut hwcap2 = 0;
371    let mut minsigstksz = 0;
372    let mut execfn = null_mut();
373    let mut sysinfo_ehdr = null_mut();
374    #[cfg(feature = "runtime")]
375    let mut secure = 0;
376    #[cfg(feature = "runtime")]
377    let mut phdr = null_mut();
378    #[cfg(feature = "runtime")]
379    let mut phnum = 0;
380    #[cfg(feature = "runtime")]
381    let mut phent = 0;
382    #[cfg(feature = "runtime")]
383    let mut entry = 0;
384    #[cfg(feature = "runtime")]
385    let mut uid = None;
386    #[cfg(feature = "runtime")]
387    let mut euid = None;
388    #[cfg(feature = "runtime")]
389    let mut gid = None;
390    #[cfg(feature = "runtime")]
391    let mut egid = None;
392    #[cfg(feature = "runtime")]
393    let mut random = null_mut();
394
395    for Elf_auxv_t { a_type, a_val } in aux_iter {
396        match a_type as _ {
397            AT_PAGESZ => pagesz = a_val as usize,
398            AT_CLKTCK => clktck = a_val as usize,
399            AT_HWCAP => hwcap = a_val as usize,
400            AT_HWCAP2 => hwcap2 = a_val as usize,
401            AT_MINSIGSTKSZ => minsigstksz = a_val as usize,
402            AT_EXECFN => execfn = check_raw_pointer::<c::c_char>(a_val as *mut _)?.as_ptr(),
403
404            // Use the `AT_SYSINFO_EHDR` if it matches the platform rustix is
405            // compiled for.
406            AT_SYSINFO_EHDR => {
407                if let Some(value) = check_elf_base(a_val as *mut _) {
408                    sysinfo_ehdr = value.as_ptr();
409                }
410            }
411
412            #[cfg(feature = "runtime")]
413            AT_SECURE => secure = (a_val as usize != 0) as u8 + 1,
414            #[cfg(feature = "runtime")]
415            AT_UID => uid = Some(a_val),
416            #[cfg(feature = "runtime")]
417            AT_EUID => euid = Some(a_val),
418            #[cfg(feature = "runtime")]
419            AT_GID => gid = Some(a_val),
420            #[cfg(feature = "runtime")]
421            AT_EGID => egid = Some(a_val),
422            #[cfg(feature = "runtime")]
423            AT_PHDR => phdr = check_raw_pointer::<Elf_Phdr>(a_val as *mut _)?.as_ptr(),
424            #[cfg(feature = "runtime")]
425            AT_PHNUM => phnum = a_val as usize,
426            #[cfg(feature = "runtime")]
427            AT_PHENT => phent = a_val as usize,
428            #[cfg(feature = "runtime")]
429            AT_ENTRY => entry = a_val as usize,
430            #[cfg(feature = "runtime")]
431            AT_RANDOM => random = check_raw_pointer::<[u8; 16]>(a_val as *mut _)?.as_ptr(),
432
433            AT_NULL => break,
434            _ => (),
435        }
436    }
437
438    #[cfg(feature = "runtime")]
439    assert_eq!(phent, size_of::<Elf_Phdr>());
440
441    // If we're running set-uid or set-gid, enable “secure execution” mode,
442    // which doesn't do much, but users may be depending on the things that
443    // it does do.
444    #[cfg(feature = "runtime")]
445    if uid != euid || gid != egid {
446        secure = 2;
447    }
448
449    // Accept the aux values.
450    PAGE_SIZE.store(pagesz, Relaxed);
451    CLOCK_TICKS_PER_SECOND.store(clktck, Relaxed);
452    HWCAP.store(hwcap, Relaxed);
453    HWCAP2.store(hwcap2, Relaxed);
454    MINSIGSTKSZ.store(minsigstksz, Relaxed);
455    EXECFN.store(execfn, Relaxed);
456    SYSINFO_EHDR.store(sysinfo_ehdr, Relaxed);
457    #[cfg(feature = "runtime")]
458    SECURE.store(secure, Relaxed);
459    #[cfg(feature = "runtime")]
460    PHDR.store(phdr, Relaxed);
461    #[cfg(feature = "runtime")]
462    PHNUM.store(phnum, Relaxed);
463    #[cfg(feature = "runtime")]
464    ENTRY.store(entry, Relaxed);
465    #[cfg(feature = "runtime")]
466    RANDOM.store(random, Relaxed);
467
468    Some(())
469}
470
471/// Check that `base` is a valid pointer to the kernel-provided vDSO.
472///
473/// `base` is some value we got from a `AT_SYSINFO_EHDR` aux record somewhere,
474/// which hopefully holds the value of the kernel-provided vDSO in memory. Do a
475/// series of checks to be as sure as we can that it's safe to use.
476#[cold]
477#[must_use]
478unsafe fn check_elf_base(base: *const Elf_Ehdr) -> Option<NonNull<Elf_Ehdr>> {
479    // If we're reading a 64-bit auxv on a 32-bit platform, we'll see a zero
480    // `a_val` because `AT_*` values are never greater than `u32::MAX`. Zero is
481    // used by libc's `getauxval` to indicate errors, so it should never be a
482    // valid value.
483    if base.is_null() {
484        return None;
485    }
486
487    let hdr = check_raw_pointer::<Elf_Ehdr>(base as *mut _)?;
488
489    let hdr = hdr.as_ref();
490    if hdr.e_ident[..SELFMAG] != ELFMAG {
491        return None; // Wrong ELF magic
492    }
493    if !matches!(hdr.e_ident[EI_OSABI], ELFOSABI_SYSV | ELFOSABI_LINUX) {
494        return None; // Unrecognized ELF OS ABI
495    }
496    if hdr.e_ident[EI_ABIVERSION] != ELFABIVERSION {
497        return None; // Unrecognized ELF ABI version
498    }
499    if hdr.e_type != ET_DYN {
500        return None; // Wrong ELF type
501    }
502
503    // If ELF is extended, we'll need to adjust.
504    if hdr.e_ident[EI_VERSION] != EV_CURRENT
505        || hdr.e_ehsize as usize != size_of::<Elf_Ehdr>()
506        || hdr.e_phentsize as usize != size_of::<Elf_Phdr>()
507    {
508        return None;
509    }
510    // We don't currently support extra-large numbers of segments.
511    if hdr.e_phnum == PN_XNUM {
512        return None;
513    }
514
515    // If `e_phoff` is zero, it's more likely that we're looking at memory that
516    // has been zeroed than that the kernel has somehow aliased the `Ehdr` and
517    // the `Phdr`.
518    if hdr.e_phoff < size_of::<Elf_Ehdr>() {
519        return None;
520    }
521
522    // Verify that the `EI_CLASS`/`EI_DATA`/`e_machine` fields match the
523    // architecture we're running as. This helps catch cases where we're
524    // running under QEMU.
525    if hdr.e_ident[EI_CLASS] != ELFCLASS {
526        return None; // Wrong ELF class
527    }
528    if hdr.e_ident[EI_DATA] != ELFDATA {
529        return None; // Wrong ELF data
530    }
531    if hdr.e_machine != EM_CURRENT {
532        return None; // Wrong machine type
533    }
534
535    Some(NonNull::new_unchecked(as_ptr(hdr) as *mut _))
536}
537
538// Aux reading utilities
539
540// Read auxv records from an array in memory.
541struct AuxPointer(*const Elf_auxv_t);
542
543impl Iterator for AuxPointer {
544    type Item = Elf_auxv_t;
545
546    #[cold]
547    fn next(&mut self) -> Option<Self::Item> {
548        unsafe {
549            let value = read_unaligned(self.0);
550            self.0 = self.0.add(1);
551            Some(value)
552        }
553    }
554}
555
556// Read auxv records from a file.
557#[cfg(not(feature = "alloc"))]
558struct AuxFile(OwnedFd);
559
560#[cfg(not(feature = "alloc"))]
561impl Iterator for AuxFile {
562    type Item = Elf_auxv_t;
563
564    // This implementation does lots of `read`s and it isn't amazing, but
565    // hopefully we won't use it often.
566    #[cold]
567    fn next(&mut self) -> Option<Self::Item> {
568        let mut buf = [0_u8; size_of::<Self::Item>()];
569        let mut slice = &mut buf[..];
570        while !slice.is_empty() {
571            match crate::io::read(&self.0, &mut *slice) {
572                Ok(0) => panic!("unexpected end of auxv file"),
573                Ok(n) => slice = &mut slice[n..],
574                Err(crate::io::Errno::INTR) => continue,
575                Err(err) => panic!("{:?}", err),
576            }
577        }
578        Some(unsafe { read_unaligned(buf.as_ptr().cast()) })
579    }
580}