1//! Encapsulation for system call arguments and return values. 2//! 3//! The inline-asm and outline-asm code paths do some amount of reordering 4//! of arguments; to ensure that we don't accidentally misroute an argument 5//! or return value, we use distinct types for each argument index and 6//! return value. 7//! 8//! # Safety 9//! 10//! The `ToAsm` and `FromAsm` traits are unsafe to use; they should only be 11//! used by the syscall code which executes actual syscall machine 12//! instructions. 13 14#![allow(unsafe_code)] 15 16use super::c; 17use super::fd::RawFd; 18use core::marker::PhantomData; 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 27 /// the 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 39 /// the 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(target_arch = "mips")] 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(target_arch = "mips")] 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: core::ops::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(target_arch = "mips")] 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