1const AT_SYSINFO_EHDR: c_ulong = 33;
19const AT_PAGESZ: c_ulong = 6;
20
21const PT_LOAD: u32 = 1;
22const PT_DYNAMIC: u32 = 2;
23const DT_HASH: u64 = 4;
24const DT_STRTAB: u64 = 5;
25const DT_SYMTAB: u64 = 6;
26
27const STT_FUNC: u8 = 2;
28const STB_GLOBAL: u8 = 1;
29const STB_WEAK: u8 = 2;
30
31const MADV_WIPEONFORK: i32 = 18;
32
33const GETRANDOM_TCB_OFFSET: usize = 0x38;
36
37const STATE_ALLOCATING: usize = 1;
41
42#[repr(C)]
44struct VgetrandomParams {
45 size_of_opaque_state: u32,
46 mmap_prot: u32,
47 mmap_flags: u32,
48 reserved: [u32; 13],
49}
50
51const VGETRANDOM_UNINIT: usize = 0;
52const VGETRANDOM_INITIALIZING: usize = 1;
53const VGETRANDOM_READY: usize = 2;
54const VGETRANDOM_DISABLED: usize = 3;
55
56static VGETRANDOM_STATE: AtomicUsize = AtomicUsize::new(VGETRANDOM_UNINIT);
57static VGETRANDOM_FN: AtomicUsize = AtomicUsize::new(0);
58static VGETRANDOM_SIZE: AtomicUsize = AtomicUsize::new(0);
59static VGETRANDOM_PROT: AtomicUsize = AtomicUsize::new(0);
60static VGETRANDOM_FLAGS: AtomicUsize = AtomicUsize::new(0);
61static VGETRANDOM_PAGE: AtomicUsize = AtomicUsize::new(0);
62
63static VGETRANDOM_ALLOC: AtomicUsize = AtomicUsize::new(0);
67
68static CLOCK_GETTIME_VDSO: AtomicUsize = AtomicUsize::new(usize::MAX);
70
71static GETCPU_VDSO: core::sync::atomic::AtomicPtr<c_void> =
74 core::sync::atomic::AtomicPtr::new(getcpu_init as *mut c_void);
75
76#[inline]
77unsafe fn read_u16(p: *const u8) -> u16 {
78 u16::from_le(core::ptr::read_unaligned(p as *const u16))
79}
80
81#[inline]
82unsafe fn read_u32(p: *const u8) -> u32 {
83 u32::from_le(core::ptr::read_unaligned(p as *const u32))
84}
85
86#[inline]
87unsafe fn read_u64(p: *const u8) -> u64 {
88 u64::from_le(core::ptr::read_unaligned(p as *const u64))
89}
90
91unsafe fn cstr_eq(s: *const u8, want: &[u8]) -> bool {
92 let mut i = 0usize;
93 while i < want.len() {
94 if *s.add(i) != want[i] {
95 return false;
96 }
97 i += 1;
98 }
99 *s.add(want.len()) == 0
100}
101
102unsafe fn vdso_lookup(want: &[u8]) -> usize {
105 let eh = crate::getauxval(AT_SYSINFO_EHDR) as usize;
106 if eh == 0 {
107 return 0;
108 }
109 let image = eh as *const u8;
110 if read_u32(image) != 0x464c_457f || *image.add(4) != 2 {
111 return 0; }
113 let phoff = read_u64(image.add(32)) as usize;
114 let phentsize = read_u16(image.add(54)) as usize;
115 let phnum = read_u16(image.add(56)) as usize;
116
117 let mut load_base = usize::MAX;
118 let mut dynamic = 0usize;
119 let mut i = 0usize;
120 while i < phnum {
121 let ph = image.add(phoff + i * phentsize);
122 match read_u32(ph) {
123 PT_LOAD => {
124 let off = read_u64(ph.add(8)) as usize;
125 let vaddr = read_u64(ph.add(16)) as usize;
126 load_base = eh.wrapping_add(off).wrapping_sub(vaddr);
127 }
128 PT_DYNAMIC => {
129 dynamic = eh.wrapping_add(read_u64(ph.add(8)) as usize);
130 }
131 _ => {}
132 }
133 i += 1;
134 }
135 if load_base == usize::MAX || dynamic == 0 {
136 return 0;
137 }
138
139 let mut strtab = 0usize;
140 let mut symtab = 0usize;
141 let mut hash = 0usize;
142 let mut dp = dynamic as *const u8;
143 loop {
144 let tag = read_u64(dp);
145 if tag == 0 {
146 break;
147 }
148 let val = read_u64(dp.add(8)) as usize;
149 match tag {
150 DT_STRTAB => strtab = load_base.wrapping_add(val),
151 DT_SYMTAB => symtab = load_base.wrapping_add(val),
152 DT_HASH => hash = load_base.wrapping_add(val),
153 _ => {}
154 }
155 dp = dp.add(16);
156 }
157 if strtab == 0 || symtab == 0 || hash == 0 {
158 return 0;
159 }
160
161 let nsym = read_u32((hash + 4) as *const u8) as usize;
163 let mut i = 1usize;
164 while i < nsym {
165 let sym = (symtab + i * 24) as *const u8;
166 let info = *sym.add(4);
167 if info & 0xf != STT_FUNC {
168 i += 1;
169 continue;
170 }
171 let bind = info >> 4;
172 if bind != STB_GLOBAL && bind != STB_WEAK {
173 i += 1;
174 continue;
175 }
176 if read_u16(sym.add(6)) == 0 {
177 i += 1;
178 continue;
179 }
180 let name = strtab.wrapping_add(read_u32(sym) as usize);
181 if cstr_eq(name as *const u8, want) {
182 return load_base.wrapping_add(read_u64(sym.add(8)) as usize);
183 }
184 i += 1;
185 }
186 0
187}
188
189unsafe fn vdso_lookup_either(vdso_name: &[u8], kernel_name: &[u8]) -> usize {
191 let p = vdso_lookup(vdso_name);
192 if p != 0 {
193 return p;
194 }
195 vdso_lookup(kernel_name)
196}
197
198#[inline]
199unsafe fn vdso_clock_gettime() -> usize {
200 let p = CLOCK_GETTIME_VDSO.load(Ordering::Relaxed);
201 if p != usize::MAX {
202 return p;
203 }
204 let p = vdso_lookup_either(b"__vdso_clock_gettime", b"__kernel_clock_gettime");
205 CLOCK_GETTIME_VDSO.store(p, Ordering::Relaxed);
206 p
207}
208
209#[no_mangle]
210pub unsafe extern "C" fn clock_gettime(clockid: c_int, ts: *mut timespec) -> c_int {
211 let p = vdso_clock_gettime();
212 if p != 0 {
213 let f: unsafe extern "C" fn(c_int, *mut timespec) -> c_int = core::mem::transmute(p);
214 let r = f(clockid, ts);
215 if r == 0 {
216 return 0;
217 }
218 if r == -EINVAL {
219 ERRNO!() = EINVAL;
220 return -1;
221 }
222 }
224 let r = sys_clock_gettime(clockid, ts);
225 if r < 0 {
226 ERRNO!() = (-r) as c_int;
227 -1
228 } else {
229 0
230 }
231}
232
233unsafe extern "C" fn getcpu_init(cpu: *mut u32, node: *mut u32, unused: *mut c_void) -> c_long {
234 let p = vdso_lookup_either(b"__vdso_getcpu", b"__kernel_getcpu");
235 GETCPU_VDSO.store(p as *mut c_void, Ordering::Relaxed);
236 if p == 0 {
237 -(ENOSYS_VAL as c_long)
238 } else {
239 let f: unsafe extern "C" fn(*mut u32, *mut u32, *mut c_void) -> c_long =
240 core::mem::transmute(p);
241 f(cpu, node, unused)
242 }
243}
244
245#[inline]
248unsafe fn vdso_getcpu(cpu: *mut u32) -> bool {
249 let p = GETCPU_VDSO.load(Ordering::Relaxed) as usize;
250 if p == 0 {
251 return false;
252 }
253 let f: unsafe extern "C" fn(*mut u32, *mut u32, *mut c_void) -> c_long =
254 core::mem::transmute(p);
255 f(cpu, core::ptr::null_mut(), core::ptr::null_mut()) == 0
256}
257
258#[inline]
263unsafe fn tcb_slot(offset: usize) -> *mut usize {
264 let tp = arch_get_tp_fast();
265 if tp == 0 {
266 return core::ptr::null_mut();
267 }
268 #[cfg(target_arch = "x86_64")]
269 let tcb = tp;
270 #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
271 let tcb = tp - 256;
272 if core::ptr::read_unaligned(tcb as *const usize) != tcb {
273 return core::ptr::null_mut();
274 }
275 (tcb + offset) as *mut usize
276}
277
278#[inline]
279unsafe fn getrandom_slot() -> *mut usize {
280 tcb_slot(GETRANDOM_TCB_OFFSET)
281}
282
283unsafe fn vgetrandom_init() -> bool {
286 match VGETRANDOM_STATE.load(Ordering::Acquire) {
287 VGETRANDOM_READY => return true,
288 VGETRANDOM_DISABLED => return false,
289 VGETRANDOM_UNINIT => {}
290 _ => return false,
293 }
294 if VGETRANDOM_STATE
295 .compare_exchange(
296 VGETRANDOM_UNINIT,
297 VGETRANDOM_INITIALIZING,
298 Ordering::AcqRel,
299 Ordering::Acquire,
300 )
301 .is_err()
302 {
303 return false;
304 }
305
306 let p = vdso_lookup_either(b"__vdso_getrandom", b"__kernel_getrandom");
307 let mut ready = false;
308 if p != 0 {
309 let query: unsafe extern "C" fn(
310 *mut u8,
311 usize,
312 u32,
313 *mut VgetrandomParams,
314 usize,
315 ) -> isize = core::mem::transmute(p);
316 let mut params: VgetrandomParams = core::mem::zeroed();
317 if query(core::ptr::null_mut(), 0, 0, &mut params, usize::MAX) == 0 {
318 let page = crate::getauxval(AT_PAGESZ) as usize;
319 let page = if page == 0 { 4096 } else { page };
320 let size = params.size_of_opaque_state as usize;
321 if size > 0 && size <= page {
322 VGETRANDOM_FN.store(p, Ordering::Relaxed);
323 VGETRANDOM_SIZE.store(size, Ordering::Relaxed);
324 VGETRANDOM_PROT.store(params.mmap_prot as usize, Ordering::Relaxed);
325 VGETRANDOM_FLAGS.store(params.mmap_flags as usize, Ordering::Relaxed);
326 VGETRANDOM_PAGE.store(page, Ordering::Relaxed);
327 ready = true;
328 }
329 }
330 }
331 VGETRANDOM_STATE.store(
332 if ready {
333 VGETRANDOM_READY
334 } else {
335 VGETRANDOM_DISABLED
336 },
337 Ordering::Release,
338 );
339 ready
340}
341
342unsafe fn vgetrandom_thread_state() -> *mut u8 {
345 let slot = getrandom_slot();
346 if slot.is_null() {
347 return core::ptr::null_mut();
348 }
349 let mut state = core::ptr::read_unaligned(slot);
355 if state == 0 {
356 if VGETRANDOM_ALLOC
357 .compare_exchange(0, 1, Ordering::Acquire, Ordering::Relaxed)
358 .is_err()
359 {
360 return core::ptr::null_mut();
361 }
362 state = core::ptr::read_unaligned(slot);
365 if state != 0 {
366 VGETRANDOM_ALLOC.store(0, Ordering::Release);
367 return if state == STATE_ALLOCATING {
368 core::ptr::null_mut()
369 } else {
370 state as *mut u8
371 };
372 }
373 core::ptr::write_unaligned(slot, STATE_ALLOCATING);
374 let saved_errno = ERRNO!();
375 let page = VGETRANDOM_PAGE.load(Ordering::Relaxed);
376 let prot = VGETRANDOM_PROT.load(Ordering::Relaxed) as i32;
377 let flags = VGETRANDOM_FLAGS.load(Ordering::Relaxed) as i32;
378 let mapped = sys_mmap(core::ptr::null_mut(), page, prot, flags, -1, 0);
379 if mapped.is_null() || mapped == MMAP_FAILED {
380 ERRNO!() = saved_errno;
381 core::ptr::write_unaligned(slot, 0);
382 VGETRANDOM_ALLOC.store(0, Ordering::Release);
383 return core::ptr::null_mut();
384 }
385 let advice = <Arch as Syscalls>::syscall3(
389 sysno(28, 233),
390 mapped as i64,
391 page as i64,
392 MADV_WIPEONFORK as i64,
393 );
394 if advice < 0 {
395 ERRNO!() = saved_errno;
396 sys_munmap(mapped, page);
397 core::ptr::write_unaligned(slot, 0);
398 VGETRANDOM_ALLOC.store(0, Ordering::Release);
399 VGETRANDOM_STATE.store(VGETRANDOM_DISABLED, Ordering::Release);
400 return core::ptr::null_mut();
401 }
402 core::ptr::write_unaligned(slot, mapped as usize);
405 VGETRANDOM_ALLOC.store(0, Ordering::Release);
406 state = mapped as usize;
407 }
408 if state == STATE_ALLOCATING {
409 return core::ptr::null_mut();
410 }
411 state as *mut u8
412}
413
414unsafe fn vgetrandom(buf: *mut u8, len: usize, flags: u32) -> Option<isize> {
416 if len == 0 || !vgetrandom_init() {
417 return None;
418 }
419 let state = vgetrandom_thread_state();
420 if state.is_null() {
421 return None;
422 }
423 let p = VGETRANDOM_FN.load(Ordering::Relaxed);
424 if p == 0 {
425 return None;
426 }
427 let size = VGETRANDOM_SIZE.load(Ordering::Relaxed);
428 let f: unsafe extern "C" fn(*mut u8, usize, u32, *mut u8, usize) -> isize =
429 core::mem::transmute(p);
430 Some(f(buf, len, flags, state, size))
431}
432
433pub unsafe fn vdso_getrandom_thread_exit() {
435 let slot = getrandom_slot();
436 if slot.is_null() {
437 return;
438 }
439 let state = core::ptr::read_unaligned(slot);
440 if state > STATE_ALLOCATING {
441 core::ptr::write_unaligned(slot, 0);
442 let page = VGETRANDOM_PAGE.load(Ordering::Relaxed);
443 sys_munmap(state as *mut u8, page);
444 }
445}