r_linux/syscall/raw.rs
1//! Raw System Calls
2//!
3//! This module provides raw and direct access to system calls on linux
4//! platforms. It exports 7 different functions, one for each possible number
5//! of arguments you can pass to a syscall (`syscall0` through `syscall6`). It
6//! is always safe to use `syscall6()` and set unused arguments to any value.
7//! For performance reasons, you might want to prefer the matching call,
8//! though.
9//!
10//! This implementation is optimized to allow inlining of the system-call
11//! invocation into the calling function. That is, when xLTO is used, the
12//! syscall setup and instruction will be inlined into the caller, and thus
13//! allows fast and efficient kernel calls. On common architectures, the inline
14//! assembly is now stable rust, so no cross-language LTO is needed, anyway.
15//!
16//! Linux system calls take between 0 and 6 arguments, each argument is passed
17//! as a native integer. Furthermore, every system call has a return value,
18//! which also is a native integer. Depending on the platform you run on, the
19//! actual underlying datatype will have different size constraints (e.g., on
20//! 32bit machines all arguments are usually 32bit integers, but on 64bit
21//! machines you pass 64bit integers). You should consult the documentation of
22//! each system call to understand how individual arguments are passed. Be
23//! warned, there are even system calls that flip argument order depending on
24//! the architecture (for historical reasons, trying to provide binary
25//! compatibility to existing platforms). Use the wrapper definitions to get a
26//! verified function prototype for each system call.
27//!
28//! The return value of a system call is limited to a native integer.
29//! Furthermore, 4096 values are reserved for error codes. For most syscalls
30//! it is enough to interpret the return value as signed integer and consider
31//! any negative value as error. However, in some special cases this is not
32//! correct. Therefore, the `Retval` type provides small accessors to check
33//! whether the return value is an error code or not. If performance is not
34//! a concern, it also provides a conversion to `Result`.
35
36/// System Call Return Value
37///
38/// On linux platforms, system calls return native integers as result. A
39/// special range is used for errors. This type wraps this result type and
40/// provides accessors to its underlying values.
41#[repr(C)]
42#[derive(Copy, Clone, Debug, PartialEq, Eq)]
43pub struct Retval(usize);
44
45impl Retval {
46 /// Create a new return-value from raw data
47 pub const fn from_usize(v: usize) -> Retval {
48 Retval(v)
49 }
50
51 /// Return raw underlying data of a return-value
52 pub const fn as_usize(self) -> usize {
53 self.0
54 }
55
56 /// Check whether this is an error-return
57 pub const fn is_error(self) -> bool {
58 self.0 > core::usize::MAX - 4096
59 }
60
61 /// Check whether this is a success-return
62 pub const fn is_success(self) -> bool {
63 !self.is_error()
64 }
65
66 /// Return the error-code unchecked
67 ///
68 /// # Safety
69 ///
70 /// This does not verify that `self` is actually an error-return. It
71 /// assumes the caller verified it.
72 pub unsafe fn error_unchecked(self) -> usize {
73 !self.0 + 1
74 }
75
76 /// Return the error-code
77 ///
78 /// If `self` is not actually an error-return, this will panic.
79 pub fn error(self) -> usize {
80 if self.is_error() {
81 unsafe { self.error_unchecked() }
82 } else {
83 panic!("called `r_linux::syscall::raw::Retval::error()` on a success value")
84 }
85 }
86
87 /// Return the success-value unchecked
88 ///
89 /// # Safety
90 ///
91 /// This does not verify that `self` is actually a success-return. It
92 /// assumes the caller verified it.
93 pub unsafe fn unwrap_unchecked(self) -> usize {
94 self.0
95 }
96
97 /// Return the success value
98 ///
99 /// If `self` is not a success-return, this will panic.
100 pub fn unwrap(self) -> usize {
101 if self.is_success() {
102 unsafe { self.unwrap_unchecked() }
103 } else {
104 panic!("called `r_linux::syscall::raw::Retval::unwrap()` on an error value")
105 }
106 }
107
108 /// Convert into a Result
109 ///
110 /// This converts the return value into a rust-native Result type. This
111 /// maps the error-return to `Err(code)` and the success-return
112 /// to `Ok(usize)`. This allows using the rich convenience library of the
113 /// `Result` type, rather than re-implementing them for this native type.
114 pub fn to_result(self) -> Result<usize, usize> {
115 if self.is_error() {
116 Err(!self.0 + 1)
117 } else {
118 Ok(self.0)
119 }
120 }
121}
122
123/// Invoke System Call With 0 Arguments
124///
125/// This invokes the system call with the specified system-call-number. No
126/// arguments are passed to the system call.
127///
128/// # Safety
129///
130/// * System calls can have arbitrary side-effects. It is the responsibility of
131/// the caller to consider all effects of a system call and take required
132/// precautions.
133pub unsafe fn syscall0(
134 nr: usize,
135) -> Retval {
136 Retval::from_usize(
137 super::arch::native::syscall::syscall0(
138 nr,
139 )
140 )
141}
142
143/// Invoke System Call With 1 Argument
144///
145/// This invokes the system call with the specified system-call-number. The
146/// provided argument is passed to the system call unmodified.
147///
148/// # Safety
149///
150/// * System calls can have arbitrary side-effects. It is the responsibility of
151/// the caller to consider all effects of a system call and take required
152/// precautions.
153pub unsafe fn syscall1(
154 nr: usize,
155 arg0: usize,
156) -> Retval {
157 Retval::from_usize(
158 super::arch::native::syscall::syscall1(
159 nr,
160 arg0,
161 )
162 )
163}
164
165/// Invoke System Call With 2 Arguments
166///
167/// This invokes the system call with the specified system-call-number. The
168/// provided arguments are passed to the system call unmodified.
169///
170/// # Safety
171///
172/// * System calls can have arbitrary side-effects. It is the responsibility of
173/// the caller to consider all effects of a system call and take required
174/// precautions.
175pub unsafe fn syscall2(
176 nr: usize,
177 arg0: usize,
178 arg1: usize,
179) -> Retval {
180 Retval::from_usize(
181 super::arch::native::syscall::syscall2(
182 nr,
183 arg0,
184 arg1,
185 )
186 )
187}
188
189/// Invoke System Call With 3 Arguments
190///
191/// This invokes the system call with the specified system-call-number. The
192/// provided arguments are passed to the system call unmodified.
193///
194/// # Safety
195///
196/// * System calls can have arbitrary side-effects. It is the responsibility of
197/// the caller to consider all effects of a system call and take required
198/// precautions.
199pub unsafe fn syscall3(
200 nr: usize,
201 arg0: usize,
202 arg1: usize,
203 arg2: usize,
204) -> Retval {
205 Retval::from_usize(
206 super::arch::native::syscall::syscall3(
207 nr,
208 arg0,
209 arg1,
210 arg2,
211 )
212 )
213}
214
215/// Invoke System Call With 4 Arguments
216///
217/// This invokes the system call with the specified system-call-number. The
218/// provided arguments are passed to the system call unmodified.
219///
220/// # Safety
221///
222/// * System calls can have arbitrary side-effects. It is the responsibility of
223/// the caller to consider all effects of a system call and take required
224/// precautions.
225pub unsafe fn syscall4(
226 nr: usize,
227 arg0: usize,
228 arg1: usize,
229 arg2: usize,
230 arg3: usize,
231) -> Retval {
232 Retval::from_usize(
233 super::arch::native::syscall::syscall4(
234 nr,
235 arg0,
236 arg1,
237 arg2,
238 arg3,
239 )
240 )
241}
242
243/// Invoke System Call With 5 Arguments
244///
245/// This invokes the system call with the specified system-call-number. The
246/// provided arguments are passed to the system call unmodified.
247///
248/// # Safety
249///
250/// * System calls can have arbitrary side-effects. It is the responsibility of
251/// the caller to consider all effects of a system call and take required
252/// precautions.
253pub unsafe fn syscall5(
254 nr: usize,
255 arg0: usize,
256 arg1: usize,
257 arg2: usize,
258 arg3: usize,
259 arg4: usize,
260) -> Retval {
261 Retval::from_usize(
262 super::arch::native::syscall::syscall5(
263 nr,
264 arg0,
265 arg1,
266 arg2,
267 arg3,
268 arg4,
269 )
270 )
271}
272
273/// Invoke System Call With 6 Arguments
274///
275/// This invokes the system call with the specified system-call-number. The
276/// provided arguments are passed to the system call unmodified.
277///
278/// # Safety
279///
280/// * System calls can have arbitrary side-effects. It is the responsibility of
281/// the caller to consider all effects of a system call and take required
282/// precautions.
283pub unsafe fn syscall6(
284 nr: usize,
285 arg0: usize,
286 arg1: usize,
287 arg2: usize,
288 arg3: usize,
289 arg4: usize,
290 arg5: usize,
291) -> Retval {
292 Retval::from_usize(
293 super::arch::native::syscall::syscall6(
294 nr,
295 arg0,
296 arg1,
297 arg2,
298 arg3,
299 arg4,
300 arg5,
301 )
302 )
303}
304
305#[cfg(test)]
306mod test {
307 use super::*;
308
309 #[test]
310 fn retval_check() {
311 //
312 // Check basic functionality of the `Retval` type and verify it has
313 // the same semantics as the system call ABI.
314 //
315
316 let success_values = [
317 0, 1, 2, 3,
318 254, 255, 256, 257,
319 65534, 65535, 65536, 65537,
320 core::usize::MAX / 2,
321 core::usize::MAX / 2 + 1,
322 core::usize::MAX - 4097,
323 core::usize::MAX - 4096,
324 ];
325
326 for v in &success_values {
327 let r = Retval::from_usize(*v);
328
329 assert_eq!(r, Retval(*v));
330 assert_eq!(r.as_usize(), *v);
331 assert_eq!(r.is_success(), true);
332 assert_eq!(r.is_error(), false);
333 assert_eq!(unsafe { r.unwrap_unchecked() }, *v);
334 assert_eq!(r.unwrap(), *v);
335 assert_eq!(r.to_result(), Ok(*v));
336 }
337
338 let error_values = [
339 (4096, core::usize::MAX - 4095),
340 (4095, core::usize::MAX - 4094),
341 (4094, core::usize::MAX - 4093),
342 (4093, core::usize::MAX - 4092),
343 (4, core::usize::MAX - 3),
344 (3, core::usize::MAX - 2),
345 (2, core::usize::MAX - 1),
346 (1, core::usize::MAX),
347 ];
348
349 for (c, v) in &error_values {
350 let r = Retval::from_usize(*v);
351
352 assert_eq!(r, Retval(*v));
353 assert_eq!(r.as_usize(), *v);
354 assert_eq!(r.is_success(), false);
355 assert_eq!(r.is_error(), true);
356 assert_eq!(unsafe { r.error_unchecked() }, *c);
357 assert_eq!(r.error(), *c);
358 assert_eq!(r.to_result(), Err(*c));
359 }
360
361 let r = Retval::from_usize(71);
362
363 // verify copy behavior
364 let copy = r;
365 assert_ne!(© as *const _, &r as *const _);
366 assert_eq!(copy, r);
367
368 // verify clone support
369 let clone = r.clone();
370 assert_ne!(&clone as *const _, &r as *const _);
371 assert_eq!(clone, r);
372
373 // verify comparisons
374 let (com1, com2) = (Retval::from_usize(71), Retval::from_usize(72));
375 assert_eq!(r, com1);
376 assert_ne!(r, com2);
377 }
378
379 #[test]
380 #[should_panic]
381 fn retval_error_panic() {
382 //
383 // Verify `Retval::error()` panics on success-values.
384 //
385
386 Retval::from_usize(0).error();
387 }
388
389 #[test]
390 #[should_panic]
391 fn retval_unwrap_panic() {
392 //
393 // Verify `Retval::unwrap()` panics on error-values.
394 //
395
396 Retval::from_usize(core::usize::MAX).unwrap();
397 }
398
399 #[test]
400 fn syscall0_check() {
401 //
402 // Test validity of `syscall0()`.
403 //
404 // Tested syscall: GETPID
405 //
406
407 let r0 = unsafe { syscall0(crate::syscall::arch::native::nr::GETPID) };
408 assert_eq!(r0.unwrap() as u32, std::process::id());
409 }
410
411 #[test]
412 fn syscall1_check() {
413 //
414 // Test validity of `syscall1()`.
415 //
416 // Tested syscall: CLOSE
417 //
418 // We run `pipe2()` and verify the `close()` syscall accepts the values
419 // without complaint.
420 //
421
422 let mut p0: [u32; 2] = [0, 0];
423
424 let r0 = unsafe {
425 syscall2(
426 crate::syscall::arch::native::nr::PIPE2,
427 p0.as_mut_ptr() as usize,
428 0,
429 ).unwrap()
430 };
431 assert_eq!(r0, 0);
432 assert!(p0[0] > 2);
433 assert!(p0[1] > 2);
434 assert_ne!(p0[0], p0[1]);
435
436 let r0 = unsafe {
437 syscall1(
438 crate::syscall::arch::native::nr::CLOSE,
439 p0[0] as usize,
440 ).unwrap()
441 };
442 assert_eq!(r0, 0);
443 let r0 = unsafe {
444 syscall1(
445 crate::syscall::arch::native::nr::CLOSE,
446 p0[1] as usize,
447 ).unwrap()
448 };
449 assert_eq!(r0, 0);
450 }
451
452 #[test]
453 fn syscall2_check() {
454 //
455 // Test validity of `syscall2()`.
456 //
457 // Tested syscall: PIPE2
458 //
459 // We run `pipe2()` and verify the `close()` syscall accepts the values
460 // without complaint.
461 //
462
463 let mut p0: [u32; 2] = [0, 0];
464
465 let r0 = unsafe {
466 syscall2(
467 crate::syscall::arch::native::nr::PIPE2,
468 p0.as_mut_ptr() as usize,
469 0,
470 ).unwrap()
471 };
472 assert_eq!(r0, 0);
473 assert!(p0[0] > 2);
474 assert!(p0[1] > 2);
475 assert_ne!(p0[0], p0[1]);
476
477 let r0 = unsafe {
478 syscall1(
479 crate::syscall::arch::native::nr::CLOSE,
480 p0[0] as usize,
481 ).unwrap()
482 };
483 assert_eq!(r0, 0);
484 let r0 = unsafe {
485 syscall1(
486 crate::syscall::arch::native::nr::CLOSE,
487 p0[1] as usize,
488 ).unwrap()
489 };
490 assert_eq!(r0, 0);
491 }
492
493 #[test]
494 fn syscall3_check() {
495 //
496 // Test validity of `syscall3()`.
497 //
498 // Tested syscall: WRITE / READ
499 //
500 // We create a pipe, write to one end and verify we can read the same
501 // data from the other end.
502 //
503
504 let mut p0: [u32; 2] = [0, 0];
505 let mut b0: [u8; 16] = [0; 16];
506
507 let r0 = unsafe {
508 syscall2(
509 crate::syscall::arch::native::nr::PIPE2,
510 p0.as_mut_ptr() as usize,
511 0,
512 ).unwrap()
513 };
514 assert_eq!(r0, 0);
515 assert!(p0[0] > 2);
516 assert!(p0[1] > 2);
517 assert_ne!(p0[0], p0[1]);
518
519 let r0 = unsafe {
520 syscall3(
521 crate::syscall::arch::native::nr::WRITE,
522 p0[1] as usize,
523 "foobar".as_ptr() as usize,
524 6 as usize,
525 )
526 .unwrap()
527 };
528 assert_eq!(r0, 6);
529
530 let r0 = unsafe {
531 syscall3(
532 crate::syscall::arch::native::nr::READ,
533 p0[0] as usize,
534 b0.as_mut_ptr() as usize,
535 6 as usize,
536 )
537 .unwrap()
538 };
539 assert_eq!(r0, 6);
540 assert_eq!(core::str::from_utf8(&b0[..6]), Ok("foobar"));
541
542 let r0 = unsafe {
543 syscall1(
544 crate::syscall::arch::native::nr::CLOSE,
545 p0[0] as usize,
546 ).unwrap()
547 };
548 assert_eq!(r0, 0);
549 let r0 = unsafe {
550 syscall1(
551 crate::syscall::arch::native::nr::CLOSE,
552 p0[1] as usize,
553 ).unwrap()
554 };
555 assert_eq!(r0, 0);
556 }
557
558 #[test]
559 fn syscall4_check() {
560 //
561 // Test validity of `syscall4()`.
562 //
563 // Tested syscall: READLINKAT
564 //
565 // We create a memfd and then query `/proc` for the link-value of the
566 // memfd. This is ABI and needs to be the (annotated) name that we
567 // passed to `memfd_create()`.
568 //
569
570 let mut b0: [u8; 128] = [0; 128];
571
572 let f0 = unsafe {
573 syscall2(
574 crate::syscall::arch::native::nr::MEMFD_CREATE,
575 "foobar\x00".as_ptr() as usize,
576 0,
577 ).unwrap()
578 };
579 assert!(f0 > 2);
580
581 let r0 = unsafe {
582 syscall4(
583 crate::syscall::arch::native::nr::READLINKAT,
584 core::usize::MAX - 100 + 1, // AT_FDCWD
585 format!("/proc/self/fd/{}\x00", f0).as_str().as_ptr() as usize,
586 b0.as_mut_ptr() as usize,
587 128 - 1,
588 )
589 .unwrap()
590 };
591 assert_eq!(r0, 23);
592 assert_eq!(
593 core::str::from_utf8(&b0[..23]).unwrap(),
594 "/memfd:foobar (deleted)",
595 );
596
597 let r0 = unsafe {
598 syscall1(
599 crate::syscall::arch::native::nr::CLOSE,
600 f0,
601 ).unwrap()
602 };
603 assert_eq!(r0, 0);
604 }
605
606 #[test]
607 fn syscall5_check() {
608 //
609 // Test validity of `syscall5()`.
610 //
611 // Tested syscall: STATX
612 //
613 // Run `statx()` on `STDIN`, but pass `AT_SYMLINK_NOFOLLOW`. This
614 // means we instead get information on the symlink in `/proc`. Check
615 // that this was correctly interpreted by the kernel and verify the
616 // `S_IFLNK` flag is set on the result.
617 //
618
619 let mut b0: [u32; 1024] = [0; 1024];
620
621 let r0 = unsafe {
622 syscall5(
623 crate::syscall::arch::native::nr::STATX,
624 core::usize::MAX - 100 + 1, // AT_FDCWD
625 "/proc/self/fd/0".as_ptr() as usize,
626 0x100, // AT_SYMLINK_NOFOLLOW
627 0x1, // STATX_TYPE
628 b0.as_mut_ptr() as usize,
629 )
630 .unwrap()
631 };
632 assert_eq!(r0, 0);
633 assert_ne!(b0[0] & 0x1, 0);
634 assert_eq!(
635 unsafe {
636 core::ptr::read_unaligned(
637 (b0.as_ptr() as *const u16).offset(14)
638 )
639 } & 0o170000, // S_IFMT
640 0o120000, // S_IFLNK
641 );
642 }
643
644 #[test]
645 fn syscall6_check() {
646 //
647 // Test validity of `syscall6()`.
648 //
649 // Tested syscall: COPY_FILE_RANGE
650 //
651 // Create two memfd instances, write a text into the first one. Use
652 // the `copy_file_range()` syscall to copy the data over into the other
653 // memfd and then verify via `read()`. Use `lseek()` to reset the file
654 // position between calls.
655 //
656
657 let mut b0: [u8; 128] = [0; 128];
658
659 let f0 = unsafe {
660 syscall2(
661 crate::syscall::arch::native::nr::MEMFD_CREATE,
662 "foobar\x00".as_ptr() as usize,
663 0,
664 ).unwrap()
665 };
666 let f1 = unsafe {
667 syscall2(
668 crate::syscall::arch::native::nr::MEMFD_CREATE,
669 "foobar\x00".as_ptr() as usize,
670 0,
671 ).unwrap()
672 };
673 assert!(f0 > 2);
674 assert!(f1 > 2);
675 assert_ne!(f0, f1);
676
677 let r0 = unsafe {
678 syscall3(
679 crate::syscall::arch::native::nr::WRITE,
680 f0 as usize,
681 "foobar".as_ptr() as usize,
682 6 as usize,
683 )
684 .unwrap()
685 };
686 assert_eq!(r0, 6);
687
688 let r0 = unsafe {
689 syscall3(
690 crate::syscall::arch::native::nr::LSEEK,
691 f0 as usize,
692 0 as usize,
693 0 as usize,
694 ).unwrap()
695 };
696 assert_eq!(r0, 0);
697
698 let r0 = unsafe {
699 syscall6(
700 crate::syscall::arch::native::nr::COPY_FILE_RANGE,
701 f0 as usize,
702 0 as usize,
703 f1 as usize,
704 0 as usize,
705 6 as usize,
706 0 as usize,
707 )
708 .unwrap()
709 };
710 assert_eq!(r0, 6);
711
712 let r0 = unsafe {
713 syscall3(
714 crate::syscall::arch::native::nr::LSEEK,
715 f1 as usize,
716 0 as usize,
717 0 as usize,
718 ).unwrap()
719 };
720 assert_eq!(r0, 0);
721
722 let r0 = unsafe {
723 syscall3(
724 crate::syscall::arch::native::nr::READ,
725 f1 as usize,
726 b0.as_mut_ptr() as usize,
727 6 as usize,
728 )
729 .unwrap()
730 };
731 assert_eq!(r0, 6);
732 assert_eq!(core::str::from_utf8(&b0[..6]), Ok("foobar"));
733
734 let r0 = unsafe {
735 syscall1(
736 crate::syscall::arch::native::nr::CLOSE,
737 f1,
738 ).unwrap()
739 };
740 assert_eq!(r0, 0);
741 let r0 = unsafe {
742 syscall1(
743 crate::syscall::arch::native::nr::CLOSE,
744 f0,
745 ).unwrap()
746 };
747 assert_eq!(r0, 0);
748 }
749}