68 lines
1.9 KiB
Rust
68 lines
1.9 KiB
Rust
#[test]
|
|
fn select_unpredictable_drop() {
|
|
use core::cell::Cell;
|
|
|
|
struct X<'a>(&'a Cell<bool>);
|
|
impl const Drop for X<'_> {
|
|
fn drop(&mut self) {
|
|
self.0.set(true);
|
|
}
|
|
}
|
|
|
|
const fn do_test() {
|
|
let a_dropped = Cell::new(false);
|
|
let b_dropped = Cell::new(false);
|
|
let a = X(&a_dropped);
|
|
let b = X(&b_dropped);
|
|
assert!(!a_dropped.get());
|
|
assert!(!b_dropped.get());
|
|
let selected = core::hint::select_unpredictable(core::hint::black_box(true), a, b);
|
|
assert!(!a_dropped.get());
|
|
assert!(b_dropped.get());
|
|
drop(selected);
|
|
assert!(a_dropped.get());
|
|
assert!(b_dropped.get());
|
|
}
|
|
|
|
do_test();
|
|
const {
|
|
do_test();
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
#[should_panic = "message canary"]
|
|
fn select_unpredictable_drop_on_panic() {
|
|
use core::cell::Cell;
|
|
|
|
struct X<'a> {
|
|
cell: &'a Cell<u16>,
|
|
expect: u16,
|
|
write: u16,
|
|
}
|
|
|
|
impl Drop for X<'_> {
|
|
fn drop(&mut self) {
|
|
let value = self.cell.get();
|
|
self.cell.set(self.write);
|
|
assert_eq!(value, self.expect, "message canary");
|
|
}
|
|
}
|
|
|
|
let cell = Cell::new(0);
|
|
|
|
// Trigger a double-panic if the selected cell was not dropped during panic.
|
|
let _armed = X { cell: &cell, expect: 0xdead, write: 0 };
|
|
let selected = X { cell: &cell, write: 0xdead, expect: 1 };
|
|
let unselected = X { cell: &cell, write: 1, expect: 0xff };
|
|
|
|
// The correct drop order is:
|
|
//
|
|
// 1. `unselected` drops, writes 1, and panics as 0 != 0xff
|
|
// 2. `selected` drops during unwind, writes 0xdead and does not panic as 1 == 1
|
|
// 3. `armed` drops during unwind, writes 0 and does not panic as 0xdead == 0xdead
|
|
//
|
|
// If `selected` is not dropped, `armed` panics as 1 != 0xdead
|
|
let _unreachable = core::hint::select_unpredictable(true, selected, unselected);
|
|
}
|