use crate::source::{EntropySource, Platform, SourceCategory, SourceInfo};
#[cfg(all(target_os = "macos", target_arch = "aarch64"))]
use crate::sources::helpers::{extract_timing_entropy_debiased, mach_time};
static APRR_JIT_TIMING_INFO: SourceInfo = SourceInfo {
name: "aprr_jit_timing",
description: "Apple APRR undocumented register JIT toggle — CV=100%, trimodal 0/42/83",
physics: "Times pthread_jit_write_protect_np() which writes to Apple's proprietary \
S3_4_C15_C2_0 register (UUIDT/APRR). The register toggle triggers: permission \
pipeline flush (draining in-flight memory ops), TLB coherency for the JIT \
page, instruction stream coupling hazards. Empirical: CV=100.0% both \
directions, trimodal at 0/42/83 ticks — one or two pipeline flush cycles. \
Apple APRR is undocumented in ARM specs, accessible only at EL0 on Apple \
Silicon, reverse-engineered from iOS jailbreak research in 2020. First \
entropy source exploiting Apple-proprietary hardware permission register.",
category: SourceCategory::Microarch,
platform: Platform::MacOS,
requirements: &[],
entropy_rate_estimate: 1.5,
composite: false,
is_fast: false,
};
pub struct APRRJitTimingSource;
#[cfg(all(target_os = "macos", target_arch = "aarch64"))]
unsafe extern "C" {
fn pthread_jit_write_protect_np(enabled: i32);
}
#[cfg(all(target_os = "macos", target_arch = "aarch64"))]
impl EntropySource for APRRJitTimingSource {
fn info(&self) -> &SourceInfo {
&APRR_JIT_TIMING_INFO
}
fn is_available(&self) -> bool {
static APRR_AVAILABLE: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
*APRR_AVAILABLE.get_or_init(|| {
let page = unsafe {
libc::mmap(
core::ptr::null_mut(),
4096,
libc::PROT_READ | libc::PROT_WRITE | libc::PROT_EXEC,
libc::MAP_PRIVATE | libc::MAP_ANONYMOUS | 0x0800, -1,
0,
)
};
if page == libc::MAP_FAILED {
return false;
}
unsafe { libc::munmap(page, 4096) };
true
})
}
fn collect(&self, n_samples: usize) -> Vec<u8> {
struct JitPage(*mut libc::c_void);
impl Drop for JitPage {
fn drop(&mut self) {
unsafe {
libc::munmap(self.0, 4096);
}
}
}
let jit_page = unsafe {
libc::mmap(
core::ptr::null_mut(),
4096,
libc::PROT_READ | libc::PROT_WRITE | libc::PROT_EXEC,
libc::MAP_PRIVATE | libc::MAP_ANONYMOUS | 0x0800, -1,
0,
)
};
if jit_page == libc::MAP_FAILED {
return Vec::new();
}
let _jit_guard = JitPage(jit_page);
let raw = n_samples * 3 + 64;
let mut timings = Vec::with_capacity(raw * 2);
for _ in 0..16 {
unsafe {
pthread_jit_write_protect_np(0);
pthread_jit_write_protect_np(1);
}
}
for _ in 0..raw {
let t0 = mach_time();
unsafe { pthread_jit_write_protect_np(0) }; let t_write = mach_time().wrapping_sub(t0);
let t1 = mach_time();
unsafe { pthread_jit_write_protect_np(1) }; let t_exec = mach_time().wrapping_sub(t1);
if t_write < 24_000 {
timings.push(t_write);
}
if t_exec < 24_000 {
timings.push(t_exec);
}
}
let mixed: Vec<u64> = timings
.chunks(2)
.filter(|c| c.len() == 2)
.map(|c| c[0].wrapping_add(c[1].wrapping_shl(3)))
.collect();
extract_timing_entropy_debiased(&mixed, n_samples)
}
}
#[cfg(not(all(target_os = "macos", target_arch = "aarch64")))]
impl EntropySource for APRRJitTimingSource {
fn info(&self) -> &SourceInfo {
&APRR_JIT_TIMING_INFO
}
fn is_available(&self) -> bool {
false
}
fn collect(&self, _: usize) -> Vec<u8> {
Vec::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn info() {
let src = APRRJitTimingSource;
assert_eq!(src.info().name, "aprr_jit_timing");
assert!(matches!(src.info().category, SourceCategory::Microarch));
assert_eq!(src.info().platform, Platform::MacOS);
assert!(!src.info().composite);
}
#[test]
#[cfg(all(target_os = "macos", target_arch = "aarch64"))]
fn is_available_on_apple_silicon() {
let _ = APRRJitTimingSource.is_available(); }
#[test]
#[ignore]
fn collects_trimodal_aprr() {
let data = APRRJitTimingSource.collect(32);
assert!(!data.is_empty());
}
}