1#[repr(C)]
44pub struct Canary<const MAGIC: u32> {
45 magic: u32,
46}
47
48impl<const MAGIC: u32> Canary<MAGIC> {
49 pub const fn new() -> Self {
51 Canary { magic: MAGIC }
52 }
53
54 pub fn assert(&self) {
60 let observed_magic = unsafe { core::ptr::read_volatile(&self.magic) };
61 if observed_magic != MAGIC {
62 canary_failed(MAGIC, observed_magic);
63 }
64 }
65
66 pub fn valid(&self) -> bool {
70 let observed_magic = unsafe { core::ptr::read_volatile(&self.magic) };
71 observed_magic == MAGIC
72 }
73}
74
75impl<const MAGIC: u32> Default for Canary<MAGIC> {
76 fn default() -> Self {
77 Self::new()
78 }
79}
80
81impl<const MAGIC: u32> Drop for Canary<MAGIC> {
82 fn drop(&mut self) {
83 self.assert();
84 unsafe {
85 core::ptr::write_volatile(&mut self.magic, 0);
86 }
87 }
88}
89
90#[cold]
91#[inline(never)]
92#[track_caller]
93fn canary_failed(expected: u32, observed: u32) -> ! {
94 panic!("Invalid canary (expt: {:08x}, got: {:08x})", expected, observed);
95}
96
97pub const fn magic(str: &[u8; 4]) -> u32 {
99 ((str[0] as u32) << 24) | ((str[1] as u32) << 16) | ((str[2] as u32) << 8) | (str[3] as u32)
100}
101
102#[macro_export]
103macro_rules! canary {
104 ($str:literal) => {
105 $crate::Canary::<{ $crate::magic($str) }>::new()
106 };
107}
108
109#[cfg(test)]
110mod tests {
111 use super::*;
112
113 #[test]
114 fn test_canary_default() {
115 let canary: Canary<{ magic(b"test") }> = Default::default();
116 assert!(canary.valid());
117 }
118
119 #[test]
120 fn test_magic_runtime() {
121 let m = magic(b"abcd");
122 assert_eq!(m, 0x61626364);
123 }
124
125 #[test]
126 fn test_canary() {
127 let canary = canary!(b"test");
128 assert!(canary.valid());
129 canary.assert();
130 }
131
132 #[test]
133 #[should_panic(expected = "Invalid canary")]
134 fn test_canary_corruption() {
135 let mut canary = canary!(b"test");
136 unsafe {
138 core::ptr::write_volatile(&mut canary.magic, 0);
139 }
140 canary.assert();
141 }
142
143 unsafe extern "C" {
144 fn check_rust_canary(ptr: *const core::ffi::c_void, expected_magic: u32) -> bool;
145 }
146
147 #[test]
148 #[cfg_attr(miri, ignore = "miri does not support calling foreign functions")]
149 fn test_canary_ffi() {
150 const MAGIC_VAL: u32 = 0x12345678; let canary = Canary::<MAGIC_VAL>::new();
152 unsafe {
153 assert!(check_rust_canary(&canary as *const _ as *const core::ffi::c_void, MAGIC_VAL));
154 }
155 }
156}