Skip to main content

secure_types/
vec.rs

1#[cfg(not(feature = "use_os"))]
2use alloc::{Layout, alloc, dealloc};
3
4#[cfg(feature = "use_os")]
5use std::vec::Vec;
6
7use super::{Error, SecureArray};
8use core::{
9   marker::PhantomData,
10   mem,
11   ops::{Bound, RangeBounds},
12   ptr::{self, NonNull},
13};
14use zeroize::{DefaultIsZeroes, Zeroize};
15
16#[cfg(feature = "use_os")]
17use super::{alloc, free};
18#[cfg(feature = "use_os")]
19use memsec::Prot;
20
21pub type SecureBytes = SecureVec<u8>;
22
23/// A securely allocated, growable vector, just like `std::vec::Vec`.
24///
25/// ## Security Model
26///
27/// When compiled with the `use_os` feature (the default), it provides several layers of protection:
28/// - **Zeroization on Drop**: The memory is zeroized when the vector is dropped.
29/// - **Memory Locking**: The underlying memory pages are locked using `mlock` & `madvise` for (Unix) or
30///   `VirtualLock` & `VirtualProtect` for (Windows) to prevent the OS from memory-dump/swap to disk or other processes accessing the memory.
31///
32/// In a `no_std` environment, it falls back to providing only the **zeroization-on-drop** guarantee.
33///
34/// ## Program Termination
35///
36/// Direct indexing (e.g., `vec[0]`) on a locked vector will cause the operating system
37/// to terminate the process with an access violation error.
38///
39/// Always use the provided scope methods (`unlock_slice`, `unlock_slice_mut`) for safe access.
40///
41/// # Notes
42///
43/// If you return a new allocated `Vec` from one of the unlock methods you are responsible for zeroizing the memory.
44///
45/// # Example
46///
47/// Using `SecureBytes` (a type alias for `SecureVec<u8>`) to handle a secret key.
48///
49/// ```
50/// use secure_types::{SecureBytes, Zeroize};
51///
52/// // Create a new, empty secure vector.
53/// let mut secret_key = SecureBytes::new().unwrap();
54///
55/// // Push some sensitive data into it.
56/// secret_key.push(0xAB);
57/// secret_key.push(0xCD);
58/// secret_key.push(0xEF);
59///
60/// // The memory is locked here.
61///
62/// // Use a scope to safely access the contents as a slice.
63/// secret_key.unlock_slice(|unlocked_slice| {
64///     assert_eq!(unlocked_slice, &[0xAB, 0xCD, 0xEF]);
65/// });
66///
67/// // Not recommended but if you allocate a new Vec make sure to zeroize it
68/// let mut exposed = secret_key.unlock_slice(|unlocked_slice| {
69///     Vec::from(unlocked_slice)
70/// });
71///
72/// // Do what you need to to do with the new vector
73/// // When you are done with it, zeroize it
74/// exposed.zeroize();
75///
76/// // The memory is automatically locked again when the scope ends.
77///
78/// // When `secret_key` is dropped, its memory is securely zeroized.
79/// ```
80pub struct SecureVec<T>
81where
82   T: Zeroize,
83{
84   ptr: NonNull<T>,
85   pub(crate) len: usize,
86   pub(crate) capacity: usize,
87   _marker: PhantomData<T>,
88}
89
90unsafe impl<T: Zeroize + Send> Send for SecureVec<T> {}
91unsafe impl<T: Zeroize + Send + Sync> Sync for SecureVec<T> {}
92
93impl<T: Zeroize> SecureVec<T> {
94   /// Create a new `SecureVec` with a capacity of 1
95   pub fn new() -> Result<Self, Error> {
96      let capacity = 1;
97      let size = capacity * mem::size_of::<T>();
98      let ptr = unsafe { alloc::<T>(size)? };
99
100      let secure = SecureVec {
101         ptr,
102         len: 0,
103         capacity,
104         _marker: PhantomData,
105      };
106
107      let locked = secure.lock_memory();
108
109      #[cfg(feature = "use_os")]
110      if !locked {
111         return Err(Error::LockFailed);
112      }
113
114      Ok(secure)
115   }
116
117   /// Create a new `SecureVec` with the given capacity
118   pub fn new_with_capacity(mut capacity: usize) -> Result<Self, Error> {
119      if capacity == 0 {
120         capacity = 1;
121      }
122
123      capacity
124         .checked_mul(size_of::<T>())
125         .ok_or(Error::AllocationFailed)?;
126
127      let size = capacity * mem::size_of::<T>();
128      let ptr = unsafe { alloc::<T>(size)? };
129
130      let secure = SecureVec {
131         ptr,
132         len: 0,
133         capacity,
134         _marker: PhantomData,
135      };
136
137      let locked = secure.lock_memory();
138
139      #[cfg(feature = "use_os")]
140      if !locked {
141         return Err(Error::LockFailed);
142      }
143
144      Ok(secure)
145   }
146
147   #[cfg(feature = "use_os")]
148   /// Create a new `SecureVec` from a `Vec`
149   ///
150   /// The `Vec` is zeroized afterwards
151   pub fn from_vec(mut vec: Vec<T>) -> Result<Self, Error> {
152      if vec.capacity() == 0 {
153         vec.reserve(1);
154      }
155
156      let capacity = vec.capacity();
157      let len = vec.len();
158
159      capacity
160         .checked_mul(size_of::<T>())
161         .ok_or(Error::AllocationFailed)?;
162
163      let size = capacity * mem::size_of::<T>();
164
165      let ptr = match unsafe { alloc::<T>(size) } {
166         Ok(ptr) => ptr,
167         Err(_) => {
168            vec.zeroize();
169            return Err(Error::AllocationFailed);
170         }
171      };
172
173      // Copy data from the old pointer to the new one
174      unsafe {
175         core::ptr::copy_nonoverlapping(vec.as_ptr(), ptr.as_ptr() as *mut T, len);
176      }
177
178      vec.zeroize();
179
180      let secure = SecureVec {
181         ptr,
182         len,
183         capacity,
184         _marker: PhantomData,
185      };
186
187      let locked = secure.lock_memory();
188
189      if !locked {
190         return Err(Error::LockFailed);
191      }
192
193      Ok(secure)
194   }
195
196   /// Create a new `SecureVec` from a mutable slice.
197   ///
198   /// The slice is zeroized afterwards
199   pub fn from_slice_mut(slice: &mut [T]) -> Result<Self, Error>
200   where
201      T: Clone + DefaultIsZeroes,
202   {
203      let mut secure_vec = SecureVec::new_with_capacity(slice.len())?;
204      secure_vec.init_from_clone(slice);
205      slice.zeroize();
206      Ok(secure_vec)
207   }
208
209   /// Create a new `SecureVec` from a slice.
210   ///
211   /// The slice is not zeroized, you are responsible for zeroizing it
212   pub fn from_slice(slice: &[T]) -> Result<Self, Error>
213   where
214      T: Clone,
215   {
216      let mut secure_vec = SecureVec::new_with_capacity(slice.len())?;
217      secure_vec.init_from_clone(slice);
218      Ok(secure_vec)
219   }
220
221   pub fn len(&self) -> usize {
222      self.len
223   }
224
225   pub fn is_empty(&self) -> bool {
226      self.len() == 0
227   }
228
229   pub(crate) fn as_mut_ptr(&mut self) -> *mut u8 {
230      self.ptr.as_ptr() as *mut u8
231   }
232
233   pub(crate) fn lock_memory(&self) -> bool {
234      #[cfg(feature = "use_os")]
235      {
236         #[cfg(windows)]
237         {
238            super::mprotect(self.ptr, Prot::NoAccess)
239         }
240         #[cfg(unix)]
241         {
242            super::mprotect(self.ptr, Prot::NoAccess)
243         }
244      }
245      #[cfg(not(feature = "use_os"))]
246      {
247         true // No-op: always "succeeds"
248      }
249   }
250
251   pub(crate) fn unlock_memory(&self) -> bool {
252      #[cfg(feature = "use_os")]
253      {
254         #[cfg(windows)]
255         {
256            super::mprotect(self.ptr, Prot::ReadWrite)
257         }
258         #[cfg(unix)]
259         {
260            super::mprotect(self.ptr, Prot::ReadWrite)
261         }
262      }
263
264      #[cfg(not(feature = "use_os"))]
265      {
266         true // No-op: always "succeeds"
267      }
268   }
269
270   /// Immutable access to the `SecureVec`
271   pub fn unlock<F, R>(&self, f: F) -> R
272   where
273      F: FnOnce(&SecureVec<T>) -> R,
274   {
275      self.unlock_memory();
276      let result = f(self);
277      self.lock_memory();
278      result
279   }
280
281   /// Immutable access to the `SecureVec` as `&[T]`
282   pub fn unlock_slice<F, R>(&self, f: F) -> R
283   where
284      F: FnOnce(&[T]) -> R,
285   {
286      unsafe {
287         self.unlock_memory();
288         let slice = core::slice::from_raw_parts(self.ptr.as_ptr(), self.len);
289         let result = f(slice);
290         self.lock_memory();
291         result
292      }
293   }
294
295   /// Mutable access to the `SecureVec` as `&mut [T]`
296   pub fn unlock_slice_mut<F, R>(&mut self, f: F) -> R
297   where
298      F: FnOnce(&mut [T]) -> R,
299   {
300      unsafe {
301         self.unlock_memory();
302         let slice = core::slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len);
303         let result = f(slice);
304         self.lock_memory();
305         result
306      }
307   }
308
309   /// Immutable access to the `SecureVec` as `Iter<T>`
310   pub fn unlock_iter<F, R>(&self, f: F) -> R
311   where
312      F: FnOnce(core::slice::Iter<T>) -> R,
313   {
314      unsafe {
315         self.unlock_memory();
316         let slice = core::slice::from_raw_parts(self.ptr.as_ptr(), self.len);
317         let iter = slice.iter();
318         let result = f(iter);
319         self.lock_memory();
320         result
321      }
322   }
323
324   /// Mutable access to the `SecureVec` as `IterMut<T>`
325   pub fn unlock_iter_mut<F, R>(&mut self, f: F) -> R
326   where
327      F: FnOnce(core::slice::IterMut<T>) -> R,
328   {
329      unsafe {
330         self.unlock_memory();
331         let slice = core::slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len);
332         let iter = slice.iter_mut();
333         let result = f(iter);
334         self.lock_memory();
335         result
336      }
337   }
338
339   /// Erase the underlying data and clears the vector
340   ///
341   /// The memory is locked again and the capacity is preserved for reuse
342   pub fn erase(&mut self) {
343      unsafe {
344         self.unlock_memory();
345         let slice = core::slice::from_raw_parts_mut(self.ptr.as_ptr(), self.capacity);
346         for elem in slice.iter_mut() {
347            elem.zeroize();
348         }
349         self.clear();
350         self.lock_memory();
351      }
352   }
353
354   /// Clear the vector
355   ///
356   /// This just sets the vector's len to zero it does not erase the underlying data
357   pub fn clear(&mut self) {
358      self.len = 0;
359   }
360
361   pub fn push(&mut self, value: T) {
362      self.reserve(1);
363
364      self.unlock_memory();
365
366      unsafe {
367         // Write the new value at the end of the vector.
368         core::ptr::write(self.ptr.as_ptr().add(self.len), value);
369
370         self.len += 1;
371      }
372
373      self.lock_memory();
374   }
375
376   /// Ensures that the vector has enough capacity for at least `additional` more elements.
377   ///
378   /// If more capacity is needed, it will reallocate. This may cause the buffer location to change.
379   ///
380   /// # Panics
381   ///
382   /// Panics if the new capacity overflows `usize` or if the allocation fails.
383   pub fn reserve(&mut self, additional: usize) {
384      if self.len() + additional <= self.capacity {
385         return;
386      }
387
388      // Use an amortized growth strategy to avoid reallocating on every push
389      let required_capacity = self.len() + additional;
390      let new_capacity = (self.capacity.max(1) * 2).max(required_capacity);
391
392      let new_size = new_capacity * mem::size_of::<T>();
393      let new_ptr = unsafe {
394         alloc::<T>(new_size).unwrap_or_else(|_| {
395            panic!(
396               "secure-types: failed to allocate {} bytes of locked memory \
397          (possibly RLIMIT_MEMLOCK exhausted); SecureVec left unchanged",
398               new_size
399            )
400         })
401      };
402
403      // Copy data to new pointer
404      unsafe {
405         self.unlock_memory();
406         core::ptr::copy_nonoverlapping(
407            self.ptr.as_ptr(),
408            new_ptr.as_ptr() as *mut T,
409            self.len(),
410         );
411
412         // Erase and free the old memory
413         if self.capacity > 0 {
414            let slice = core::slice::from_raw_parts_mut(self.ptr.as_ptr(), self.capacity);
415            for elem in slice.iter_mut() {
416               elem.zeroize();
417            }
418         }
419
420         #[cfg(feature = "use_os")]
421         free(self.ptr);
422
423         #[cfg(not(feature = "use_os"))]
424         {
425            let old_size = self.capacity * mem::size_of::<T>();
426            let old_layout = Layout::from_size_align_unchecked(old_size, mem::align_of::<T>());
427            dealloc(self.ptr.as_ptr() as *mut u8, old_layout);
428         }
429      }
430
431      // Update pointer and capacity, then re-lock the new memory region
432      self.ptr = new_ptr;
433      self.capacity = new_capacity;
434      self.lock_memory();
435   }
436
437   /// Creates a draining iterator that removes the specified range from the vector
438   /// and yields the removed items.
439   ///
440   /// Note: The vector is unlocked during the lifetime of the `Drain` iterator.
441   /// The memory is relocked when the `Drain` iterator is dropped.
442   ///
443   /// # Panics
444   /// Panics if the starting point is greater than the end point or if the end point
445   /// is greater than the length of the vector.
446   pub fn drain<R>(&mut self, range: R) -> Drain<'_, T>
447   where
448      R: RangeBounds<usize>,
449   {
450      self.unlock_memory();
451
452      let original_len = self.len;
453
454      let (drain_start_idx, drain_end_idx) = resolve_range_indices(range, original_len);
455
456      let tail_len = original_len - drain_end_idx;
457
458      self.len = drain_start_idx;
459
460      Drain {
461         vec_ref: self,
462         drain_start_index: drain_start_idx,
463         current_drain_iter_index: drain_start_idx,
464         drain_end_index: drain_end_idx,
465         original_vec_len: original_len,
466         tail_len,
467         _marker: PhantomData,
468      }
469   }
470
471   /// Initializes a freshly-allocated (uninitialized) buffer by cloning `src`
472   /// into it. Uses `ptr::write` so the uninitialized destination slots are
473   /// never read, never dropped, and no `&mut [T]` is ever formed over them.
474   ///
475   /// `len` is set only after every write succeeds, so a panic from
476   /// `T::clone` leaves the vector at its previous length (0 for a fresh one).
477   pub(crate) fn init_from_clone(&mut self, src: &[T])
478   where
479      T: Clone,
480   {
481      debug_assert!(src.len() <= self.capacity);
482
483      self.unlock_memory();
484      unsafe {
485         let dst = self.ptr.as_ptr();
486         for (i, item) in src.iter().enumerate() {
487            core::ptr::write(dst.add(i), item.clone());
488         }
489      }
490      self.len = src.len();
491      self.lock_memory();
492   }
493}
494
495impl<T: Clone + Zeroize> Clone for SecureVec<T> {
496   fn clone(&self) -> Self {
497      let mut new_vec = SecureVec::new_with_capacity(self.capacity).unwrap();
498      self.unlock_slice(|src_slice| {
499         new_vec.init_from_clone(src_slice);
500      });
501      new_vec
502   }
503}
504
505impl<const LENGTH: usize> From<SecureArray<u8, LENGTH>> for SecureVec<u8> {
506   fn from(array: SecureArray<u8, LENGTH>) -> Self {
507      let mut new_vec = SecureVec::new_with_capacity(LENGTH)
508         .expect("Failed to allocate SecureVec during conversion");
509      array.unlock(|array_slice| {
510         new_vec.init_from_clone(array_slice);
511      });
512      new_vec
513   }
514}
515
516impl<T: Zeroize> Drop for SecureVec<T> {
517   fn drop(&mut self) {
518      self.erase();
519      self.unlock_memory();
520
521      #[cfg(feature = "use_os")]
522      free(self.ptr);
523
524      #[cfg(not(feature = "use_os"))]
525      {
526         let layout =
527            Layout::from_size_align_unchecked(self.allocated_byte_size(), mem::align_of::<T>());
528         dealloc(self.ptr.as_ptr() as *mut u8, layout);
529      }
530   }
531}
532
533impl<T: Zeroize> core::ops::Index<usize> for SecureVec<T> {
534   type Output = T;
535
536   fn index(&self, index: usize) -> &Self::Output {
537      assert!(index < self.len, "Index out of bounds");
538      unsafe {
539         let ptr = self.ptr.as_ptr().add(index);
540         &*ptr
541      }
542   }
543}
544
545#[cfg(feature = "serde")]
546impl serde::Serialize for SecureVec<u8> {
547   fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
548   where
549      S: serde::Serializer,
550   {
551      self.unlock_slice(|slice| serializer.collect_seq(slice.iter()))
552   }
553}
554
555#[cfg(feature = "serde")]
556impl<'de> serde::Deserialize<'de> for SecureVec<u8> {
557   fn deserialize<D>(deserializer: D) -> Result<SecureVec<u8>, D::Error>
558   where
559      D: serde::Deserializer<'de>,
560   {
561      struct SecureVecVisitor;
562      impl<'de> serde::de::Visitor<'de> for SecureVecVisitor {
563         type Value = SecureVec<u8>;
564         fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
565            write!(formatter, "a sequence of bytes")
566         }
567         fn visit_seq<A>(
568            self,
569            mut seq: A,
570         ) -> Result<<Self as serde::de::Visitor<'de>>::Value, A::Error>
571         where
572            A: serde::de::SeqAccess<'de>,
573         {
574            let mut vec = SecureVec::new().map_err(serde::de::Error::custom)?;
575            while let Some(byte) = seq.next_element::<u8>()? {
576               vec.push(byte);
577            }
578            Ok(vec)
579         }
580      }
581      deserializer.deserialize_seq(SecureVecVisitor)
582   }
583}
584
585/// A draining iterator for `SecureVec<T>`.
586///
587/// This struct is created by the `drain` method on `SecureVec`.
588///
589/// # Safety
590/// The returned `Drain` iterator must not be forgotten (via `mem::forget`).
591/// Forgetting the iterator sets the len of `SecureVec` to 0 and the memory will remain unlocked
592pub struct Drain<'a, T: Zeroize + 'a> {
593   vec_ref: &'a mut SecureVec<T>,
594   drain_start_index: usize,
595   current_drain_iter_index: usize,
596   drain_end_index: usize,
597
598   original_vec_len: usize, // Original length of vec_ref before drain
599   tail_len: usize,         // Number of elements after the drain range in the original vec
600
601   _marker: PhantomData<&'a T>,
602}
603
604impl<'a, T: Zeroize> Iterator for Drain<'a, T> {
605   type Item = T;
606
607   fn next(&mut self) -> Option<T> {
608      if self.current_drain_iter_index < self.drain_end_index {
609         // SecureVec is already unlocked by the `drain` method.
610         unsafe {
611            let item_ptr = self.vec_ref.ptr.as_ptr().add(self.current_drain_iter_index);
612            let item = ptr::read(item_ptr);
613            self.current_drain_iter_index += 1;
614            Some(item)
615         }
616      } else {
617         None
618      }
619   }
620
621   fn size_hint(&self) -> (usize, Option<usize>) {
622      let remaining = self.drain_end_index - self.current_drain_iter_index;
623      (remaining, Some(remaining))
624   }
625}
626
627impl<'a, T: Zeroize> ExactSizeIterator for Drain<'a, T> {}
628
629impl<'a, T: Zeroize> Drop for Drain<'a, T> {
630   fn drop(&mut self) {
631      unsafe {
632         // The vec_ref's memory is currently unlocked.
633         if mem::needs_drop::<T>() {
634            let mut current_ptr = self.vec_ref.ptr.as_ptr().add(self.current_drain_iter_index);
635            let end_ptr = self.vec_ref.ptr.as_ptr().add(self.drain_end_index);
636            while current_ptr < end_ptr {
637               ptr::drop_in_place(current_ptr);
638               current_ptr = current_ptr.add(1);
639            }
640         }
641
642         let hole_dst_ptr = self.vec_ref.ptr.as_ptr().add(self.drain_start_index);
643         let tail_src_ptr = self.vec_ref.ptr.as_ptr().add(self.drain_end_index);
644
645         if self.tail_len > 0 {
646            ptr::copy(tail_src_ptr, hole_dst_ptr, self.tail_len);
647         }
648
649         // The new length of the vector.
650         let new_len = self.drain_start_index + self.tail_len;
651
652         // Process the memory region that is no longer part of the active vector's content.
653         // This region is from `vec_ref.ptr + new_len` up to `vec_ref.ptr + original_vec_len`.
654         // It contains:
655         //    a) Original data of the latter part of the drained slice (if not overwritten by tail).
656         //       These were dropped in step 1 if T:Drop.
657         //    b) Original data of the tail items (which have now been copied).
658         //       These need to be dropped if T:Drop, as ptr::copy doesn't drop the source.
659         // After any necessary drops, this entire region must be zeroized.
660
661         let mut current_cleanup_ptr = self.vec_ref.ptr.as_ptr().add(new_len);
662         let end_cleanup_ptr = self.vec_ref.ptr.as_ptr().add(self.original_vec_len);
663
664         // Determine the start of the original tail's memory region
665         let original_tail_start_ptr_val = tail_src_ptr as usize;
666
667         while current_cleanup_ptr < end_cleanup_ptr {
668            if mem::needs_drop::<T>() {
669               let current_ptr_val = current_cleanup_ptr as usize;
670               let original_tail_end_ptr_val =
671                  original_tail_start_ptr_val + self.tail_len * mem::size_of::<T>();
672
673               if current_ptr_val >= original_tail_start_ptr_val
674                  && current_ptr_val < original_tail_end_ptr_val
675               {
676                  // This element was part of the original tail. ptr::copy moved its value.
677                  // The original instance here needs to be dropped.
678                  ptr::drop_in_place(current_cleanup_ptr);
679               }
680               // Else, it was part of the drained range (not covered by tail move).
681               // If it needed dropping, it was handled in step 1.
682            }
683
684            // Zeroize the memory of this element.
685            (*current_cleanup_ptr).zeroize();
686            current_cleanup_ptr = current_cleanup_ptr.add(1);
687         }
688
689         // Update the SecureVec's length.
690         self.vec_ref.len = new_len;
691
692         // Relock the SecureVec's memory.
693         self.vec_ref.lock_memory();
694      }
695   }
696}
697
698// Helper function to resolve RangeBounds to (start, end) indices
699fn resolve_range_indices<R: RangeBounds<usize>>(range: R, len: usize) -> (usize, usize) {
700   let start_bound = range.start_bound();
701   let end_bound = range.end_bound();
702
703   let start = match start_bound {
704      Bound::Included(&s) => s,
705      Bound::Excluded(&s) => s
706         .checked_add(1)
707         .unwrap_or_else(|| panic!("attempted to start drain at Excluded(usize::MAX)")),
708      Bound::Unbounded => 0,
709   };
710
711   let end = match end_bound {
712      Bound::Included(&e) => e
713         .checked_add(1)
714         .unwrap_or_else(|| panic!("attempted to end drain at Included(usize::MAX)")),
715      Bound::Excluded(&e) => e,
716      Bound::Unbounded => len,
717   };
718
719   if start > end {
720      panic!(
721         "drain range start ({}) must be less than or equal to end ({})",
722         start, end
723      );
724   }
725   if end > len {
726      panic!(
727         "drain range end ({}) out of bounds for slice of length {}",
728         end, len
729      );
730   }
731
732   (start, end)
733}
734
735#[cfg(all(test, feature = "use_os"))]
736mod tests {
737   use super::*;
738   use std::process::{Command, Stdio};
739   use std::sync::{Arc, Mutex};
740
741   #[test]
742   fn test_creation() {
743      let vec: Vec<u8> = vec![1, 2, 3];
744      let secure_vec = SecureVec::from_vec(vec).unwrap();
745
746      secure_vec.unlock_slice(|slice| {
747         assert_eq!(slice, &[1, 2, 3]);
748      });
749
750      let exposed_slice = &mut [1, 2, 3];
751      let secure_slice = SecureVec::from_slice_mut(exposed_slice).unwrap();
752      assert_eq!(exposed_slice, &[0u8; 3]);
753
754      secure_slice.unlock_slice(|slice| {
755         assert_eq!(slice, &[1, 2, 3]);
756      });
757
758      let exposed_slice = [1, 2, 3];
759      let secure_slice = SecureVec::from_slice(&exposed_slice).unwrap();
760
761      secure_slice.unlock_slice(|slice| {
762         assert_eq!(slice, exposed_slice);
763      });
764   }
765
766   #[test]
767   fn test_from_secure_array() {
768      let exposed: &mut [u8; 3] = &mut [1, 2, 3];
769      let array: SecureArray<u8, 3> = SecureArray::from_slice_mut(exposed).unwrap();
770      let vec: SecureVec<u8> = array.into();
771      assert_eq!(vec.len(), 3);
772      vec.unlock_slice(|slice| {
773         assert_eq!(slice, &[1, 2, 3]);
774      });
775   }
776
777   #[test]
778   fn lock_unlock_works() {
779      let secure: SecureVec<u8> = SecureVec::new().unwrap();
780
781      let unlocked = secure.unlock_memory();
782      assert!(unlocked);
783
784      let locked = secure.lock_memory();
785      assert!(locked);
786   }
787
788   #[test]
789   fn test_thread_safety() {
790      let vec: Vec<u8> = vec![];
791      let secure = SecureVec::from_vec(vec).unwrap();
792      let secure = Arc::new(Mutex::new(secure));
793
794      let mut handles = Vec::new();
795      for i in 0..5u8 {
796         let secure_clone = secure.clone();
797         let handle = std::thread::spawn(move || {
798            let mut secure = secure_clone.lock().unwrap();
799            secure.push(i);
800         });
801         handles.push(handle);
802      }
803
804      for handle in handles {
805         handle.join().unwrap();
806      }
807
808      let mut sec = secure.lock().unwrap();
809      sec.unlock_slice_mut(|slice| {
810         slice.sort();
811         assert_eq!(slice.len(), 5);
812         assert_eq!(slice, &[0, 1, 2, 3, 4]);
813      });
814   }
815
816   #[test]
817   fn test_clone() {
818      let vec: Vec<u8> = vec![1, 2, 3];
819      let secure1 = SecureVec::from_vec(vec).unwrap();
820      let secure2 = secure1.clone();
821
822      secure1.unlock_slice(|slice| {
823         secure2.unlock_slice(|slice2| {
824            assert_eq!(slice, slice2);
825         });
826      });
827   }
828
829   #[test]
830   fn test_do_not_call_forget_on_drain() {
831      let vec: Vec<u8> = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
832      let mut secure = SecureVec::from_vec(vec).unwrap();
833      let drain = secure.drain(..3);
834      core::mem::forget(drain);
835      // we can still use secure vec but its state is unreachable
836      secure.unlock_slice(|secure| {
837         assert_eq!(secure.len(), 0);
838      });
839   }
840
841   #[test]
842   fn test_drain() {
843      let vec: Vec<u8> = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
844      let mut secure = SecureVec::from_vec(vec).unwrap();
845      let mut drain = secure.drain(..3);
846      assert_eq!(drain.next(), Some(1));
847      assert_eq!(drain.next(), Some(2));
848      assert_eq!(drain.next(), Some(3));
849      assert_eq!(drain.next(), None);
850      drop(drain);
851      secure.unlock_slice(|secure| {
852         assert_eq!(secure.len(), 7);
853         assert_eq!(secure, &[4, 5, 6, 7, 8, 9, 10]);
854      });
855   }
856
857   #[cfg(feature = "serde")]
858   #[test]
859   fn test_secure_vec_serde() {
860      let vec: Vec<u8> = vec![1, 2, 3];
861      let secure = SecureVec::from_vec(vec).unwrap();
862      let json = serde_json::to_vec(&secure).expect("Serialization failed");
863      let deserialized: SecureVec<u8> =
864         serde_json::from_slice(&json).expect("Deserialization failed");
865      deserialized.unlock_slice(|slice| {
866         assert_eq!(slice, &[1, 2, 3]);
867      });
868   }
869
870   #[test]
871   fn test_erase() {
872      let mut secure = SecureVec::new_with_capacity(10).unwrap();
873      for i in 0..9 {
874         secure.push(i);
875      }
876
877      secure.erase();
878
879      secure.unlock(|secure| {
880         assert_eq!(secure.len, 0);
881         assert_eq!(secure.capacity, 10);
882      });
883
884      secure.unlock_iter(|iter| {
885         for elem in iter {
886            assert_eq!(elem, &0);
887         }
888      });
889   }
890
891   #[test]
892   fn test_push() {
893      let vec: Vec<u8> = Vec::new();
894      let mut secure = SecureVec::from_vec(vec).unwrap();
895      for i in 0..10 {
896         secure.push(i);
897      }
898
899      assert_eq!(secure.len(), 10);
900
901      secure.unlock_slice(|slice| {
902         assert_eq!(slice, &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
903      });
904   }
905
906   #[test]
907   fn test_reserve() {
908      let mut secure: SecureVec<u8> = SecureVec::new().unwrap();
909      secure.reserve(10);
910      assert_eq!(secure.capacity, 10);
911   }
912
913   #[test]
914   fn test_reserve_doubling() {
915      let mut secure: SecureVec<u8> = SecureVec::new().unwrap();
916      secure.reserve(10);
917
918      for i in 0..9 {
919         secure.push(i);
920      }
921
922      secure.push(9);
923      assert_eq!(secure.capacity, 10);
924      assert_eq!(secure.len(), 10);
925
926      secure.push(10);
927      assert_eq!(secure.capacity, 20);
928      assert_eq!(secure.len(), 11);
929   }
930
931   #[test]
932   fn test_index() {
933      let vec: Vec<u8> = vec![1, 2, 3];
934      let secure = SecureVec::from_vec(vec).unwrap();
935      secure.unlock(|secure| {
936         assert_eq!(secure[0], 1);
937         assert_eq!(secure[1], 2);
938         assert_eq!(secure[2], 3);
939      });
940   }
941
942   #[test]
943   fn test_unlock_slice() {
944      let vec: Vec<u8> = vec![1, 2, 3];
945      let secure = SecureVec::from_vec(vec).unwrap();
946      secure.unlock_slice(|slice| {
947         assert_eq!(slice, &[1, 2, 3]);
948      });
949   }
950
951   #[test]
952   fn test_unlock_slice_mut() {
953      let vec: Vec<u8> = vec![1, 2, 3];
954      let mut secure = SecureVec::from_vec(vec).unwrap();
955
956      secure.unlock_slice_mut(|slice| {
957         slice[0] = 4;
958         assert_eq!(slice, &mut [4, 2, 3]);
959      });
960   }
961
962   #[test]
963   fn test_unlock_iter() {
964      let vec: Vec<u8> = vec![1, 2, 3];
965      let secure = SecureVec::from_vec(vec).unwrap();
966      let sum: u8 = secure.unlock_iter(|iter| iter.map(|&x| x).sum());
967
968      assert_eq!(sum, 6);
969
970      let secure: SecureVec<u8> = SecureVec::new_with_capacity(3).unwrap();
971      let sum: u8 = secure.unlock_iter(|iter| iter.map(|&x| x).sum());
972
973      assert_eq!(sum, 0);
974   }
975
976   #[test]
977   fn test_unlock_iter_mut() {
978      let vec: Vec<u8> = vec![1, 2, 3];
979      let mut secure = SecureVec::from_vec(vec).unwrap();
980      secure.unlock_iter_mut(|iter| {
981         for elem in iter {
982            *elem += 1;
983         }
984      });
985
986      secure.unlock_slice(|slice| {
987         assert_eq!(slice, &[2, 3, 4]);
988      });
989   }
990
991   #[test]
992   fn test_index_should_fail_when_locked() {
993      let arg = "CRASH_TEST_SECUREVEC_LOCKED";
994
995      if std::env::args().any(|a| a == arg) {
996         let vec: Vec<u8> = vec![1, 2, 3];
997         let secure = SecureVec::from_vec(vec).unwrap();
998         let _value = core::hint::black_box(secure[0]);
999
1000         std::process::exit(1);
1001      }
1002
1003      let child = Command::new(std::env::current_exe().unwrap())
1004         .arg("vec::tests::test_index_should_fail_when_locked")
1005         .arg(arg)
1006         .arg("--nocapture")
1007         .stdout(Stdio::piped())
1008         .stderr(Stdio::piped())
1009         .spawn()
1010         .expect("Failed to spawn child process");
1011
1012      let output = child.wait_with_output().expect("Failed to wait on child");
1013      let status = output.status;
1014
1015      assert!(
1016         !status.success(),
1017         "Process exited successfully with code {:?}, but it should have crashed.",
1018         status.code()
1019      );
1020
1021      #[cfg(unix)]
1022      {
1023         use std::os::unix::process::ExitStatusExt;
1024         let signal = status
1025            .signal()
1026            .expect("Process was not terminated by a signal on Unix.");
1027         assert!(
1028            signal == libc::SIGSEGV || signal == libc::SIGBUS,
1029            "Process terminated with unexpected signal: {}",
1030            signal
1031         );
1032         println!(
1033            "Test passed: Process correctly terminated with signal {}.",
1034            signal
1035         );
1036      }
1037
1038      #[cfg(windows)]
1039      {
1040         const STATUS_ACCESS_VIOLATION: i32 = 0xC0000005_u32 as i32;
1041         assert_eq!(
1042            status.code(),
1043            Some(STATUS_ACCESS_VIOLATION),
1044            "Process exited with unexpected code: {:x?}. Expected STATUS_ACCESS_VIOLATION.",
1045            status.code()
1046         );
1047         eprintln!("Test passed: Process correctly terminated with STATUS_ACCESS_VIOLATION.");
1048      }
1049   }
1050}