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