Skip to main content

unsafe_tools_canary/
lib.rs

1#![doc = include_str!("../README.md")]
2
3use std::ops::{Deref, DerefMut};
4
5#[repr(C)]
6#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
7pub struct Canary<T, const CANARY: u64> {
8    _canary: u64,
9    inner: T,
10}
11
12impl<T, const CANARY: u64> Canary<T, CANARY> {
13    pub const fn new(inner: T) -> Self {
14        Self {
15            _canary: CANARY,
16            inner,
17        }
18    }
19
20    pub fn assert_valid(me: &Self) {
21        assert!(
22            me._canary == CANARY,
23            "canary mismatch! {} is not properly initialized (expected {CANARY} but found {})",
24            core::any::type_name::<T>(),
25            me._canary
26        );
27    }
28
29    pub fn into_inner(self) -> T {
30        self.inner
31    }
32}
33
34impl<T, const CANARY: u64> Deref for Canary<T, CANARY> {
35    type Target = T;
36
37    fn deref(&self) -> &Self::Target {
38        #[cfg(debug_assertions)]
39        Self::assert_valid(self);
40
41        &self.inner
42    }
43}
44
45impl<T, const CANARY: u64> DerefMut for Canary<T, CANARY> {
46    fn deref_mut(&mut self) -> &mut Self::Target {
47        #[cfg(debug_assertions)]
48        Self::assert_valid(self);
49
50        &mut self.inner
51    }
52}
53
54#[cfg(test)]
55mod tests {
56    use super::*;
57
58    #[test]
59    fn basically_works() {
60        const CANARY: u64 = u64::from_le_bytes(*b"testmagc");
61
62        let mut canary = Canary::<_, CANARY>::new(42);
63        assert_eq!(*canary, 42);
64        *canary += 1;
65        assert_eq!(*canary, 43);
66    }
67
68    #[test]
69    #[cfg_attr(debug_assertions, should_panic)]
70    fn panics_with_incorrect_canary() {
71        const CANARY: u64 = u64::from_le_bytes(*b"testmagc");
72
73        let mut canary = Canary::<_, CANARY>::new(42);
74        canary._canary = 10;
75        assert_eq!(*canary, 42);
76    }
77}