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!(&copy 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}