-
Notifications
You must be signed in to change notification settings - Fork 204
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add linux_raw
opt-in backend
#572
Changes from all commits
bff082c
c5c743b
2edbfcf
2da6796
fd78be9
e558c4c
fbd8acb
53c4ca8
f626152
28503c2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
//! Implementation for Linux / Android using `asm!`-based syscalls. | ||
use crate::{Error, MaybeUninit}; | ||
|
||
pub use crate::util::{inner_u32, inner_u64}; | ||
|
||
#[cfg(not(any(target_os = "android", target_os = "linux")))] | ||
compile_error!("`linux_raw` backend can be enabled only for Linux/Android targets!"); | ||
|
||
#[allow(non_upper_case_globals)] | ||
unsafe fn getrandom_syscall(buf: *mut u8, buflen: usize, flags: u32) -> isize { | ||
let r0; | ||
|
||
// Based on `rustix` and `linux-raw-sys` code. | ||
cfg_if! { | ||
if #[cfg(target_arch = "arm")] { | ||
const __NR_getrandom: u32 = 384; | ||
// In thumb-mode, r7 is the frame pointer and is not permitted to be used in | ||
// an inline asm operand, so we have to use a different register and copy it | ||
// into r7 inside the inline asm. | ||
// Theoretically, we could detect thumb mode in the build script, but several | ||
// register moves are cheap enough compared to the syscall cost, so we do not | ||
// bother with it. | ||
core::arch::asm!( | ||
"mov {tmp}, r7", | ||
"mov r7, {nr}", | ||
"svc 0", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looking at the Personally, I would be fine omitting 32-bit arm support (similar to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looks like I think this code would be fine provided we simply confirm (via There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This code is based on After a brief search I couldn't find any relevant information about OABI support in Rust. Are you sure that Rust supports it in the first place? For example, it looks like GCC has dropped OABI support completely. |
||
"mov r7, {tmp}", | ||
nr = const __NR_getrandom, | ||
tmp = out(reg) _, | ||
inlateout("r0") buf => r0, | ||
in("r1") buflen, | ||
in("r2") flags, | ||
options(nostack, preserves_flags) | ||
); | ||
} else if #[cfg(target_arch = "aarch64")] { | ||
const __NR_getrandom: u32 = 278; | ||
core::arch::asm!( | ||
"svc 0", | ||
in("x8") __NR_getrandom, | ||
inlateout("x0") buf => r0, | ||
in("x1") buflen, | ||
in("x2") flags, | ||
options(nostack, preserves_flags) | ||
); | ||
} else if #[cfg(target_arch = "loongarch64")] { | ||
const __NR_getrandom: u32 = 278; | ||
core::arch::asm!( | ||
"syscall 0", | ||
in("$a7") __NR_getrandom, | ||
inlateout("$a0") buf => r0, | ||
in("$a1") buflen, | ||
in("$a2") flags, | ||
options(nostack, preserves_flags) | ||
); | ||
} else if #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] { | ||
const __NR_getrandom: u32 = 278; | ||
core::arch::asm!( | ||
"ecall", | ||
in("a7") __NR_getrandom, | ||
inlateout("a0") buf => r0, | ||
in("a1") buflen, | ||
in("a2") flags, | ||
options(nostack, preserves_flags) | ||
); | ||
} else if #[cfg(target_arch = "s390x")] { | ||
const __NR_getrandom: u32 = 349; | ||
core::arch::asm!( | ||
"svc 0", | ||
in("r1") __NR_getrandom, | ||
inlateout("r2") buf => r0, | ||
in("r3") buflen, | ||
in("r4") flags, | ||
options(nostack, preserves_flags) | ||
); | ||
} else if #[cfg(target_arch = "x86")] { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Give that there is no There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IIRC the only problem with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I tweaked the warning a bit. Now it notes that |
||
const __NR_getrandom: u32 = 355; | ||
// `int 0x80` is famously slow, but implementing vDSO is too complex | ||
// and `sysenter`/`syscall` have their own portability issues, | ||
// so we use the simple "legacy" way of doing syscalls. | ||
core::arch::asm!( | ||
"int $$0x80", | ||
in("eax") __NR_getrandom, | ||
in("ebx") buf, | ||
in("ecx") buflen, | ||
in("edx") flags, | ||
lateout("eax") r0, | ||
options(nostack, preserves_flags) | ||
); | ||
} else if #[cfg(target_arch = "x86_64")] { | ||
#[cfg(target_pointer_width = "64")] | ||
const __NR_getrandom: u32 = 318; | ||
#[cfg(target_pointer_width = "32")] | ||
const __NR_getrandom: u32 = (1 << 30) + 318; | ||
|
||
core::arch::asm!( | ||
"syscall", | ||
in("rax") __NR_getrandom, | ||
in("rdi") buf, | ||
in("rsi") buflen, | ||
in("rdx") flags, | ||
lateout("rax") r0, | ||
lateout("rcx") _, | ||
lateout("r11") _, | ||
options(nostack, preserves_flags) | ||
); | ||
} else { | ||
compile_error!("`linux_raw` backend does not support this target arch"); | ||
} | ||
} | ||
|
||
r0 | ||
} | ||
|
||
#[inline] | ||
pub fn fill_inner(mut dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> { | ||
// Value of this error code is stable across all target arches. | ||
const EINTR: isize = -4; | ||
|
||
loop { | ||
let ret = unsafe { getrandom_syscall(dest.as_mut_ptr().cast(), dest.len(), 0) }; | ||
match usize::try_from(ret) { | ||
Ok(0) => return Err(Error::UNEXPECTED), | ||
Ok(len) => { | ||
dest = dest.get_mut(len..).ok_or(Error::UNEXPECTED)?; | ||
if dest.is_empty() { | ||
return Ok(()); | ||
} | ||
} | ||
Err(_) if ret == EINTR => continue, | ||
Err(_) => { | ||
let code: u32 = ret | ||
.wrapping_neg() | ||
.try_into() | ||
.map_err(|_| Error::UNEXPECTED)?; | ||
return Err(Error::from_os_error(code)); | ||
} | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm trying to figure out what the constraints are around directly invoking syscalls on Android. I can try to check with some people at work, but it's unclear if bionic libc (the libc shipped with Android) does any special handling for syscalls. If the only demand for this is on
linux
targets, it might just be best to initially exclude Android.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, it would be nice to verify that raw syscalls work on Android without any surprises.
Here is a relevant
rustix
discussion: bytecodealliance/rustix#1095 No one has mentioned any fundamental technical obstacles to using raw syscalls and it looks like some people use them in practice.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since users have to explicitly enable the backend, I think we can leave it as-is and potentially disable it later if it will be found problematic on Android for some reason.