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