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