55//! This uses raw pointers to locate and read the kernel-provided auxv array.
66#![ allow( unsafe_code) ]
77
8+ use super :: super :: conv:: { c_int, pass_usize, ret_usize} ;
89use crate :: backend:: c;
910use crate :: fd:: OwnedFd ;
1011#[ cfg( feature = "param" ) ]
@@ -27,6 +28,8 @@ use linux_raw_sys::general::{
2728use linux_raw_sys:: general:: {
2829 AT_EGID , AT_ENTRY , AT_EUID , AT_GID , AT_PHDR , AT_PHENT , AT_PHNUM , AT_RANDOM , AT_SECURE , AT_UID ,
2930} ;
31+ #[ cfg( feature = "alloc" ) ]
32+ use { alloc:: borrow:: Cow , alloc:: vec} ;
3033
3134#[ cfg( feature = "param" ) ]
3235#[ inline]
@@ -120,12 +123,19 @@ pub(crate) fn exe_phdrs() -> (*const c::c_void, usize, usize) {
120123
121124/// `AT_SYSINFO_EHDR` isn't present on all platforms in all configurations, so
122125/// 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.
123129#[ inline]
124130pub ( in super :: super ) fn sysinfo_ehdr ( ) -> * const Elf_Ehdr {
125131 let mut ehdr = SYSINFO_EHDR . load ( Relaxed ) ;
126132
127133 if ehdr. is_null ( ) {
128- init_auxv ( ) ;
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+
129139 ehdr = SYSINFO_EHDR . load ( Relaxed ) ;
130140 }
131141
@@ -177,74 +187,122 @@ static ENTRY: AtomicUsize = AtomicUsize::new(0);
177187#[ cfg( feature = "runtime" ) ]
178188static RANDOM : AtomicPtr < [ u8 ; 16 ] > = AtomicPtr :: new ( null_mut ( ) ) ;
179189
180- #[ cfg( feature = "alloc" ) ]
181- fn pr_get_auxv ( ) -> crate :: io:: Result < Vec < u8 > > {
182- use super :: super :: conv:: { c_int, pass_usize, ret_usize} ;
183- const PR_GET_AUXV : c:: c_int = 0x4155_5856 ;
184- let mut buffer = alloc:: vec![ 0u8 ; 512 ] ;
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]
198+ fn pr_get_auxv_static ( buffer : & mut [ u8 ; 512 ] ) -> Result < & mut [ u8 ] , crate :: io:: Result < usize > > {
185199 let len = unsafe {
186200 ret_usize ( syscall_always_asm ! (
187201 __NR_prctl,
188202 c_int( PR_GET_AUXV ) ,
189- buffer. as_ptr ( ) ,
203+ buffer. as_mut_ptr ( ) ,
190204 pass_usize( buffer. len( ) ) ,
191205 pass_usize( 0 ) ,
192206 pass_usize( 0 )
193- ) ) ?
207+ ) )
208+ . map_err ( Err ) ?
194209 } ;
195210 if len <= buffer. len ( ) {
196- buffer. truncate ( len) ;
197- return Ok ( buffer) ;
211+ return Ok ( & mut buffer[ ..len] ) ;
198212 }
199- buffer. resize ( len, 0 ) ;
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]
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] ;
200233 let len = unsafe {
201234 ret_usize ( syscall_always_asm ! (
202235 __NR_prctl,
203236 c_int( PR_GET_AUXV ) ,
204- buffer. as_ptr ( ) ,
237+ buffer. as_mut_ptr ( ) ,
205238 pass_usize( buffer. len( ) ) ,
206239 pass_usize( 0 ) ,
207240 pass_usize( 0 )
208241 ) ) ?
209242 } ;
210243 assert_eq ! ( len, buffer. len( ) ) ;
211- return Ok ( buffer) ;
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]
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]
257+ fn maybe_init_auxv ( ) {
258+ if let Ok ( ( ) ) = init_auxv_impl ( ) {
259+ return ;
260+ }
212261}
213262
214263/// If we don't have "use-explicitly-provided-auxv" or "use-libc-auxv", we
215264/// read the aux vector via the `prctl` `PR_GET_AUXV`, with a fallback to
216265/// /proc/self/auxv for kernels that don't support `PR_GET_AUXV`.
217266#[ cold]
218- fn init_auxv ( ) {
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.
219278 #[ cfg( feature = "alloc" ) ]
220- {
221- match pr_get_auxv ( ) {
222- Ok ( buffer) => {
223- // SAFETY: We assume the kernel returns a valid auxv.
224- unsafe {
225- init_from_aux_iter ( AuxPointer ( buffer. as_ptr ( ) . cast ( ) ) ) . unwrap ( ) ;
226- }
227- return ;
228- }
229- Err ( _) => {
230- // Fall back to /proc/self/auxv on error.
231- }
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 ( ) ;
232285 }
286+ return Ok ( ( ) ) ;
233287 }
234288
235- // Open "/proc/self/auxv", either because we trust "/proc", or because
236- // we're running inside QEMU and `proc_self_auxv`'s extra checking foils
237- // QEMU's emulation so we need to do a plain open to get the right
238- // auxv records.
239- let file = crate :: fs:: open ( "/proc/self/auxv" , OFlags :: RDONLY , Mode :: empty ( ) ) . unwrap ( ) ;
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 ( ) ;
240296
241- #[ cfg( feature = "alloc" ) ]
242- init_from_auxv_file ( file) . unwrap ( ) ;
297+ #[ cfg( not( feature = "alloc" ) ) ]
298+ unsafe {
299+ init_from_aux_iter ( AuxFile ( file) ) . unwrap ( ) ;
300+ }
243301
244- #[ cfg( not( feature = "alloc" ) ) ]
245- unsafe {
246- init_from_aux_iter ( AuxFile ( file) ) . unwrap ( ) ;
302+ return Ok ( ( ) ) ;
247303 }
304+
305+ Err ( ( ) )
248306}
249307
250308/// Process auxv entries from the open file `auxv`.
0 commit comments