Created
February 28, 2025 22:52
-
-
Save qti3e/bcd1bff5b95f2b38f6f9f281a8197df4 to your computer and use it in GitHub Desktop.
attempt to use rseq in rust
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use core::arch::asm; | |
use core::ptr::null_mut; | |
// https://github.com/torvalds/linux/blob/276f98efb64a2c31c099465ace78d3054c662a0f/arch/x86/entry/syscalls/syscall_64.tbl | |
const SYS_RSEQ: u64 = 334; | |
// https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/errno-base.h | |
const EINVAL: i64 = 22; | |
// https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/errno.h | |
const ENOSYS: i64 = 38; | |
// enum rseq_abi_cpu_id_state { | |
const RSEQ_ABI_CPU_ID_UNINITIALIZED: i32 = -1; | |
const RSEQ_ABI_CPU_ID_REGISTRATION_FAILED: i32 = -2; | |
// enum rseq_abi_flags { | |
const RSEQ_ABI_FLAG_UNREGISTER: u32 = (1 << 0); | |
// enum rseq_abi_cs_flags_bit { | |
const RSEQ_ABI_CS_FLAG_NO_RESTART_ON_PREEMPT_BIT: u32 = 0; | |
const RSEQ_ABI_CS_FLAG_NO_RESTART_ON_SIGNAL_BIT: u32 = 1; | |
const RSEQ_ABI_CS_FLAG_NO_RESTART_ON_MIGRATE_BIT: u32 = 2; | |
// enum rseq_abi_cs_flags | |
const RSEQ_ABI_CS_FLAG_NO_RESTART_ON_PREEMPT: u32 = 1 << RSEQ_ABI_CS_FLAG_NO_RESTART_ON_PREEMPT_BIT; | |
const RSEQ_ABI_CS_FLAG_NO_RESTART_ON_SIGNAL: u32 = 1 << RSEQ_ABI_CS_FLAG_NO_RESTART_ON_SIGNAL_BIT; | |
const RSEQ_ABI_CS_FLAG_NO_RESTART_ON_MIGRATE: u32 = 1 << RSEQ_ABI_CS_FLAG_NO_RESTART_ON_MIGRATE_BIT; | |
const RSEQ_SIG: u32 = 0x53053053; | |
/* | |
* struct rseq_abi is aligned on 4 * 8 bytes to ensure it is always | |
* contained within a single cache-line. | |
* | |
* A single struct rseq_abi per thread is allowed. | |
*/ | |
#[repr(C, align(1024))] | |
#[derive(Default, Debug)] | |
pub struct RSeqAbi<T: ?Sized = ()> { | |
/* | |
* Restartable sequences cpu_id_start field. Updated by the | |
* kernel. Read by user-space with single-copy atomicity | |
* semantics. This field should only be read by the thread which | |
* registered this data structure. Aligned on 32-bit. Always | |
* contains a value in the range of possible CPUs, although the | |
* value may not be the actual current CPU (e.g. if rseq is not | |
* initialized). This CPU number value should always be compared | |
* against the value of the cpu_id field before performing a rseq | |
* commit or returning a value read from a data structure indexed | |
* using the cpu_id_start value. | |
*/ | |
cpu_id_start: i32, | |
/* | |
* Restartable sequences cpu_id field. Updated by the kernel. | |
* Read by user-space with single-copy atomicity semantics. This | |
* field should only be read by the thread which registered this | |
* data structure. Aligned on 32-bit. Values | |
* RSEQ_CPU_ID_UNINITIALIZED and RSEQ_CPU_ID_REGISTRATION_FAILED | |
* have a special semantic: the former means "rseq uninitialized", | |
* and latter means "rseq initialization failed". This value is | |
* meant to be read within rseq critical sections and compared | |
* with the cpu_id_start value previously read, before performing | |
* the commit instruction, or read and compared with the | |
* cpu_id_start value before returning a value loaded from a data | |
* structure indexed using the cpu_id_start value. | |
*/ | |
cpu_id: i32, | |
/* | |
* Restartable sequences rseq_cs field. | |
* | |
* Contains NULL when no critical section is active for the current | |
* thread, or holds a pointer to the currently active struct rseq_cs. | |
* | |
* Updated by user-space, which sets the address of the currently | |
* active rseq_cs at the beginning of assembly instruction sequence | |
* block, and set to NULL by the kernel when it restarts an assembly | |
* instruction sequence block, as well as when the kernel detects that | |
* it is preempting or delivering a signal outside of the range | |
* targeted by the rseq_cs. Also needs to be set to NULL by user-space | |
* before reclaiming memory that contains the targeted struct rseq_cs. | |
* | |
* Read and set by the kernel. Set by user-space with single-copy | |
* atomicity semantics. This field should only be updated by the | |
* thread which registered this data structure. Aligned on 64-bit. | |
* | |
* 32-bit architectures should update the low order bits of the | |
* rseq_cs field, leaving the high order bits initialized to 0. | |
*/ | |
rseq_cs: i64, | |
/* | |
* Restartable sequences flags field. | |
* | |
* This field should only be updated by the thread which | |
* registered this data structure. Read by the kernel. | |
* Mainly used for single-stepping through rseq critical sections | |
* with debuggers. | |
* | |
* - RSEQ_CS_FLAG_NO_RESTART_ON_PREEMPT | |
* Inhibit instruction sequence block restart on preemption | |
* for this thread. | |
* - RSEQ_CS_FLAG_NO_RESTART_ON_SIGNAL | |
* Inhibit instruction sequence block restart on signal | |
* delivery for this thread. | |
* - RSEQ_CS_FLAG_NO_RESTART_ON_MIGRATE | |
* Inhibit instruction sequence block restart on migration for | |
* this thread. | |
*/ | |
flags: u32, | |
/* | |
* Restartable sequences node_id field. Updated by the kernel. Read by | |
* user-space with single-copy atomicity semantics. This field should | |
* only be read by the thread which registered this data structure. | |
* Aligned on 32-bit. Contains the current NUMA node ID. | |
*/ | |
node_id: i32, | |
/* | |
* Restartable sequences mm_cid field. Updated by the kernel. Read by | |
* user-space with single-copy atomicity semantics. This field should | |
* only be read by the thread which registered this data structure. | |
* Aligned on 32-bit. Contains the current thread's concurrency ID | |
* (allocated uniquely within a memory map). | |
*/ | |
mm_cid: i32, | |
/* | |
* Flexible array member at end of structure, after last feature field. | |
*/ | |
end: T, | |
} | |
/// Wrapper around a Linux syscall with three arguments. It returns | |
/// the syscall result (or error code) that gets stored in rax. | |
unsafe fn syscall_4(num: u64, arg1: u64, arg2: u64, arg3: u64, arg4: u64) -> Result<i64, i64> { | |
dbg!(arg1); | |
// https://github.com/torvalds/linux/blob/v5.0/arch/x86/entry/entry_64.S#L107 | |
let res; | |
asm!( | |
// there is no need to write "mov"-instructions, see below | |
"syscall", | |
// from 'in("rax")' the compiler will | |
// generate corresponding 'mov'-instructions | |
in("rax") num, | |
in("rdi") arg1, | |
in("rsi") arg2, | |
in("rdx") arg3, | |
in("r10") arg3, | |
lateout("rax") res, | |
); | |
syscall_ret(res) | |
} | |
// https://git.musl-libc.org/cgit/musl/tree/src/internal/syscall_ret.c | |
fn syscall_ret(r: i64) -> Result<i64, i64> { | |
if r > -4096 { Err(-r) } else { Ok(r) } | |
} | |
unsafe fn sys_rseq<T: ?Sized>( | |
rseq_abi: *mut RSeqAbi<T>, | |
rseq_len: u32, | |
flags: u32, | |
sig: u32, | |
) -> Result<i64, i64> { | |
syscall_4( | |
SYS_RSEQ, | |
rseq_abi as *mut u8 as usize as u64, | |
rseq_len as u64, | |
flags as u64, | |
sig as u64, | |
) | |
} | |
fn rseq_available() -> bool { | |
match unsafe { sys_rseq::<()>(null_mut(), 0, 0, 0) } { | |
Err(EINVAL) => true, | |
Err(ENOSYS) => false, | |
Ok(_) | Err(_) => panic!("unexpected syscall result"), | |
} | |
} | |
fn rseq_register_current_thread() { | |
let mut abi = RSeqAbi { | |
cpu_id_start: RSEQ_ABI_CPU_ID_UNINITIALIZED, | |
cpu_id: 0, | |
rseq_cs: 0, | |
flags: 0, | |
node_id: 0, | |
mm_cid: 0, | |
end: (), | |
}; | |
let x = unsafe { sys_rseq(&mut abi, 32, 0, RSEQ_SIG) }; | |
dbg!(x); | |
dbg!(abi); | |
} | |
fn main() { | |
dbg!(rseq_available()); | |
rseq_register_current_thread(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment