rustix/backend/linux_raw/
reg.rs

1//! Encapsulation for system call arguments and return values.
2//!
3//! The inline-asm code paths do some amount of reordering of arguments; to
4//! ensure that we don't accidentally misroute an argument or return value, we
5//! use distinct types for each argument index and return value.
6//!
7//! # Safety
8//!
9//! The `ToAsm` and `FromAsm` traits are unsafe to use; they should only be
10//! used by the syscall code which executes actual syscall machine
11//! instructions.
12
13#![allow(unsafe_code)]
14
15use super::c;
16use super::fd::RawFd;
17use core::marker::PhantomData;
18use core::ops::Range;
19
20pub(super) trait ToAsm: private::Sealed {
21    /// Convert `self` to a `usize` ready to be passed to a syscall
22    /// machine instruction.
23    ///
24    /// # Safety
25    ///
26    /// This should be used immediately before the syscall instruction, and the
27    /// returned value shouldn't be used for any other purpose.
28    #[must_use]
29    unsafe fn to_asm(self) -> *mut Opaque;
30}
31
32pub(super) trait FromAsm: private::Sealed {
33    /// Convert `raw` from a value produced by a syscall machine instruction
34    /// into a `Self`.
35    ///
36    /// # Safety
37    ///
38    /// This should be used immediately after the syscall instruction, and the
39    /// operand value shouldn't be used for any other purpose.
40    #[must_use]
41    unsafe fn from_asm(raw: *mut Opaque) -> Self;
42}
43
44/// To preserve provenance, syscall arguments and return values are passed as
45/// pointer types. They need a type to point to, so we define a custom private
46/// type, to prevent it from being used for anything else.
47#[repr(transparent)]
48#[allow(dead_code)]
49pub(super) struct Opaque(c::c_void);
50
51// Argument numbers.
52pub(super) struct A0(());
53pub(super) struct A1(());
54pub(super) struct A2(());
55pub(super) struct A3(());
56pub(super) struct A4(());
57pub(super) struct A5(());
58#[cfg(any(target_arch = "mips", target_arch = "mips32r6"))]
59pub(super) struct A6(());
60#[cfg(target_arch = "x86")]
61pub(super) struct SocketArg;
62
63pub(super) trait ArgNumber: private::Sealed {}
64impl ArgNumber for A0 {}
65impl ArgNumber for A1 {}
66impl ArgNumber for A2 {}
67impl ArgNumber for A3 {}
68impl ArgNumber for A4 {}
69impl ArgNumber for A5 {}
70#[cfg(any(target_arch = "mips", target_arch = "mips32r6"))]
71impl ArgNumber for A6 {}
72#[cfg(target_arch = "x86")]
73impl ArgNumber for SocketArg {}
74
75// Return value numbers.
76pub(super) struct R0(());
77
78pub(super) trait RetNumber: private::Sealed {}
79impl RetNumber for R0 {}
80
81/// Syscall arguments use register-sized types. We use a newtype to
82/// discourage accidental misuse of the raw integer values.
83///
84/// This type doesn't implement `Clone` or `Copy`; it should be used exactly
85/// once. And it has a lifetime to ensure that it doesn't outlive any resources
86/// it might be pointing to.
87#[repr(transparent)]
88#[must_use]
89pub(super) struct ArgReg<'a, Num: ArgNumber> {
90    raw: *mut Opaque,
91    _phantom: PhantomData<(&'a (), Num)>,
92}
93
94impl<'a, Num: ArgNumber> ToAsm for ArgReg<'a, Num> {
95    #[inline]
96    unsafe fn to_asm(self) -> *mut Opaque {
97        self.raw
98    }
99}
100
101/// Syscall return values use register-sized types. We use a newtype to
102/// discourage accidental misuse of the raw integer values.
103///
104/// This type doesn't implement `Clone` or `Copy`; it should be used exactly
105/// once.
106#[repr(transparent)]
107#[must_use]
108pub(super) struct RetReg<Num: RetNumber> {
109    raw: *mut Opaque,
110    _phantom: PhantomData<Num>,
111}
112
113impl<Num: RetNumber> RetReg<Num> {
114    #[inline]
115    pub(super) fn decode_usize(self) -> usize {
116        debug_assert!(!(-4095..0).contains(&(self.raw as isize)));
117        self.raw as usize
118    }
119
120    #[inline]
121    pub(super) fn decode_raw_fd(self) -> RawFd {
122        let bits = self.decode_usize();
123        let raw_fd = bits as RawFd;
124
125        // Converting `raw` to `RawFd` should be lossless.
126        debug_assert_eq!(raw_fd as usize, bits);
127
128        raw_fd
129    }
130
131    #[inline]
132    pub(super) fn decode_c_int(self) -> c::c_int {
133        let bits = self.decode_usize();
134        let c_int_ = bits as c::c_int;
135
136        // Converting `raw` to `c_int` should be lossless.
137        debug_assert_eq!(c_int_ as usize, bits);
138
139        c_int_
140    }
141
142    #[inline]
143    pub(super) fn decode_c_uint(self) -> c::c_uint {
144        let bits = self.decode_usize();
145        let c_uint_ = bits as c::c_uint;
146
147        // Converting `raw` to `c_uint` should be lossless.
148        debug_assert_eq!(c_uint_ as usize, bits);
149
150        c_uint_
151    }
152
153    #[inline]
154    pub(super) fn decode_void_star(self) -> *mut c::c_void {
155        self.raw.cast()
156    }
157
158    #[cfg(target_pointer_width = "64")]
159    #[inline]
160    pub(super) fn decode_u64(self) -> u64 {
161        self.decode_usize() as u64
162    }
163
164    #[inline]
165    pub(super) fn decode_void(self) {
166        let ignore = self.decode_usize();
167        debug_assert_eq!(ignore, 0);
168    }
169
170    #[inline]
171    pub(super) fn decode_error_code(self) -> u16 {
172        let bits = self.raw as usize;
173
174        // `raw` must be in `-4095..0`. Linux always returns errors in
175        // `-4095..0`, and we double-check it here.
176        debug_assert!((-4095..0).contains(&(bits as isize)));
177
178        bits as u16
179    }
180
181    #[inline]
182    pub(super) fn is_nonzero(&self) -> bool {
183        !self.raw.is_null()
184    }
185
186    #[inline]
187    pub(super) fn is_negative(&self) -> bool {
188        (self.raw as isize) < 0
189    }
190
191    #[inline]
192    pub(super) fn is_in_range(&self, range: Range<isize>) -> bool {
193        range.contains(&(self.raw as isize))
194    }
195}
196
197impl<Num: RetNumber> FromAsm for RetReg<Num> {
198    #[inline]
199    unsafe fn from_asm(raw: *mut Opaque) -> Self {
200        Self {
201            raw,
202            _phantom: PhantomData,
203        }
204    }
205}
206
207#[repr(transparent)]
208pub(super) struct SyscallNumber<'a> {
209    pub(super) nr: usize,
210    _phantom: PhantomData<&'a ()>,
211}
212
213impl<'a> ToAsm for SyscallNumber<'a> {
214    #[inline]
215    unsafe fn to_asm(self) -> *mut Opaque {
216        self.nr as usize as *mut Opaque
217    }
218}
219
220/// Encode a system call argument as an `ArgReg`.
221#[inline]
222pub(super) fn raw_arg<'a, Num: ArgNumber>(raw: *mut Opaque) -> ArgReg<'a, Num> {
223    ArgReg {
224        raw,
225        _phantom: PhantomData,
226    }
227}
228
229/// Encode a system call number (a `__NR_*` constant) as a `SyscallNumber`.
230#[inline]
231pub(super) const fn nr<'a>(nr: u32) -> SyscallNumber<'a> {
232    SyscallNumber {
233        nr: nr as usize,
234        _phantom: PhantomData,
235    }
236}
237
238/// Seal our various traits using the technique documented [here].
239///
240/// [here]: https://rust-lang.github.io/api-guidelines/future-proofing.html
241mod private {
242    pub trait Sealed {}
243
244    // Implement for those same types, but no others.
245    impl<'a, Num: super::ArgNumber> Sealed for super::ArgReg<'a, Num> {}
246    impl<Num: super::RetNumber> Sealed for super::RetReg<Num> {}
247    impl<'a> Sealed for super::SyscallNumber<'a> {}
248    impl Sealed for super::A0 {}
249    impl Sealed for super::A1 {}
250    impl Sealed for super::A2 {}
251    impl Sealed for super::A3 {}
252    impl Sealed for super::A4 {}
253    impl Sealed for super::A5 {}
254    #[cfg(any(target_arch = "mips", target_arch = "mips32r6"))]
255    impl Sealed for super::A6 {}
256    #[cfg(target_arch = "x86")]
257    impl Sealed for super::SocketArg {}
258    impl Sealed for super::R0 {}
259}