1 //! zeroize integration tests.
2 
3 use std::{
4     marker::{PhantomData, PhantomPinned},
5     mem::{size_of, MaybeUninit},
6     num::*,
7 };
8 use zeroize::*;
9 
10 #[cfg(feature = "std")]
11 use std::ffi::CString;
12 
13 #[derive(Clone, Debug, PartialEq)]
14 struct ZeroizedOnDrop(u64);
15 
16 impl Drop for ZeroizedOnDrop {
drop(&mut self)17     fn drop(&mut self) {
18         self.0.zeroize();
19     }
20 }
21 
22 #[test]
non_zero()23 fn non_zero() {
24     macro_rules! non_zero_test {
25         ($($type:ty),+) => {
26             $(let mut value = <$type>::new(42).unwrap();
27             value.zeroize();
28             assert_eq!(value.get(), 1);)+
29         };
30     }
31 
32     non_zero_test!(
33         NonZeroI8,
34         NonZeroI16,
35         NonZeroI32,
36         NonZeroI64,
37         NonZeroI128,
38         NonZeroIsize,
39         NonZeroU8,
40         NonZeroU16,
41         NonZeroU32,
42         NonZeroU64,
43         NonZeroU128,
44         NonZeroUsize
45     );
46 }
47 
48 #[test]
zeroize_byte_arrays()49 fn zeroize_byte_arrays() {
50     let mut arr = [42u8; 137];
51     arr.zeroize();
52     assert_eq!(arr.as_ref(), [0u8; 137].as_ref());
53 }
54 
55 #[test]
zeroize_on_drop_byte_arrays()56 fn zeroize_on_drop_byte_arrays() {
57     let mut arr = [ZeroizedOnDrop(42); 1];
58     unsafe { core::ptr::drop_in_place(&mut arr) };
59     assert_eq!(arr.as_ref(), [ZeroizedOnDrop(0); 1].as_ref());
60 }
61 
62 #[test]
zeroize_maybeuninit_byte_arrays()63 fn zeroize_maybeuninit_byte_arrays() {
64     let mut arr = [MaybeUninit::new(42u64); 64];
65     arr.zeroize();
66     let arr_init: [u64; 64] = unsafe { core::mem::transmute(arr) };
67     assert_eq!(arr_init, [0u64; 64]);
68 }
69 
70 #[test]
zeroize_check_zerosize_types()71 fn zeroize_check_zerosize_types() {
72     // Since we assume these types have zero size, we test this holds for
73     // the current version of Rust.
74     assert_eq!(size_of::<()>(), 0);
75     assert_eq!(size_of::<PhantomPinned>(), 0);
76     assert_eq!(size_of::<PhantomData<usize>>(), 0);
77 }
78 
79 #[test]
zeroize_check_tuple()80 fn zeroize_check_tuple() {
81     let mut tup1 = (42u8,);
82     tup1.zeroize();
83     assert_eq!(tup1, (0u8,));
84 
85     let mut tup2 = (42u8, 42u8);
86     tup2.zeroize();
87     assert_eq!(tup2, (0u8, 0u8));
88 }
89 
90 #[test]
zeroize_on_drop_check_tuple()91 fn zeroize_on_drop_check_tuple() {
92     let mut tup1 = (ZeroizedOnDrop(42),);
93     unsafe { core::ptr::drop_in_place(&mut tup1) };
94     assert_eq!(tup1, (ZeroizedOnDrop(0),));
95 
96     let mut tup2 = (ZeroizedOnDrop(42), ZeroizedOnDrop(42));
97     unsafe { core::ptr::drop_in_place(&mut tup2) };
98     assert_eq!(tup2, (ZeroizedOnDrop(0), ZeroizedOnDrop(0)));
99 }
100 
101 #[cfg(feature = "alloc")]
102 #[test]
zeroize_vec()103 fn zeroize_vec() {
104     let mut vec = vec![42; 3];
105     vec.zeroize();
106     assert!(vec.is_empty());
107 }
108 
109 #[cfg(feature = "alloc")]
110 #[test]
zeroize_vec_entire_capacity()111 fn zeroize_vec_entire_capacity() {
112     #[derive(Clone)]
113     struct PanicOnNonZeroDrop(u64);
114 
115     impl Zeroize for PanicOnNonZeroDrop {
116         fn zeroize(&mut self) {
117             self.0 = 0;
118         }
119     }
120 
121     impl Drop for PanicOnNonZeroDrop {
122         fn drop(&mut self) {
123             if self.0 != 0 {
124                 panic!("dropped non-zeroized data");
125             }
126         }
127     }
128 
129     // Ensure that the entire capacity of the vec is zeroized and that no unitinialized data
130     // is ever interpreted as initialized
131     let mut vec = vec![PanicOnNonZeroDrop(42); 2];
132 
133     unsafe {
134         vec.set_len(1);
135     }
136 
137     vec.zeroize();
138 
139     unsafe {
140         vec.set_len(2);
141     }
142 
143     drop(vec);
144 }
145 
146 #[cfg(feature = "alloc")]
147 #[test]
zeroize_string()148 fn zeroize_string() {
149     let mut string = String::from("Hello, world!");
150     string.zeroize();
151     assert!(string.is_empty());
152 }
153 
154 #[cfg(feature = "alloc")]
155 #[test]
zeroize_string_entire_capacity()156 fn zeroize_string_entire_capacity() {
157     let mut string = String::from("Hello, world!");
158     string.truncate(5);
159 
160     string.zeroize();
161 
162     // convert the string to a vec to easily access the unused capacity
163     let mut as_vec = string.into_bytes();
164     unsafe { as_vec.set_len(as_vec.capacity()) };
165 
166     assert!(as_vec.iter().all(|byte| *byte == 0));
167 }
168 
169 // TODO(tarcieri): debug flaky test (with potential UB?) See: RustCrypto/utils#774
170 #[cfg(feature = "std")]
171 #[ignore]
172 #[test]
zeroize_c_string()173 fn zeroize_c_string() {
174     let mut cstring = CString::new("Hello, world!").expect("CString::new failed");
175     let orig_len = cstring.as_bytes().len();
176     let orig_ptr = cstring.as_bytes().as_ptr();
177     cstring.zeroize();
178     // This doesn't quite test that the original memory has been cleared, but only that
179     // cstring now owns an empty buffer
180     assert!(cstring.as_bytes().is_empty());
181     for i in 0..orig_len {
182         unsafe {
183             // Using a simple deref, only one iteration of the loop is performed
184             // presumably because after zeroize, the internal buffer has a length of one/
185             // `read_volatile` seems to "fix" this
186             // Note that this is very likely UB
187             assert_eq!(orig_ptr.add(i).read_volatile(), 0);
188         }
189     }
190 }
191 
192 #[cfg(feature = "alloc")]
193 #[test]
zeroize_box()194 fn zeroize_box() {
195     let mut boxed_arr = Box::new([42u8; 3]);
196     boxed_arr.zeroize();
197     assert_eq!(boxed_arr.as_ref(), &[0u8; 3]);
198 }
199 
200 #[cfg(feature = "alloc")]
201 #[test]
asref()202 fn asref() {
203     let mut buffer: Zeroizing<Vec<u8>> = Default::default();
204     let _asmut: &mut [u8] = buffer.as_mut();
205     let _asref: &[u8] = buffer.as_ref();
206 
207     let mut buffer: Zeroizing<Box<[u8]>> = Default::default();
208     let _asmut: &mut [u8] = buffer.as_mut();
209     let _asref: &[u8] = buffer.as_ref();
210 }
211