1 //! libc syscalls supporting `rustix::thread`.
2 
3 use crate::backend::c;
4 use crate::backend::conv::ret;
5 use crate::io;
6 #[cfg(not(target_os = "redox"))]
7 use crate::thread::{NanosleepRelativeResult, Timespec};
8 #[cfg(all(target_env = "gnu", fix_y2038))]
9 use crate::timespec::LibcTimespec;
10 use core::mem::MaybeUninit;
11 #[cfg(linux_kernel)]
12 use {
13     crate::backend::conv::{borrowed_fd, ret_c_int, ret_usize},
14     crate::fd::BorrowedFd,
15     crate::pid::Pid,
16     crate::thread::{FutexFlags, FutexOperation},
17     crate::utils::as_mut_ptr,
18 };
19 #[cfg(not(any(
20     apple,
21     freebsdlike,
22     target_os = "emscripten",
23     target_os = "espidf",
24     target_os = "haiku",
25     target_os = "openbsd",
26     target_os = "redox",
27     target_os = "vita",
28     target_os = "wasi",
29 )))]
30 use {crate::thread::ClockId, core::ptr::null_mut};
31 
32 #[cfg(all(target_env = "gnu", fix_y2038))]
33 weak!(fn __clock_nanosleep_time64(c::clockid_t, c::c_int, *const LibcTimespec, *mut LibcTimespec) -> c::c_int);
34 #[cfg(all(target_env = "gnu", fix_y2038))]
35 weak!(fn __nanosleep64(*const LibcTimespec, *mut LibcTimespec) -> c::c_int);
36 
37 #[cfg(not(any(
38     apple,
39     target_os = "dragonfly",
40     target_os = "emscripten",
41     target_os = "espidf",
42     target_os = "freebsd", // FreeBSD 12 has clock_nanosleep, but libc targets FreeBSD 11.
43     target_os = "haiku",
44     target_os = "openbsd",
45     target_os = "redox",
46     target_os = "vita",
47     target_os = "wasi",
48 )))]
49 #[inline]
clock_nanosleep_relative(id: ClockId, request: &Timespec) -> NanosleepRelativeResult50 pub(crate) fn clock_nanosleep_relative(id: ClockId, request: &Timespec) -> NanosleepRelativeResult {
51     // Old 32-bit version: libc has `clock_nanosleep` but it is not y2038 safe
52     // by default. But there may be a `__clock_nanosleep_time64` we can use.
53     #[cfg(fix_y2038)]
54     {
55         #[cfg(target_env = "gnu")]
56         if let Some(libc_clock_nanosleep) = __clock_nanosleep_time64.get() {
57             let flags = 0;
58             let mut remain = MaybeUninit::<LibcTimespec>::uninit();
59 
60             unsafe {
61                 return match libc_clock_nanosleep(
62                     id as c::clockid_t,
63                     flags,
64                     &request.clone().into(),
65                     remain.as_mut_ptr(),
66                 ) {
67                     0 => NanosleepRelativeResult::Ok,
68                     err if err == io::Errno::INTR.0 => {
69                         NanosleepRelativeResult::Interrupted(remain.assume_init().into())
70                     }
71                     err => NanosleepRelativeResult::Err(io::Errno(err)),
72                 };
73             }
74         }
75 
76         clock_nanosleep_relative_old(id, request)
77     }
78 
79     // Main version: libc is y2038 safe and has `clock_nanosleep`.
80     #[cfg(not(fix_y2038))]
81     unsafe {
82         let flags = 0;
83         let mut remain = MaybeUninit::<Timespec>::uninit();
84 
85         match c::clock_nanosleep(id as c::clockid_t, flags, request, remain.as_mut_ptr()) {
86             0 => NanosleepRelativeResult::Ok,
87             err if err == io::Errno::INTR.0 => {
88                 NanosleepRelativeResult::Interrupted(remain.assume_init())
89             }
90             err => NanosleepRelativeResult::Err(io::Errno(err)),
91         }
92     }
93 }
94 
95 #[cfg(all(
96     fix_y2038,
97     not(any(
98         apple,
99         target_os = "emscripten",
100         target_os = "haiku",
101         target_os = "vita"
102     ))
103 ))]
clock_nanosleep_relative_old(id: ClockId, request: &Timespec) -> NanosleepRelativeResult104 fn clock_nanosleep_relative_old(id: ClockId, request: &Timespec) -> NanosleepRelativeResult {
105     let tv_sec = match request.tv_sec.try_into() {
106         Ok(tv_sec) => tv_sec,
107         Err(_) => return NanosleepRelativeResult::Err(io::Errno::OVERFLOW),
108     };
109     let tv_nsec = match request.tv_nsec.try_into() {
110         Ok(tv_nsec) => tv_nsec,
111         Err(_) => return NanosleepRelativeResult::Err(io::Errno::INVAL),
112     };
113     let old_request = c::timespec { tv_sec, tv_nsec };
114     let mut old_remain = MaybeUninit::<c::timespec>::uninit();
115     let flags = 0;
116 
117     unsafe {
118         match c::clock_nanosleep(
119             id as c::clockid_t,
120             flags,
121             &old_request,
122             old_remain.as_mut_ptr(),
123         ) {
124             0 => NanosleepRelativeResult::Ok,
125             err if err == io::Errno::INTR.0 => {
126                 let old_remain = old_remain.assume_init();
127                 let remain = Timespec {
128                     tv_sec: old_remain.tv_sec.into(),
129                     tv_nsec: old_remain.tv_nsec.into(),
130                 };
131                 NanosleepRelativeResult::Interrupted(remain)
132             }
133             err => NanosleepRelativeResult::Err(io::Errno(err)),
134         }
135     }
136 }
137 
138 #[cfg(not(any(
139     apple,
140     target_os = "dragonfly",
141     target_os = "emscripten",
142     target_os = "espidf",
143     target_os = "freebsd", // FreeBSD 12 has clock_nanosleep, but libc targets FreeBSD 11.
144     target_os = "haiku",
145     target_os = "openbsd",
146     target_os = "redox",
147     target_os = "vita",
148     target_os = "wasi",
149 )))]
150 #[inline]
clock_nanosleep_absolute(id: ClockId, request: &Timespec) -> io::Result<()>151 pub(crate) fn clock_nanosleep_absolute(id: ClockId, request: &Timespec) -> io::Result<()> {
152     // Old 32-bit version: libc has `clock_nanosleep` but it is not y2038 safe
153     // by default. But there may be a `__clock_nanosleep_time64` we can use.
154     #[cfg(fix_y2038)]
155     {
156         #[cfg(target_env = "gnu")]
157         if let Some(libc_clock_nanosleep) = __clock_nanosleep_time64.get() {
158             let flags = c::TIMER_ABSTIME;
159             unsafe {
160                 return match {
161                     libc_clock_nanosleep(
162                         id as c::clockid_t,
163                         flags,
164                         &request.clone().into(),
165                         null_mut(),
166                     )
167                 } {
168                     0 => Ok(()),
169                     err => Err(io::Errno(err)),
170                 };
171             }
172         }
173 
174         clock_nanosleep_absolute_old(id, request)
175     }
176 
177     // Main version: libc is y2038 safe and has `clock_nanosleep`.
178     #[cfg(not(fix_y2038))]
179     {
180         let flags = c::TIMER_ABSTIME;
181 
182         match unsafe { c::clock_nanosleep(id as c::clockid_t, flags as _, request, null_mut()) } {
183             0 => Ok(()),
184             err => Err(io::Errno(err)),
185         }
186     }
187 }
188 
189 #[cfg(all(
190     fix_y2038,
191     not(any(
192         apple,
193         target_os = "emscripten",
194         target_os = "haiku",
195         target_os = "vita"
196     ))
197 ))]
clock_nanosleep_absolute_old(id: ClockId, request: &Timespec) -> io::Result<()>198 fn clock_nanosleep_absolute_old(id: ClockId, request: &Timespec) -> io::Result<()> {
199     let flags = c::TIMER_ABSTIME;
200 
201     let old_request = c::timespec {
202         tv_sec: request.tv_sec.try_into().map_err(|_| io::Errno::OVERFLOW)?,
203         tv_nsec: request.tv_nsec.try_into().map_err(|_| io::Errno::INVAL)?,
204     };
205     match unsafe { c::clock_nanosleep(id as c::clockid_t, flags, &old_request, null_mut()) } {
206         0 => Ok(()),
207         err => Err(io::Errno(err)),
208     }
209 }
210 
211 #[cfg(not(target_os = "redox"))]
212 #[inline]
nanosleep(request: &Timespec) -> NanosleepRelativeResult213 pub(crate) fn nanosleep(request: &Timespec) -> NanosleepRelativeResult {
214     // Old 32-bit version: libc has `nanosleep` but it is not y2038 safe by
215     // default. But there may be a `__nanosleep64` we can use.
216     #[cfg(fix_y2038)]
217     {
218         #[cfg(target_env = "gnu")]
219         if let Some(libc_nanosleep) = __nanosleep64.get() {
220             let mut remain = MaybeUninit::<LibcTimespec>::uninit();
221             unsafe {
222                 return match ret(libc_nanosleep(&request.clone().into(), remain.as_mut_ptr())) {
223                     Ok(()) => NanosleepRelativeResult::Ok,
224                     Err(io::Errno::INTR) => {
225                         NanosleepRelativeResult::Interrupted(remain.assume_init().into())
226                     }
227                     Err(err) => NanosleepRelativeResult::Err(err),
228                 };
229             }
230         }
231 
232         nanosleep_old(request)
233     }
234 
235     // Main version: libc is y2038 safe and has `nanosleep`.
236     #[cfg(not(fix_y2038))]
237     unsafe {
238         let mut remain = MaybeUninit::<Timespec>::uninit();
239 
240         match ret(c::nanosleep(request, remain.as_mut_ptr())) {
241             Ok(()) => NanosleepRelativeResult::Ok,
242             Err(io::Errno::INTR) => NanosleepRelativeResult::Interrupted(remain.assume_init()),
243             Err(err) => NanosleepRelativeResult::Err(err),
244         }
245     }
246 }
247 
248 #[cfg(fix_y2038)]
nanosleep_old(request: &Timespec) -> NanosleepRelativeResult249 fn nanosleep_old(request: &Timespec) -> NanosleepRelativeResult {
250     let tv_sec = match request.tv_sec.try_into() {
251         Ok(tv_sec) => tv_sec,
252         Err(_) => return NanosleepRelativeResult::Err(io::Errno::OVERFLOW),
253     };
254     let tv_nsec = match request.tv_nsec.try_into() {
255         Ok(tv_nsec) => tv_nsec,
256         Err(_) => return NanosleepRelativeResult::Err(io::Errno::INVAL),
257     };
258     let old_request = c::timespec { tv_sec, tv_nsec };
259     let mut old_remain = MaybeUninit::<c::timespec>::uninit();
260 
261     unsafe {
262         match ret(c::nanosleep(&old_request, old_remain.as_mut_ptr())) {
263             Ok(()) => NanosleepRelativeResult::Ok,
264             Err(io::Errno::INTR) => {
265                 let old_remain = old_remain.assume_init();
266                 let remain = Timespec {
267                     tv_sec: old_remain.tv_sec.into(),
268                     tv_nsec: old_remain.tv_nsec.into(),
269                 };
270                 NanosleepRelativeResult::Interrupted(remain)
271             }
272             Err(err) => NanosleepRelativeResult::Err(err),
273         }
274     }
275 }
276 
277 #[cfg(linux_kernel)]
278 #[inline]
279 #[must_use]
gettid() -> Pid280 pub(crate) fn gettid() -> Pid {
281     // `gettid` wasn't supported in glibc until 2.30, and musl until 1.2.2,
282     // so use `syscall`.
283     // <https://sourceware.org/bugzilla/show_bug.cgi?id=6399#c62>
284     weak_or_syscall! {
285         fn gettid() via SYS_gettid -> c::pid_t
286     }
287 
288     unsafe {
289         let tid = gettid();
290         Pid::from_raw_unchecked(tid)
291     }
292 }
293 
294 #[cfg(linux_kernel)]
295 #[inline]
setns(fd: BorrowedFd<'_>, nstype: c::c_int) -> io::Result<c::c_int>296 pub(crate) fn setns(fd: BorrowedFd<'_>, nstype: c::c_int) -> io::Result<c::c_int> {
297     // `setns` wasn't supported in glibc until 2.14, and musl until 0.9.5,
298     // so use `syscall`.
299     weak_or_syscall! {
300         fn setns(fd: c::c_int, nstype: c::c_int) via SYS_setns -> c::c_int
301     }
302 
303     unsafe { ret_c_int(setns(borrowed_fd(fd), nstype)) }
304 }
305 
306 #[cfg(linux_kernel)]
307 #[inline]
unshare(flags: crate::thread::UnshareFlags) -> io::Result<()>308 pub(crate) fn unshare(flags: crate::thread::UnshareFlags) -> io::Result<()> {
309     unsafe { ret(c::unshare(flags.bits() as i32)) }
310 }
311 
312 #[cfg(linux_kernel)]
313 #[inline]
capget( header: &mut linux_raw_sys::general::__user_cap_header_struct, data: &mut [MaybeUninit<linux_raw_sys::general::__user_cap_data_struct>], ) -> io::Result<()>314 pub(crate) fn capget(
315     header: &mut linux_raw_sys::general::__user_cap_header_struct,
316     data: &mut [MaybeUninit<linux_raw_sys::general::__user_cap_data_struct>],
317 ) -> io::Result<()> {
318     syscall! {
319         fn capget(
320             hdrp: *mut linux_raw_sys::general::__user_cap_header_struct,
321             data: *mut linux_raw_sys::general::__user_cap_data_struct
322         ) via SYS_capget -> c::c_int
323     }
324 
325     unsafe {
326         ret(capget(
327             as_mut_ptr(header),
328             data.as_mut_ptr()
329                 .cast::<linux_raw_sys::general::__user_cap_data_struct>(),
330         ))
331     }
332 }
333 
334 #[cfg(linux_kernel)]
335 #[inline]
capset( header: &mut linux_raw_sys::general::__user_cap_header_struct, data: &[linux_raw_sys::general::__user_cap_data_struct], ) -> io::Result<()>336 pub(crate) fn capset(
337     header: &mut linux_raw_sys::general::__user_cap_header_struct,
338     data: &[linux_raw_sys::general::__user_cap_data_struct],
339 ) -> io::Result<()> {
340     syscall! {
341         fn capset(
342             hdrp: *mut linux_raw_sys::general::__user_cap_header_struct,
343             data: *const linux_raw_sys::general::__user_cap_data_struct
344         ) via SYS_capset -> c::c_int
345     }
346 
347     unsafe { ret(capset(as_mut_ptr(header), data.as_ptr())) }
348 }
349 
350 #[cfg(linux_kernel)]
351 #[inline]
setuid_thread(uid: crate::ugid::Uid) -> io::Result<()>352 pub(crate) fn setuid_thread(uid: crate::ugid::Uid) -> io::Result<()> {
353     syscall! {
354         fn setuid(uid: c::uid_t) via SYS_setuid -> c::c_int
355     }
356 
357     unsafe { ret(setuid(uid.as_raw())) }
358 }
359 
360 #[cfg(linux_kernel)]
361 #[inline]
setresuid_thread( ruid: crate::ugid::Uid, euid: crate::ugid::Uid, suid: crate::ugid::Uid, ) -> io::Result<()>362 pub(crate) fn setresuid_thread(
363     ruid: crate::ugid::Uid,
364     euid: crate::ugid::Uid,
365     suid: crate::ugid::Uid,
366 ) -> io::Result<()> {
367     #[cfg(any(target_arch = "x86", target_arch = "arm", target_arch = "sparc"))]
368     const SYS: c::c_long = c::SYS_setresuid32 as c::c_long;
369     #[cfg(not(any(target_arch = "x86", target_arch = "arm", target_arch = "sparc")))]
370     const SYS: c::c_long = c::SYS_setresuid as c::c_long;
371 
372     syscall! {
373         fn setresuid(ruid: c::uid_t, euid: c::uid_t, suid: c::uid_t) via SYS -> c::c_int
374     }
375 
376     unsafe { ret(setresuid(ruid.as_raw(), euid.as_raw(), suid.as_raw())) }
377 }
378 
379 #[cfg(linux_kernel)]
380 #[inline]
setgid_thread(gid: crate::ugid::Gid) -> io::Result<()>381 pub(crate) fn setgid_thread(gid: crate::ugid::Gid) -> io::Result<()> {
382     syscall! {
383         fn setgid(gid: c::gid_t) via SYS_setgid -> c::c_int
384     }
385 
386     unsafe { ret(setgid(gid.as_raw())) }
387 }
388 
389 #[cfg(linux_kernel)]
390 #[inline]
setresgid_thread( rgid: crate::ugid::Gid, egid: crate::ugid::Gid, sgid: crate::ugid::Gid, ) -> io::Result<()>391 pub(crate) fn setresgid_thread(
392     rgid: crate::ugid::Gid,
393     egid: crate::ugid::Gid,
394     sgid: crate::ugid::Gid,
395 ) -> io::Result<()> {
396     #[cfg(any(target_arch = "x86", target_arch = "arm", target_arch = "sparc"))]
397     const SYS: c::c_long = c::SYS_setresgid32 as c::c_long;
398     #[cfg(not(any(target_arch = "x86", target_arch = "arm", target_arch = "sparc")))]
399     const SYS: c::c_long = c::SYS_setresgid as c::c_long;
400 
401     syscall! {
402         fn setresgid(rgid: c::gid_t, egid: c::gid_t, sgid: c::gid_t) via SYS -> c::c_int
403     }
404 
405     unsafe { ret(setresgid(rgid.as_raw(), egid.as_raw(), sgid.as_raw())) }
406 }
407 
408 // TODO: This could be de-multiplexed.
409 #[cfg(linux_kernel)]
futex( uaddr: *mut u32, op: FutexOperation, flags: FutexFlags, val: u32, utime: *const Timespec, uaddr2: *mut u32, val3: u32, ) -> io::Result<usize>410 pub(crate) unsafe fn futex(
411     uaddr: *mut u32,
412     op: FutexOperation,
413     flags: FutexFlags,
414     val: u32,
415     utime: *const Timespec,
416     uaddr2: *mut u32,
417     val3: u32,
418 ) -> io::Result<usize> {
419     #[cfg(all(
420         target_pointer_width = "32",
421         not(any(target_arch = "aarch64", target_arch = "x86_64"))
422     ))]
423     {
424         // TODO: Upstream this to the libc crate.
425         #[allow(non_upper_case_globals)]
426         const SYS_futex_time64: i32 = linux_raw_sys::general::__NR_futex_time64 as i32;
427 
428         syscall! {
429             fn futex_time64(
430                 uaddr: *mut u32,
431                 futex_op: c::c_int,
432                 val: u32,
433                 timeout: *const Timespec,
434                 uaddr2: *mut u32,
435                 val3: u32
436             ) via SYS_futex_time64 -> c::ssize_t
437         }
438 
439         ret_usize(futex_time64(
440             uaddr,
441             op as i32 | flags.bits() as i32,
442             val,
443             utime,
444             uaddr2,
445             val3,
446         ))
447         .or_else(|err| {
448             // See the comments in `rustix_clock_gettime_via_syscall` about
449             // emulation.
450             if err == io::Errno::NOSYS {
451                 futex_old(uaddr, op, flags, val, utime, uaddr2, val3)
452             } else {
453                 Err(err)
454             }
455         })
456     }
457 
458     #[cfg(any(
459         target_pointer_width = "64",
460         target_arch = "aarch64",
461         target_arch = "x86_64"
462     ))]
463     {
464         syscall! {
465             fn futex(
466                 uaddr: *mut u32,
467                 futex_op: c::c_int,
468                 val: u32,
469                 timeout: *const linux_raw_sys::general::__kernel_timespec,
470                 uaddr2: *mut u32,
471                 val3: u32
472             ) via SYS_futex -> c::c_long
473         }
474 
475         ret_usize(futex(
476             uaddr,
477             op as i32 | flags.bits() as i32,
478             val,
479             utime.cast(),
480             uaddr2,
481             val3,
482         ) as isize)
483     }
484 }
485 
486 #[cfg(linux_kernel)]
487 #[cfg(all(
488     target_pointer_width = "32",
489     not(any(target_arch = "aarch64", target_arch = "x86_64"))
490 ))]
futex_old( uaddr: *mut u32, op: FutexOperation, flags: FutexFlags, val: u32, utime: *const Timespec, uaddr2: *mut u32, val3: u32, ) -> io::Result<usize>491 unsafe fn futex_old(
492     uaddr: *mut u32,
493     op: FutexOperation,
494     flags: FutexFlags,
495     val: u32,
496     utime: *const Timespec,
497     uaddr2: *mut u32,
498     val3: u32,
499 ) -> io::Result<usize> {
500     syscall! {
501         fn futex(
502             uaddr: *mut u32,
503             futex_op: c::c_int,
504             val: u32,
505             timeout: *const linux_raw_sys::general::__kernel_old_timespec,
506             uaddr2: *mut u32,
507             val3: u32
508         ) via SYS_futex -> c::c_long
509     }
510 
511     let old_utime = linux_raw_sys::general::__kernel_old_timespec {
512         tv_sec: (*utime).tv_sec.try_into().map_err(|_| io::Errno::INVAL)?,
513         tv_nsec: (*utime).tv_nsec.try_into().map_err(|_| io::Errno::INVAL)?,
514     };
515     ret_usize(futex(
516         uaddr,
517         op as i32 | flags.bits() as i32,
518         val,
519         &old_utime,
520         uaddr2,
521         val3,
522     ) as isize)
523 }
524