1 | //! linux_raw syscalls supporting `rustix::thread`. |
2 | //! |
3 | //! # Safety |
4 | //! |
5 | //! See the `rustix::backend` module documentation for details. |
6 | #![allow (unsafe_code, clippy::undocumented_unsafe_blocks)] |
7 | |
8 | use crate::backend::c; |
9 | use crate::backend::conv::{ |
10 | by_mut, by_ref, c_int, c_uint, ret, ret_c_int, ret_c_int_infallible, ret_usize, slice, |
11 | slice_just_addr, slice_just_addr_mut, zero, |
12 | }; |
13 | use crate::fd::BorrowedFd; |
14 | use crate::io; |
15 | use crate::pid::Pid; |
16 | use crate::thread::{ClockId, FutexFlags, FutexOperation, NanosleepRelativeResult, Timespec}; |
17 | use core::mem::MaybeUninit; |
18 | #[cfg (target_pointer_width = "32" )] |
19 | use linux_raw_sys::general::timespec as __kernel_old_timespec; |
20 | use linux_raw_sys::general::{__kernel_timespec, TIMER_ABSTIME}; |
21 | |
22 | #[inline ] |
23 | pub(crate) fn clock_nanosleep_relative( |
24 | id: ClockId, |
25 | req: &__kernel_timespec, |
26 | ) -> NanosleepRelativeResult { |
27 | #[cfg (target_pointer_width = "32" )] |
28 | unsafe { |
29 | let mut rem = MaybeUninit::<__kernel_timespec>::uninit(); |
30 | match ret(syscall!( |
31 | __NR_clock_nanosleep_time64, |
32 | id, |
33 | c_int(0), |
34 | by_ref(req), |
35 | &mut rem |
36 | )) |
37 | .or_else(|err| { |
38 | // See the comments in `rustix_clock_gettime_via_syscall` about |
39 | // emulation. |
40 | if err == io::Errno::NOSYS { |
41 | clock_nanosleep_relative_old(id, req, &mut rem) |
42 | } else { |
43 | Err(err) |
44 | } |
45 | }) { |
46 | Ok(()) => NanosleepRelativeResult::Ok, |
47 | Err(io::Errno::INTR) => NanosleepRelativeResult::Interrupted(rem.assume_init()), |
48 | Err(err) => NanosleepRelativeResult::Err(err), |
49 | } |
50 | } |
51 | #[cfg (target_pointer_width = "64" )] |
52 | unsafe { |
53 | let mut rem = MaybeUninit::<__kernel_timespec>::uninit(); |
54 | match ret(syscall!( |
55 | __NR_clock_nanosleep, |
56 | id, |
57 | c_int(0), |
58 | by_ref(req), |
59 | &mut rem |
60 | )) { |
61 | Ok(()) => NanosleepRelativeResult::Ok, |
62 | Err(io::Errno::INTR) => NanosleepRelativeResult::Interrupted(rem.assume_init()), |
63 | Err(err) => NanosleepRelativeResult::Err(err), |
64 | } |
65 | } |
66 | } |
67 | |
68 | #[cfg (target_pointer_width = "32" )] |
69 | unsafe fn clock_nanosleep_relative_old( |
70 | id: ClockId, |
71 | req: &__kernel_timespec, |
72 | rem: &mut MaybeUninit<__kernel_timespec>, |
73 | ) -> io::Result<()> { |
74 | let old_req = __kernel_old_timespec { |
75 | tv_sec: req.tv_sec.try_into().map_err(|_| io::Errno::INVAL)?, |
76 | tv_nsec: req.tv_nsec.try_into().map_err(|_| io::Errno::INVAL)?, |
77 | }; |
78 | let mut old_rem = MaybeUninit::<__kernel_old_timespec>::uninit(); |
79 | ret(syscall!( |
80 | __NR_clock_nanosleep, |
81 | id, |
82 | c_int(0), |
83 | by_ref(&old_req), |
84 | &mut old_rem |
85 | ))?; |
86 | let old_rem = old_rem.assume_init(); |
87 | rem.write(__kernel_timespec { |
88 | tv_sec: old_rem.tv_sec.into(), |
89 | tv_nsec: old_rem.tv_nsec.into(), |
90 | }); |
91 | Ok(()) |
92 | } |
93 | |
94 | #[inline ] |
95 | pub(crate) fn clock_nanosleep_absolute(id: ClockId, req: &__kernel_timespec) -> io::Result<()> { |
96 | #[cfg (target_pointer_width = "32" )] |
97 | unsafe { |
98 | ret(syscall_readonly!( |
99 | __NR_clock_nanosleep_time64, |
100 | id, |
101 | c_uint(TIMER_ABSTIME), |
102 | by_ref(req), |
103 | zero() |
104 | )) |
105 | .or_else(|err| { |
106 | // See the comments in `rustix_clock_gettime_via_syscall` about |
107 | // emulation. |
108 | if err == io::Errno::NOSYS { |
109 | clock_nanosleep_absolute_old(id, req) |
110 | } else { |
111 | Err(err) |
112 | } |
113 | }) |
114 | } |
115 | #[cfg (target_pointer_width = "64" )] |
116 | unsafe { |
117 | ret(syscall_readonly!( |
118 | __NR_clock_nanosleep, |
119 | id, |
120 | c_uint(TIMER_ABSTIME), |
121 | by_ref(req), |
122 | zero() |
123 | )) |
124 | } |
125 | } |
126 | |
127 | #[cfg (target_pointer_width = "32" )] |
128 | unsafe fn clock_nanosleep_absolute_old(id: ClockId, req: &__kernel_timespec) -> io::Result<()> { |
129 | let old_req = __kernel_old_timespec { |
130 | tv_sec: req.tv_sec.try_into().map_err(|_| io::Errno::INVAL)?, |
131 | tv_nsec: req.tv_nsec.try_into().map_err(|_| io::Errno::INVAL)?, |
132 | }; |
133 | ret(syscall_readonly!( |
134 | __NR_clock_nanosleep, |
135 | id, |
136 | c_int(0), |
137 | by_ref(&old_req), |
138 | zero() |
139 | )) |
140 | } |
141 | |
142 | #[inline ] |
143 | pub(crate) fn nanosleep(req: &__kernel_timespec) -> NanosleepRelativeResult { |
144 | #[cfg (target_pointer_width = "32" )] |
145 | unsafe { |
146 | let mut rem = MaybeUninit::<__kernel_timespec>::uninit(); |
147 | match ret(syscall!( |
148 | __NR_clock_nanosleep_time64, |
149 | ClockId::Realtime, |
150 | c_int(0), |
151 | by_ref(req), |
152 | &mut rem |
153 | )) |
154 | .or_else(|err| { |
155 | // See the comments in `rustix_clock_gettime_via_syscall` about |
156 | // emulation. |
157 | if err == io::Errno::NOSYS { |
158 | nanosleep_old(req, &mut rem) |
159 | } else { |
160 | Err(err) |
161 | } |
162 | }) { |
163 | Ok(()) => NanosleepRelativeResult::Ok, |
164 | Err(io::Errno::INTR) => NanosleepRelativeResult::Interrupted(rem.assume_init()), |
165 | Err(err) => NanosleepRelativeResult::Err(err), |
166 | } |
167 | } |
168 | #[cfg (target_pointer_width = "64" )] |
169 | unsafe { |
170 | let mut rem = MaybeUninit::<__kernel_timespec>::uninit(); |
171 | match ret(syscall!(__NR_nanosleep, by_ref(req), &mut rem)) { |
172 | Ok(()) => NanosleepRelativeResult::Ok, |
173 | Err(io::Errno::INTR) => NanosleepRelativeResult::Interrupted(rem.assume_init()), |
174 | Err(err) => NanosleepRelativeResult::Err(err), |
175 | } |
176 | } |
177 | } |
178 | |
179 | #[cfg (target_pointer_width = "32" )] |
180 | unsafe fn nanosleep_old( |
181 | req: &__kernel_timespec, |
182 | rem: &mut MaybeUninit<__kernel_timespec>, |
183 | ) -> io::Result<()> { |
184 | let old_req = __kernel_old_timespec { |
185 | tv_sec: req.tv_sec.try_into().map_err(|_| io::Errno::INVAL)?, |
186 | tv_nsec: req.tv_nsec.try_into().map_err(|_| io::Errno::INVAL)?, |
187 | }; |
188 | let mut old_rem = MaybeUninit::<__kernel_old_timespec>::uninit(); |
189 | ret(syscall!(__NR_nanosleep, by_ref(&old_req), &mut old_rem))?; |
190 | let old_rem = old_rem.assume_init(); |
191 | rem.write(__kernel_timespec { |
192 | tv_sec: old_rem.tv_sec.into(), |
193 | tv_nsec: old_rem.tv_nsec.into(), |
194 | }); |
195 | Ok(()) |
196 | } |
197 | |
198 | #[inline ] |
199 | pub(crate) fn gettid() -> Pid { |
200 | unsafe { |
201 | let tid: i32 = ret_c_int_infallible(raw:syscall_readonly!(__NR_gettid)); |
202 | Pid::from_raw_unchecked(raw:tid) |
203 | } |
204 | } |
205 | |
206 | // TODO: This could be de-multiplexed. |
207 | #[inline ] |
208 | pub(crate) unsafe fn futex( |
209 | uaddr: *mut u32, |
210 | op: FutexOperation, |
211 | flags: FutexFlags, |
212 | val: u32, |
213 | utime: *const Timespec, |
214 | uaddr2: *mut u32, |
215 | val3: u32, |
216 | ) -> io::Result<usize> { |
217 | #[cfg (target_pointer_width = "32" )] |
218 | { |
219 | ret_usize(syscall!( |
220 | __NR_futex_time64, |
221 | uaddr, |
222 | (op, flags), |
223 | c_uint(val), |
224 | utime, |
225 | uaddr2, |
226 | c_uint(val3) |
227 | )) |
228 | .or_else(|err| { |
229 | // See the comments in `rustix_clock_gettime_via_syscall` about |
230 | // emulation. |
231 | if err == io::Errno::NOSYS { |
232 | futex_old(uaddr, op, flags, val, utime, uaddr2, val3) |
233 | } else { |
234 | Err(err) |
235 | } |
236 | }) |
237 | } |
238 | #[cfg (target_pointer_width = "64" )] |
239 | ret_usize(syscall!( |
240 | __NR_futex, |
241 | uaddr, |
242 | (op, flags), |
243 | c_uint(val), |
244 | utime, |
245 | uaddr2, |
246 | c_uint(val3) |
247 | )) |
248 | } |
249 | |
250 | #[cfg (target_pointer_width = "32" )] |
251 | unsafe fn futex_old( |
252 | uaddr: *mut u32, |
253 | op: FutexOperation, |
254 | flags: FutexFlags, |
255 | val: u32, |
256 | utime: *const Timespec, |
257 | uaddr2: *mut u32, |
258 | val3: u32, |
259 | ) -> io::Result<usize> { |
260 | let old_utime = __kernel_old_timespec { |
261 | tv_sec: (*utime).tv_sec.try_into().map_err(|_| io::Errno::INVAL)?, |
262 | tv_nsec: (*utime).tv_nsec.try_into().map_err(|_| io::Errno::INVAL)?, |
263 | }; |
264 | ret_usize(syscall!( |
265 | __NR_futex, |
266 | uaddr, |
267 | (op, flags), |
268 | c_uint(val), |
269 | by_ref(&old_utime), |
270 | uaddr2, |
271 | c_uint(val3) |
272 | )) |
273 | } |
274 | |
275 | #[inline ] |
276 | pub(crate) fn setns(fd: BorrowedFd<'_>, nstype: c::c_int) -> io::Result<c::c_int> { |
277 | unsafe { ret_c_int(raw:syscall_readonly!(__NR_setns, fd, c_int(nstype))) } |
278 | } |
279 | |
280 | #[inline ] |
281 | pub(crate) fn unshare(flags: crate::thread::UnshareFlags) -> io::Result<()> { |
282 | unsafe { ret(raw:syscall_readonly!(__NR_unshare, flags)) } |
283 | } |
284 | |
285 | #[inline ] |
286 | pub(crate) fn capget( |
287 | header: &mut linux_raw_sys::general::__user_cap_header_struct, |
288 | data: &mut [MaybeUninit<linux_raw_sys::general::__user_cap_data_struct>], |
289 | ) -> io::Result<()> { |
290 | unsafe { |
291 | ret(raw:syscall!( |
292 | __NR_capget, |
293 | by_mut(header), |
294 | slice_just_addr_mut(data) |
295 | )) |
296 | } |
297 | } |
298 | |
299 | #[inline ] |
300 | pub(crate) fn capset( |
301 | header: &mut linux_raw_sys::general::__user_cap_header_struct, |
302 | data: &[linux_raw_sys::general::__user_cap_data_struct], |
303 | ) -> io::Result<()> { |
304 | unsafe { ret(raw:syscall!(__NR_capset, by_mut(header), slice_just_addr(data))) } |
305 | } |
306 | |
307 | #[inline ] |
308 | pub(crate) fn setuid_thread(uid: crate::ugid::Uid) -> io::Result<()> { |
309 | unsafe { ret(raw:syscall_readonly!(__NR_setuid, uid)) } |
310 | } |
311 | |
312 | #[inline ] |
313 | pub(crate) fn setresuid_thread( |
314 | ruid: crate::ugid::Uid, |
315 | euid: crate::ugid::Uid, |
316 | suid: crate::ugid::Uid, |
317 | ) -> io::Result<()> { |
318 | #[cfg (any(target_arch = "x86" , target_arch = "arm" , target_arch = "sparc" ))] |
319 | unsafe { |
320 | ret(syscall_readonly!(__NR_setresuid32, ruid, euid, suid)) |
321 | } |
322 | #[cfg (not(any(target_arch = "x86" , target_arch = "arm" , target_arch = "sparc" )))] |
323 | unsafe { |
324 | ret(raw:syscall_readonly!(__NR_setresuid, ruid, euid, suid)) |
325 | } |
326 | } |
327 | |
328 | #[inline ] |
329 | pub(crate) fn setgid_thread(gid: crate::ugid::Gid) -> io::Result<()> { |
330 | unsafe { ret(raw:syscall_readonly!(__NR_setgid, gid)) } |
331 | } |
332 | |
333 | #[inline ] |
334 | pub(crate) fn setresgid_thread( |
335 | rgid: crate::ugid::Gid, |
336 | egid: crate::ugid::Gid, |
337 | sgid: crate::ugid::Gid, |
338 | ) -> io::Result<()> { |
339 | #[cfg (any(target_arch = "x86" , target_arch = "arm" , target_arch = "sparc" ))] |
340 | unsafe { |
341 | ret(syscall_readonly!(__NR_setresgid32, rgid, egid, sgid)) |
342 | } |
343 | #[cfg (not(any(target_arch = "x86" , target_arch = "arm" , target_arch = "sparc" )))] |
344 | unsafe { |
345 | ret(raw:syscall_readonly!(__NR_setresgid, rgid, egid, sgid)) |
346 | } |
347 | } |
348 | |
349 | #[inline ] |
350 | pub(crate) fn setgroups_thread(gids: &[crate::ugid::Gid]) -> io::Result<()> { |
351 | let (addr: ArgReg<'_, A1>, len: ArgReg<'_, A0>) = slice(gids); |
352 | unsafe { ret(raw:syscall_readonly!(__NR_setgroups, len, addr)) } |
353 | } |
354 | |