use crate::platform;
use core::sync::atomic::{AtomicU64, Ordering};
static CANARY_SECRET: AtomicU64 = AtomicU64::new(0);
pub unsafe fn init_canary_secret() {
let mut buf = [0u8; 8];
#[cfg(target_os = "linux")]
{
let mut filled = 0usize;
while filled < 8 {
let ret = libc::syscall(
libc::SYS_getrandom,
buf.as_mut_ptr().add(filled),
8 - filled,
0u32,
);
if ret > 0 {
filled += ret as usize;
} else if ret == 0 {
break;
} else {
let err = *libc::__errno_location();
if err == libc::EINTR {
continue;
}
break;
}
}
}
#[cfg(not(target_os = "linux"))]
{
libc::arc4random_buf(buf.as_mut_ptr() as *mut libc::c_void, 8);
}
let secret = u64::from_le_bytes(buf);
let secret = if secret != 0 {
secret
} else {
let stack_addr = &buf as *const _ as u64;
platform::splitmix64(stack_addr)
};
CANARY_SECRET.store(secret, Ordering::Release);
}
#[allow(dead_code)]
#[inline]
pub fn generate_canary(ptr: *mut u8) -> u64 {
let addr = ptr as u64;
let secret = CANARY_SECRET.load(Ordering::Acquire);
platform::splitmix64(platform::splitmix64(secret) ^ platform::splitmix64(addr))
}
#[allow(dead_code)]
#[inline]
pub unsafe fn write_canary(ptr: *mut u8, requested_size: usize, slot_size: usize, canary: u64) {
let gap = slot_size - requested_size;
if gap == 0 {
return;
}
let canary_start = ptr.add(requested_size);
let mut i = 0;
while i + 8 <= gap {
(canary_start.add(i) as *mut u64).write_unaligned(canary);
i += 8;
}
let canary_bytes = canary.to_le_bytes();
while i < gap {
canary_start.add(i).write(canary_bytes[i & 7]);
i += 1;
}
}
#[allow(dead_code)]
#[inline]
pub unsafe fn check_canary(
ptr: *mut u8,
requested_size: usize,
slot_size: usize,
canary: u64,
) -> bool {
let gap = slot_size - requested_size;
if gap == 0 {
return true;
}
let canary_start = ptr.add(requested_size);
let mut diff: u64 = 0;
let mut i = 0;
while i + 8 <= gap {
diff |= (canary_start.add(i) as *const u64).read_unaligned() ^ canary;
i += 8;
}
let canary_bytes = canary.to_le_bytes();
let mut byte_diff: u8 = 0;
while i < gap {
byte_diff |= canary_start.add(i).read() ^ canary_bytes[i & 7];
i += 1;
}
diff == 0 && byte_diff == 0
}
#[allow(dead_code)]
#[inline]
pub unsafe fn write_canary_front(slot_base: *mut u8, gap: usize, canary: u64) {
if gap == 0 {
return;
}
let mut i = 0;
while i + 8 <= gap {
(slot_base.add(i) as *mut u64).write_unaligned(canary);
i += 8;
}
let canary_bytes = canary.to_le_bytes();
while i < gap {
slot_base.add(i).write(canary_bytes[i & 7]);
i += 1;
}
}
#[allow(dead_code)]
#[inline]
pub unsafe fn check_canary_front(slot_base: *mut u8, gap: usize, canary: u64) -> bool {
if gap == 0 {
return true;
}
let mut diff: u64 = 0;
let mut i = 0;
while i + 8 <= gap {
diff |= (slot_base.add(i) as *const u64).read_unaligned() ^ canary;
i += 8;
}
let canary_bytes = canary.to_le_bytes();
let mut byte_diff: u8 = 0;
while i < gap {
byte_diff |= slot_base.add(i).read() ^ canary_bytes[i & 7];
i += 1;
}
diff == 0 && byte_diff == 0
}
#[inline(always)]
pub(crate) fn secret() -> u64 {
CANARY_SECRET.load(Ordering::Relaxed)
}