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