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