in reverie-ptrace/src/validation.rs [459:603]
fn check_for_xen_pmi_bug(precise_ip: bool) -> Result<(), PmuValidationError> {
#[allow(unused_assignments)]
let mut count: i32 = -1;
let mut attr = ticks_attr(precise_ip);
attr.__bindgen_anon_1.sample_period = NUM_BRANCHES - 1;
let fd = start_counter(0, -1, &mut attr, None)?;
// The original C++ uses `rand()` to make sure this isn't optimized around.
// We will use black_box to do the same thing without introducing variability
fn make_accumulator_seed() -> u32 {
20764791
}
let mut accumulator = core::hint::black_box(make_accumulator_seed());
let mut expected_accumulator = accumulator;
// reproduce the assembly here to calculate what the final accumulator value should be
for _ in 0..(NUM_BRANCHES - 2) {
let temp_exp_accumulator = expected_accumulator.overflowing_shl(3).0;
expected_accumulator = temp_exp_accumulator
.overflowing_sub(expected_accumulator)
.0
.overflowing_add(2)
.0
& 0xffffff;
}
let raw_fd = fd.0;
#[allow(deprecated)]
unsafe {
// The following asm block does this:
// ```
// let ret = syscall!(sys_ioctl, raw_fd, _PERF_EVENT_IOC_ENABLE, 0);
// if ret >= -4095 as u64 { return; }
// let ret = syscall!(SYS_ioctl, raw_fd, _PERF_EVENT_IOC_RESET, 0);
// // From this point on, all conditional branches count!
// if ret >= -4095 as u64 { return; }
// // Reset the counter period to the desired value.
// let ret = syscall!(SYS_ioctl, raw_fd, _PERF_EVENT_IOC_PERIOD, attr.sample_period);
// if ret >= -4095 as u64 { return; }
// let mut iterations = NUM_BRANCHES - 2;
// loop {
// iterations -= 1;
// accumulator *= 7;
// accumulator += 2;
// accumulator &= 0xffffff;
// if iterations == 0 {
// break;
// }
// }
//
// let ret = syscall!(SYS_ioctl, raw_fd, _PERF_EVENT_IOC_DISABLE, 0);
// if ret >= -4095 as u64 { return; }
// count = 0;
// ```
llvm_asm!(
"
mov $2, %rax;
mov $9, %edi;
xor %rdx, %rdx;
mov $4, %rsi;
syscall;
cmp $$-4095, %rax;
jae 2f;
mov $2, %rax;
mov $6, %rsi;
syscall;
/* From this point on all conditional branches count! */
cmp $$-4095, %rax;
jae 2f;
/* Reset the counter period to the desired value. */
mov $2, %rax;
mov $5, %rsi;
mov $8, %rdx;
syscall;
cmp $$-4095, %rax;
jae 2f;
mov $7, %rax;
1: dec %rax;
/* Multiply by 7. */
mov $0, %edx;
shl $$3, $0;
sub %edx, $0;
/* Add 2. */
add $$2, $0;
/* Mask off bits. */
and $$0xffffff, $0;
/* And loop. */
test %rax, %rax;
jnz 1b;
mov $3, %rsi;
mov $2, %rax;
xor %rdx, %rdx;
/* We didn't touch rdi. */
syscall;
cmp $$-4095, %rax;
jae 2f;
movl $$0, $1;
2: nop;"
:
"+r"(accumulator),
"=r"(count)
:
"i"(libc::SYS_ioctl)
,
"i"(perf::perf_event_ioctls_DISABLE),
"i"(perf::perf_event_ioctls_ENABLE),
"i"(perf::perf_event_ioctls_PERIOD),
"i"(perf::perf_event_ioctls_RESET),
// The check for the failure of some of our ioctls is in
// the measured region, so account for that when looping.
"i"(NUM_BRANCHES - 2),
"rm"(&attr.__bindgen_anon_1.sample_period), "rm"(raw_fd)
:
"rax", "rdx", "rdi", "rsi"
/* `syscall` clobbers rcx and r11. */
,
"rcx", "r11"
: "volatile"
);
}
// If things worked above, `count` should have been set to 0.
if count == 0 {
count = read_counter(&fd)? as i32;
}
// Use 'accumulator' so it can't be optimized out.
if accumulator != expected_accumulator {
return Err(PmuValidationError::UnexpectedTestingError(format!(
"Unexpected accumulator value in xen pmi bug check. Expected {}, but was {}",
expected_accumulator, accumulator
)));
}
let has_xen_pmi_bug = count as u64 > NUM_BRANCHES || count == -1;
if has_xen_pmi_bug {
Err(PmuValidationError::IntelXenPmiBugDetected)
} else {
Ok(())
}
}