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)]
48pub(super) struct Opaque(c::c_void);
49
50// Argument numbers.
51pub(super) struct A0(());
52pub(super) struct A1(());
53pub(super) struct A2(());
54pub(super) struct A3(());
55pub(super) struct A4(());
56pub(super) struct A5(());
57#[cfg(any(target_arch = "mips", target_arch = "mips32r6"))]
58pub(super) struct A6(());
59#[cfg(target_arch = "x86")]
60pub(super) struct SocketArg;
61
62pub(super) trait ArgNumber: private::Sealed {}
63impl ArgNumber for A0 {}
64impl ArgNumber for A1 {}
65impl ArgNumber for A2 {}
66impl ArgNumber for A3 {}
67impl ArgNumber for A4 {}
68impl ArgNumber for A5 {}
69#[cfg(any(target_arch = "mips", target_arch = "mips32r6"))]
70impl ArgNumber for A6 {}
71#[cfg(target_arch = "x86")]
72impl ArgNumber for SocketArg {}
73
74// Return value numbers.
75pub(super) struct R0(());
76
77pub(super) trait RetNumber: private::Sealed {}
78impl RetNumber for R0 {}
79
80/// Syscall arguments use register-sized types. We use a newtype to
81/// discourage accidental misuse of the raw integer values.
82///
83/// This type doesn't implement `Clone` or `Copy`; it should be used exactly
84/// once. And it has a lifetime to ensure that it doesn't outlive any resources
85/// it might be pointing to.
86#[repr(transparent)]
87#[must_use]
88pub(super) struct ArgReg<'a, Num: ArgNumber> {
89 raw: *mut Opaque,
90 _phantom: PhantomData<(&'a (), Num)>,
91}
92
93impl<'a, Num: ArgNumber> ToAsm for ArgReg<'a, Num> {
94 #[inline]
95 unsafe fn to_asm(self) -> *mut Opaque {
96 self.raw
97 }
98}
99
100/// Syscall return values use register-sized types. We use a newtype to
101/// discourage accidental misuse of the raw integer values.
102///
103/// This type doesn't implement `Clone` or `Copy`; it should be used exactly
104/// once.
105#[repr(transparent)]
106#[must_use]
107pub(super) struct RetReg<Num: RetNumber> {
108 raw: *mut Opaque,
109 _phantom: PhantomData<Num>,
110}
111
112impl<Num: RetNumber> RetReg<Num> {
113 #[inline]
114 pub(super) fn decode_usize(self) -> usize {
115 debug_assert!(!(-4095..0).contains(&(self.raw as isize)));
116 self.raw as usize
117 }
118
119 #[inline]
120 pub(super) fn decode_raw_fd(self) -> RawFd {
121 let bits = self.decode_usize();
122 let raw_fd = bits as RawFd;
123
124 // Converting `raw` to `RawFd` should be lossless.
125 debug_assert_eq!(raw_fd as usize, bits);
126
127 raw_fd
128 }
129
130 #[inline]
131 pub(super) fn decode_c_int(self) -> c::c_int {
132 let bits = self.decode_usize();
133 let c_int_ = bits as c::c_int;
134
135 // Converting `raw` to `c_int` should be lossless.
136 debug_assert_eq!(c_int_ as usize, bits);
137
138 c_int_
139 }
140
141 #[inline]
142 pub(super) fn decode_c_uint(self) -> c::c_uint {
143 let bits = self.decode_usize();
144 let c_uint_ = bits as c::c_uint;
145
146 // Converting `raw` to `c_uint` should be lossless.
147 debug_assert_eq!(c_uint_ as usize, bits);
148
149 c_uint_
150 }
151
152 #[inline]
153 pub(super) fn decode_void_star(self) -> *mut c::c_void {
154 self.raw.cast()
155 }
156
157 #[cfg(target_pointer_width = "64")]
158 #[inline]
159 pub(super) fn decode_u64(self) -> u64 {
160 self.decode_usize() as u64
161 }
162
163 #[inline]
164 pub(super) fn decode_void(self) {
165 let ignore = self.decode_usize();
166 debug_assert_eq!(ignore, 0);
167 }
168
169 #[inline]
170 pub(super) fn decode_error_code(self) -> u16 {
171 let bits = self.raw as usize;
172
173 // `raw` must be in `-4095..0`. Linux always returns errors in
174 // `-4095..0`, and we double-check it here.
175 debug_assert!((-4095..0).contains(&(bits as isize)));
176
177 bits as u16
178 }
179
180 #[inline]
181 pub(super) fn is_nonzero(&self) -> bool {
182 !self.raw.is_null()
183 }
184
185 #[inline]
186 pub(super) fn is_negative(&self) -> bool {
187 (self.raw as isize) < 0
188 }
189
190 #[inline]
191 pub(super) fn is_in_range(&self, range: Range<isize>) -> bool {
192 range.contains(&(self.raw as isize))
193 }
194}
195
196impl<Num: RetNumber> FromAsm for RetReg<Num> {
197 #[inline]
198 unsafe fn from_asm(raw: *mut Opaque) -> Self {
199 Self {
200 raw,
201 _phantom: PhantomData,
202 }
203 }
204}
205
206#[repr(transparent)]
207pub(super) struct SyscallNumber<'a> {
208 nr: usize,
209 _phantom: PhantomData<&'a ()>,
210}
211
212impl<'a> ToAsm for SyscallNumber<'a> {
213 #[inline]
214 unsafe fn to_asm(self) -> *mut Opaque {
215 self.nr as usize as *mut Opaque
216 }
217}
218
219/// Encode a system call argument as an `ArgReg`.
220#[inline]
221pub(super) fn raw_arg<'a, Num: ArgNumber>(raw: *mut Opaque) -> ArgReg<'a, Num> {
222 ArgReg {
223 raw,
224 _phantom: PhantomData,
225 }
226}
227
228/// Encode a system call number (a `__NR_*` constant) as a `SyscallNumber`.
229#[inline]
230pub(super) const fn nr<'a>(nr: u32) -> SyscallNumber<'a> {
231 SyscallNumber {
232 nr: nr as usize,
233 _phantom: PhantomData,
234 }
235}
236
237/// Seal our various traits using the technique documented [here].
238///
239/// [here]: https://rust-lang.github.io/api-guidelines/future-proofing.html
240mod private {
241 pub trait Sealed {}
242
243 // Implement for those same types, but no others.
244 impl<'a, Num: super::ArgNumber> Sealed for super::ArgReg<'a, Num> {}
245 impl<Num: super::RetNumber> Sealed for super::RetReg<Num> {}
246 impl<'a> Sealed for super::SyscallNumber<'a> {}
247 impl Sealed for super::A0 {}
248 impl Sealed for super::A1 {}
249 impl Sealed for super::A2 {}
250 impl Sealed for super::A3 {}
251 impl Sealed for super::A4 {}
252 impl Sealed for super::A5 {}
253 #[cfg(any(target_arch = "mips", target_arch = "mips32r6"))]
254 impl Sealed for super::A6 {}
255 #[cfg(target_arch = "x86")]
256 impl Sealed for super::SocketArg {}
257 impl Sealed for super::R0 {}
258}
259