il2_utils/vec/mod.rs
1/*
2 * BSD 3-Clause License
3 *
4 * Copyright (c) 2019-2020, InterlockLedger Network
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 *
10 * * Redistributions of source code must retain the above copyright notice, this
11 * list of conditions and the following disclaimer.
12 *
13 * * Redistributions in binary form must reproduce the above copyright notice,
14 * this list of conditions and the following disclaimer in the documentation
15 * and/or other materials provided with the distribution.
16 *
17 * * Neither the name of the copyright holder nor the names of its
18 * contributors may be used to endorse or promote products derived from
19 * this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32//! This module implements utilities to handle [`std::vec::Vec`]. Most of those
33//! utilities are optimized for fast memory manipulations whenever possible.
34//!
35//! It is important to notice that some methods and functions inside this
36//! package rely on unsafe code to achieve maximum performance.
37#[cfg(test)]
38mod tests;
39
40use zeroize::Zeroize;
41
42//=============================================================================
43// VecExtensions
44//-----------------------------------------------------------------------------
45/// This trait adds some extension methods to [`std::vec::Vec`] for primitive
46/// types like integers, floating points and booleans.
47///
48/// Most of those extensions are designed to be either fast implementations of
49/// existing methods or secure versions of them.
50///
51/// ## Safety
52///
53/// Some operations performed by this extension relies heavily on pointer
54/// operations and bitwise copies (see [`std::ptr`] for further details).
55/// Because of that, it is not safe to implement this trait for non primitive
56/// types because it may lead to memory safety violations and potential double
57/// free situations.
58///
59/// ## Secure variants
60///
61/// This extension also include secure variants of some of the vector methods.
62/// Those variants always zeroes the memory segments before releasing them back
63/// to the memory pool.
64///
65/// Logically, they do perform the same operations but they are way more
66/// expensive than their regular versions. We recommend the use of those
67/// versions if and only if you need to avoid potential confidential data
68/// leak to the system.
69///
70/// It is possible that, when the **allocator_api**
71/// [#32838](https://github.com/rust-lang/rust/issues/32838) become fully
72/// integrated into the standard API, those methods will no longer be
73/// necessary as the proper memory cleanup will be done by an
74/// [`std::alloc::Allocator`] instead of the hacks used by those methods.
75pub trait VecExtensions<T: Copy + Sized>: Zeroize {
76 /// Creates a new vector already initialized with the specified value.
77 ///
78 /// Since it is the first allocation, there is no need to have a secure version
79 /// of this constructor.
80 ///
81 /// Arguments:
82 /// - `value`: The initial value of the new Vec instance, the elements of this
83 /// slice are copied into the new vector;
84 fn with_value(value: &[T]) -> Vec<T>;
85
86 /// This method sets the capacity of the given Vec<u8> to hold at least the
87 /// specified amount of entries. It is similar to [`Vec<u8>::reserve()`] but it
88 /// takes the target capacity insted of an additional capacity.
89 ///
90 /// If the current capacity is equal or larger than the required capacity,
91 /// this method does nothing.
92 ///
93 /// Arguments:
94 /// - `capacity`: The new capacity;
95 fn set_capacity_to(&mut self, capacity: usize);
96
97 /// This method is the secure variant of [`Self::set_capacity_to()`].
98 ///
99 /// Arguments:
100 /// - `capacity`: The new capacity;
101 fn set_capacity_to_secure(&mut self, capacity: usize);
102
103 /// Replaces the contents of this vector with the contents of a given
104 /// slice. It will expand the size of this vector as needed but will
105 /// never shrink it.
106 ///
107 /// Arguments:
108 /// - `other`: The new capacity;
109 fn set_contents_from_slice(&mut self, other: &[T]);
110
111 /// This method is the secure variant of [`Self::set_contents_from_slice()`].
112 ///
113 /// Arguments:
114 /// - `other`: The new capacity;
115 fn set_contents_from_slice_secure(&mut self, other: &[T]);
116
117 /// This method is the secure version of [`std::vec::Vec::shrink_to_fit()`].
118 fn shrink_to_fit_secure(&mut self);
119
120 /// This method is the secure version of [`std::vec::Vec::reserve()`].
121 fn reserve_secure(&mut self, additional: usize);
122
123 /// This method is the secure version of [`std::vec::Vec::extend_from_slice()`].
124 fn extend_from_slice_secure(&mut self, other: &[T]);
125}
126
127macro_rules! vecextention_base_impl {
128 ($type: ty) => {
129 impl VecExtensions<$type> for Vec<$type> {
130 fn with_value(value: &[$type]) -> Vec<$type> {
131 let mut obj = Vec::with_capacity(value.len());
132 obj.set_contents_from_slice(value);
133 obj
134 }
135
136 fn set_capacity_to(&mut self, capacity: usize) {
137 let curr_capacity = self.capacity();
138 if curr_capacity < capacity {
139 self.reserve(capacity - self.len());
140 }
141 }
142
143 fn set_capacity_to_secure(&mut self, capacity: usize) {
144 let curr_capacity = self.capacity();
145 if curr_capacity < capacity {
146 if self.is_empty() {
147 // No data to move, just adjust the capacity
148 self.zeroize();
149 self.set_capacity_to(capacity);
150 } else if curr_capacity < capacity {
151 // Copy the values into a temporary buffer before resizing
152 // because it is not possible to ensure that the original
153 // buffer will not be replaced by a larger one. If this happens,
154 // the original data will be released to the memory pool with its
155 // contents intact and this is exactly what we are trying to avoid.
156 let mut tmp: Vec<$type> = Vec::with_capacity(self.len());
157 tmp.set_contents_from_slice(self.as_slice());
158 // Zeroize the original vector before resizing, also set its
159 // size to zero to avoid unecessary copy operation while resizing.
160 self.zeroize();
161 self.truncate(0);
162 // Sets the new capacity
163 self.set_capacity_to(capacity);
164 // Copy the values back into the original vector
165 assert!(self.capacity() >= tmp.len());
166 unsafe {
167 std::ptr::copy_nonoverlapping(
168 tmp.as_ptr(),
169 self.as_mut_ptr(),
170 tmp.len(),
171 );
172 self.set_len(tmp.len());
173 }
174 // Clear the temporay copy...
175 tmp.zeroize();
176 }
177 }
178 }
179
180 fn set_contents_from_slice(&mut self, other: &[$type]) {
181 self.set_capacity_to(other.len());
182 unsafe {
183 self.set_len(other.len());
184 std::ptr::copy_nonoverlapping(other.as_ptr(), self.as_mut_ptr(), other.len());
185 }
186 }
187
188 fn set_contents_from_slice_secure(&mut self, other: &[$type]) {
189 self.zeroize();
190 self.reserve(other.len());
191 unsafe {
192 self.set_len(other.len());
193 std::ptr::copy_nonoverlapping(other.as_ptr(), self.as_mut_ptr(), other.len());
194 }
195 }
196
197 fn shrink_to_fit_secure(&mut self) {
198 // Copy to a temporary value
199 let mut tmp: Vec<$type> = Vec::with_capacity(self.len());
200 tmp.set_contents_from_slice(self.as_slice());
201 // Clear the old data and shrink
202 self.zeroize();
203 self.shrink_to_fit();
204 // Copy the contents back into the array.
205 self.set_contents_from_slice(tmp.as_slice());
206 // Clear the temporary buffer
207 tmp.zeroize();
208 }
209
210 fn reserve_secure(&mut self, additional: usize) {
211 self.set_capacity_to_secure(self.len() + additional);
212 }
213
214 fn extend_from_slice_secure(&mut self, other: &[$type]) {
215 self.reserve_secure(other.len());
216 assert!(self.capacity() >= self.len() + other.len());
217 unsafe {
218 std::ptr::copy_nonoverlapping(
219 other.as_ptr(),
220 self.as_mut_ptr().add(self.len()),
221 other.len(),
222 );
223 self.set_len(self.len() + other.len());
224 }
225 }
226 }
227 };
228}
229
230macro_rules! multi_vecextention_base_impl {
231 ($type: ty) => {
232 vecextention_base_impl!($type);
233 };
234 ($type: ty, $($type2: ty), +) => {
235 vecextention_base_impl! ($type);
236 multi_vecextention_base_impl!($($type2), +);
237 };
238}
239
240multi_vecextention_base_impl!(bool);
241multi_vecextention_base_impl!(u8, u16, u32, u64, u128);
242multi_vecextention_base_impl!(i8, i16, i32, i64, i128);
243multi_vecextention_base_impl!(f32, f64);