Branch data Line data Source code
1 : : /*
2 : : * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 : : *
4 : : * Licensed under the Apache License, Version 2.0 (the "License").
5 : : * You may not use this file except in compliance with the License.
6 : : * A copy of the License is located at
7 : : *
8 : : * http://aws.amazon.com/apache2.0
9 : : *
10 : : * or in the "license" file accompanying this file. This file is distributed
11 : : * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 : : * express or implied. See the License for the specific language governing
13 : : * permissions and limitations under the License.
14 : : */
15 : :
16 : : #define _DEFAULT_SOURCE 1
17 : : #if defined(S2N_FEATURES_AVAILABLE)
18 : : #include <features.h>
19 : : #endif
20 : :
21 : : #include <limits.h>
22 : : #include <stdint.h>
23 : : #include <stdlib.h>
24 : : #ifndef _WIN32
25 : : #include <sys/mman.h>
26 : : #include <sys/param.h>
27 : : #include <unistd.h>
28 : : #endif
29 : :
30 : : #include "error/s2n_errno.h"
31 : : #include "utils/s2n_blob.h"
32 : : #include "utils/s2n_mem.h"
33 : : #include "utils/s2n_safety.h"
34 : :
35 : : static uint32_t page_size = 4096;
36 : : static bool initialized = false;
37 : :
38 : : static int s2n_mem_init_impl(void);
39 : : static int s2n_mem_cleanup_impl(void);
40 : :
41 : : #ifdef _WIN32
42 : : static int s2n_windows_mem_free_impl(void *ptr, uint32_t size);
43 : : static int s2n_windows_mem_malloc_impl(void **ptr, uint32_t requested, uint32_t *allocated);
44 : : #else
45 : : static int s2n_mem_free_no_mlock_impl(void *ptr, uint32_t size);
46 : : static int s2n_mem_malloc_no_mlock_impl(void **ptr, uint32_t requested, uint32_t *allocated);
47 : : static int s2n_mem_free_mlock_impl(void *ptr, uint32_t size);
48 : : static int s2n_mem_malloc_mlock_impl(void **ptr, uint32_t requested, uint32_t *allocated);
49 : : #endif
50 : :
51 : : static s2n_mem_init_callback s2n_mem_init_cb = s2n_mem_init_impl;
52 : : static s2n_mem_cleanup_callback s2n_mem_cleanup_cb = s2n_mem_cleanup_impl;
53 : : #ifdef _WIN32
54 : : static s2n_mem_malloc_callback s2n_mem_malloc_cb = s2n_windows_mem_malloc_impl;
55 : : static s2n_mem_free_callback s2n_mem_free_cb = s2n_windows_mem_free_impl;
56 : : #else
57 : : static s2n_mem_malloc_callback s2n_mem_malloc_cb = s2n_mem_malloc_mlock_impl;
58 : : static s2n_mem_free_callback s2n_mem_free_cb = s2n_mem_free_mlock_impl;
59 : : #endif
60 : :
61 : : static int s2n_mem_init_impl(void)
62 : 344 : {
63 : 344 : #ifndef _WIN32
64 : 344 : long sysconf_rc = sysconf(_SC_PAGESIZE);
65 : :
66 : : /* sysconf must not error, and page_size cannot be 0 */
67 [ - + ][ # # ]: 344 : POSIX_ENSURE_GT(sysconf_rc, 0);
68 : :
69 : : /* page_size must be a valid uint32 */
70 : 344 : long max_page_size = S2N_MIN(UINT32_MAX, LONG_MAX);
71 [ - + ][ # # ]: 344 : POSIX_ENSURE_LTE(sysconf_rc, max_page_size);
72 : 344 : page_size = (uint32_t) sysconf_rc;
73 : :
74 [ + - ][ # # ]: 344 : if (getenv("S2N_DONT_MLOCK") || s2n_in_unit_test()) {
75 : 344 : s2n_mem_malloc_cb = s2n_mem_malloc_no_mlock_impl;
76 : 344 : s2n_mem_free_cb = s2n_mem_free_no_mlock_impl;
77 : 344 : }
78 : 344 : #endif
79 : 344 : return S2N_SUCCESS;
80 : 344 : }
81 : :
82 : : static int s2n_mem_cleanup_impl(void)
83 : 344 : {
84 : 344 : page_size = 4096;
85 : : #ifdef _WIN32
86 : : s2n_mem_malloc_cb = s2n_windows_mem_malloc_impl;
87 : : s2n_mem_free_cb = s2n_windows_mem_free_impl;
88 : : #else
89 : 344 : s2n_mem_malloc_cb = s2n_mem_malloc_no_mlock_impl;
90 : 344 : s2n_mem_free_cb = s2n_mem_free_no_mlock_impl;
91 : 344 : #endif
92 : 344 : return S2N_SUCCESS;
93 : 344 : }
94 : :
95 : : #ifdef _WIN32
96 : : /* mlock is not supported on Windows, so these use plain malloc/free without memory locking. */
97 : :
98 : : static int s2n_windows_mem_malloc_impl(void **ptr, uint32_t requested, uint32_t *allocated)
99 : : {
100 : : *ptr = malloc(requested);
101 : : POSIX_ENSURE(*ptr != NULL, S2N_ERR_ALLOC);
102 : : *allocated = requested;
103 : :
104 : : return S2N_SUCCESS;
105 : : }
106 : :
107 : : static int s2n_windows_mem_free_impl(void *ptr, uint32_t size)
108 : : {
109 : : free(ptr);
110 : : return S2N_SUCCESS;
111 : : }
112 : :
113 : : #else
114 : :
115 : : static int s2n_mem_free_mlock_impl(void *ptr, uint32_t size)
116 : 0 : {
117 : : /* Perform a best-effort `munlock`: ignore any errors during unlocking. */
118 : 0 : munlock(ptr, size);
119 : 0 : free(ptr);
120 : 0 : return S2N_SUCCESS;
121 : 0 : }
122 : :
123 : : static int s2n_mem_free_no_mlock_impl(void *ptr, uint32_t size)
124 : 7718526 : {
125 : 7718526 : free(ptr);
126 : :
127 : 7718526 : return S2N_SUCCESS;
128 : 7718526 : }
129 : :
130 : : static int s2n_mem_malloc_mlock_impl(void **ptr, uint32_t requested, uint32_t *allocated)
131 : 0 : {
132 [ # # ][ # # ]: 0 : POSIX_ENSURE_REF(ptr);
133 : :
134 : : /* Page aligned allocation required for mlock */
135 : 0 : uint32_t allocate = 0;
136 : :
137 [ # # ]: 0 : POSIX_GUARD(s2n_align_to(requested, page_size, &allocate));
138 : :
139 : 0 : *ptr = NULL;
140 [ # # ][ # # ]: 0 : POSIX_ENSURE(posix_memalign(ptr, page_size, allocate) == 0, S2N_ERR_ALLOC);
141 : 0 : *allocated = allocate;
142 : :
143 : : /*
144 : : ** We disable MAD_DONTDUMP when fuzz-testing or using the address sanitizer because
145 : : ** both need to be able to dump pages to function. It's how they map heap output.
146 : : */
147 : 0 : #if defined(MADV_DONTDUMP) && !defined(S2N_ADDRESS_SANITIZER) && !defined(S2N_FUZZ_TESTING)
148 [ # # ]: 0 : if (madvise(*ptr, *allocated, MADV_DONTDUMP) != 0) {
149 [ # # ]: 0 : POSIX_GUARD(s2n_mem_free_no_mlock_impl(*ptr, *allocated));
150 [ # # ]: 0 : POSIX_BAIL(S2N_ERR_MADVISE);
151 : 0 : }
152 : 0 : #endif
153 : :
154 [ # # ]: 0 : if (mlock(*ptr, *allocated) != 0) {
155 : : /* When mlock fails, no memory will be locked, so we don't use munlock on free */
156 [ # # ]: 0 : POSIX_GUARD(s2n_mem_free_no_mlock_impl(*ptr, *allocated));
157 [ # # ]: 0 : POSIX_BAIL(S2N_ERR_MLOCK);
158 : 0 : }
159 : :
160 [ # # ][ # # ]: 0 : POSIX_ENSURE(*ptr != NULL, S2N_ERR_ALLOC);
161 : :
162 : 0 : return S2N_SUCCESS;
163 : 0 : }
164 : :
165 : : static int s2n_mem_malloc_no_mlock_impl(void **ptr, uint32_t requested, uint32_t *allocated)
166 : 7718527 : {
167 : 7718527 : *ptr = malloc(requested);
168 [ # # ][ - + ]: 7718527 : POSIX_ENSURE(*ptr != NULL, S2N_ERR_ALLOC);
169 : 7718527 : *allocated = requested;
170 : :
171 : 7718527 : return S2N_SUCCESS;
172 : 7718527 : }
173 : :
174 : : #endif
175 : :
176 : : int s2n_mem_set_callbacks(s2n_mem_init_callback mem_init_callback, s2n_mem_cleanup_callback mem_cleanup_callback,
177 : : s2n_mem_malloc_callback mem_malloc_callback, s2n_mem_free_callback mem_free_callback)
178 : 2 : {
179 [ + + ][ + - ]: 2 : POSIX_ENSURE(!initialized, S2N_ERR_INITIALIZED);
180 [ - + ]: 1 : POSIX_GUARD_RESULT(s2n_mem_override_callbacks(mem_init_callback, mem_cleanup_callback,
181 : 1 : mem_malloc_callback, mem_free_callback));
182 : 1 : return S2N_SUCCESS;
183 : 1 : }
184 : :
185 : : S2N_RESULT s2n_mem_override_callbacks(s2n_mem_init_callback mem_init_callback, s2n_mem_cleanup_callback mem_cleanup_callback,
186 : : s2n_mem_malloc_callback mem_malloc_callback, s2n_mem_free_callback mem_free_callback)
187 : 336 : {
188 [ # # ][ - + ]: 336 : RESULT_ENSURE_REF(mem_init_callback);
189 [ - + ][ # # ]: 336 : RESULT_ENSURE_REF(mem_cleanup_callback);
190 [ - + ][ # # ]: 336 : RESULT_ENSURE_REF(mem_malloc_callback);
191 [ - + ][ # # ]: 336 : RESULT_ENSURE_REF(mem_free_callback);
192 : :
193 : 336 : s2n_mem_init_cb = mem_init_callback;
194 : 336 : s2n_mem_cleanup_cb = mem_cleanup_callback;
195 : 336 : s2n_mem_malloc_cb = mem_malloc_callback;
196 : 336 : s2n_mem_free_cb = mem_free_callback;
197 : :
198 : 336 : return S2N_RESULT_OK;
199 : 336 : }
200 : :
201 : : S2N_RESULT s2n_mem_get_callbacks(s2n_mem_init_callback *mem_init_callback, s2n_mem_cleanup_callback *mem_cleanup_callback,
202 : : s2n_mem_malloc_callback *mem_malloc_callback, s2n_mem_free_callback *mem_free_callback)
203 : 332 : {
204 [ # # ][ - + ]: 332 : RESULT_ENSURE_REF(mem_init_callback);
205 [ - + ][ # # ]: 332 : RESULT_ENSURE_REF(mem_cleanup_callback);
206 [ - + ][ # # ]: 332 : RESULT_ENSURE_REF(mem_malloc_callback);
207 [ - + ][ # # ]: 332 : RESULT_ENSURE_REF(mem_free_callback);
208 : :
209 : 332 : *mem_init_callback = s2n_mem_init_cb;
210 : 332 : *mem_cleanup_callback = s2n_mem_cleanup_cb;
211 : 332 : *mem_malloc_callback = s2n_mem_malloc_cb;
212 : 332 : *mem_free_callback = s2n_mem_free_cb;
213 : :
214 : 332 : return S2N_RESULT_OK;
215 : 332 : }
216 : :
217 : : /**
218 : : * Allocate a new blob on the heap.
219 : : *
220 : : * The blob will be _growable_.
221 : : *
222 : : * This blob owns the underlying memory, which will be freed when `s2n_free` is
223 : : * called on `b`.
224 : : */
225 : : int s2n_alloc(struct s2n_blob *b, uint32_t size)
226 : 7850472 : {
227 [ # # ][ - + ]: 7850472 : POSIX_ENSURE(initialized, S2N_ERR_NOT_INITIALIZED);
228 [ - + ][ # # ]: 7850472 : POSIX_ENSURE_REF(b);
229 : 7850472 : const struct s2n_blob temp = { 0 };
230 : 7850472 : *b = temp;
231 [ + + ]: 7850472 : POSIX_GUARD(s2n_realloc(b, size));
232 : 7850471 : return S2N_SUCCESS;
233 : 7850472 : }
234 : :
235 : : /* A blob is growable if it is either explicitly marked as such, or if it contains no data */
236 : : bool s2n_blob_is_growable(const struct s2n_blob *b)
237 : 84571367 : {
238 [ + + ][ + + ]: 84571367 : return b && (b->growable || (b->data == NULL && b->size == 0 && b->allocated == 0));
[ + + ][ + - ]
[ + - ]
239 : 84571367 : }
240 : :
241 : : /**
242 : : * Resize a blob to `size`. The blob must be allocated or empty.
243 : : *
244 : : * This will allocate more memory if necessary, or reuse the existing allocation
245 : : * if the requested size is smaller than the current size.
246 : : *
247 : : * If failed, *b remains unchanged.
248 : : */
249 : : int s2n_realloc(struct s2n_blob *b, uint32_t size)
250 : 18737530 : {
251 [ - + ][ # # ]: 18737530 : POSIX_ENSURE(initialized, S2N_ERR_NOT_INITIALIZED);
252 [ + - ][ + + ]: 18737530 : POSIX_ENSURE_REF(b);
253 [ + + ][ + - ]: 18737529 : POSIX_ENSURE(s2n_blob_is_growable(b), S2N_ERR_RESIZE_STATIC_BLOB);
254 [ + + ]: 18737528 : if (size == 0) {
255 : 11015151 : return s2n_free(b);
256 : 11015151 : }
257 : :
258 : : /* blob already has space for the request */
259 [ + + ]: 7722377 : if (size <= b->allocated) {
260 [ + + ]: 2243 : if (size < b->size) {
261 : : /* Zero the existing blob memory before the we release it */
262 : 63 : struct s2n_blob slice = { 0 };
263 [ - + ]: 63 : POSIX_GUARD(s2n_blob_slice(b, &slice, size, b->size - size));
264 [ - + ]: 63 : POSIX_GUARD(s2n_blob_zero(&slice));
265 : 63 : }
266 : :
267 : 2243 : b->size = size;
268 : 2243 : return S2N_SUCCESS;
269 : 2243 : }
270 : :
271 : 7720134 : struct s2n_blob new_memory = { .data = NULL, .size = size, .allocated = 0, .growable = 1 };
272 [ + + ]: 7720134 : if (s2n_mem_malloc_cb((void **) &new_memory.data, new_memory.size, &new_memory.allocated) != 0) {
273 : 1 : S2N_ERROR_PRESERVE_ERRNO();
274 : 1 : }
275 : :
276 [ - + ][ # # ]: 7720133 : POSIX_ENSURE(new_memory.allocated >= new_memory.size, S2N_ERR_ALLOC);
277 [ - + ][ # # ]: 7720133 : POSIX_ENSURE(new_memory.data != NULL, S2N_ERR_ALLOC);
278 : :
279 [ + + ]: 7720133 : if (b->size) {
280 [ # # ][ - + ]: 10458 : POSIX_CHECKED_MEMCPY(new_memory.data, b->data, b->size);
[ + - ]
281 : 10458 : }
282 : :
283 [ + + ]: 7720133 : if (b->allocated) {
284 [ - + ]: 10459 : POSIX_GUARD(s2n_free(b));
285 : 10459 : }
286 : :
287 : 7720133 : *b = new_memory;
288 : 7720133 : return S2N_SUCCESS;
289 : 7720133 : }
290 : :
291 : : int s2n_free_object(uint8_t **p_data, uint32_t size)
292 : 676244 : {
293 [ - + ][ # # ]: 676244 : POSIX_ENSURE_REF(p_data);
294 : :
295 [ + + ]: 676244 : if (*p_data == NULL) {
296 : 7424 : return S2N_SUCCESS;
297 : 7424 : }
298 : :
299 [ # # ][ - + ]: 668820 : POSIX_ENSURE(initialized, S2N_ERR_NOT_INITIALIZED);
300 : 668820 : struct s2n_blob b = { .data = *p_data, .allocated = size, .size = size, .growable = 1 };
301 : :
302 : : /* s2n_free() will call free() even if it returns error (for a growable blob).
303 : : ** This makes sure *p_data is not used after free() */
304 : 668820 : *p_data = NULL;
305 : :
306 : 668820 : return s2n_free(&b);
307 : 668820 : }
308 : :
309 : : /**
310 : : * Allocate enough memory for `to` to contain all the data in `from`, then copy
311 : : * the data in `from` to `to`.
312 : : */
313 : : int s2n_dup(struct s2n_blob *from, struct s2n_blob *to)
314 : 41346 : {
315 [ - + ][ # # ]: 41346 : POSIX_ENSURE(initialized, S2N_ERR_NOT_INITIALIZED);
316 [ # # ][ - + ]: 41346 : POSIX_ENSURE_REF(to);
317 [ - + ][ # # ]: 41346 : POSIX_ENSURE_REF(from);
318 [ # # ][ - + ]: 41346 : POSIX_ENSURE_EQ(to->size, 0);
319 [ - + ][ # # ]: 41346 : POSIX_ENSURE_EQ(to->data, NULL);
320 [ + - ][ + + ]: 41346 : POSIX_ENSURE_NE(from->size, 0);
321 [ - + ][ # # ]: 41344 : POSIX_ENSURE_NE(from->data, NULL);
322 : :
323 [ + + ]: 41344 : POSIX_GUARD(s2n_alloc(to, from->size));
324 : :
325 [ - + ][ # # ]: 41343 : POSIX_CHECKED_MEMCPY(to->data, from->data, to->size);
[ + - ]
326 : :
327 : 41343 : return S2N_SUCCESS;
328 : 41343 : }
329 : :
330 : : int s2n_mem_init(void)
331 : 345 : {
332 [ # # ][ - + ]: 345 : POSIX_ENSURE(s2n_mem_init_cb() >= S2N_SUCCESS, S2N_ERR_CANCELLED);
333 : :
334 : 345 : initialized = true;
335 : :
336 : 345 : return S2N_SUCCESS;
337 : 345 : }
338 : :
339 : : bool s2n_mem_is_init(void)
340 : 0 : {
341 : 0 : return initialized;
342 : 0 : }
343 : :
344 : : uint32_t s2n_mem_get_page_size(void)
345 : 0 : {
346 : 0 : return page_size;
347 : 0 : }
348 : :
349 : : int s2n_mem_cleanup(void)
350 : 372 : {
351 [ + + ][ + - ]: 372 : POSIX_ENSURE(initialized, S2N_ERR_NOT_INITIALIZED);
352 [ - + ][ # # ]: 345 : POSIX_ENSURE(s2n_mem_cleanup_cb() >= S2N_SUCCESS, S2N_ERR_CANCELLED);
353 : :
354 : 345 : initialized = false;
355 : :
356 : 345 : return S2N_SUCCESS;
357 : 345 : }
358 : :
359 : : int s2n_free(struct s2n_blob *b)
360 : 65822522 : {
361 : : /* To avoid memory leaks, don't exit the function until the memory
362 : : has been freed */
363 : 65822522 : int zero_rc = s2n_blob_zero(b);
364 [ + + ]: 65822522 : POSIX_GUARD(s2n_free_without_wipe(b));
365 : 65822438 : return zero_rc;
366 : 65822522 : }
367 : :
368 : : int s2n_free_without_wipe(struct s2n_blob *b)
369 : 65833916 : {
370 [ + + ][ + + ]: 65833916 : POSIX_PRECONDITION(s2n_blob_validate(b));
371 : :
372 [ + + ][ + - ]: 65833913 : POSIX_ENSURE(initialized, S2N_ERR_NOT_INITIALIZED);
373 [ + + ][ + - ]: 65833832 : POSIX_ENSURE(s2n_blob_is_growable(b), S2N_ERR_FREE_STATIC_BLOB);
374 : :
375 [ + + ]: 65833831 : if (b->data) {
376 : 7720133 : void *data = b->data;
377 : 7720133 : uint32_t allocated = b->allocated;
378 : : /* Set data point to NULL first to prevent potential double-free on s2n_mem_free_cb error path */
379 : 7720133 : *b = (struct s2n_blob){ 0 };
380 [ + + ][ + - ]: 7720133 : POSIX_ENSURE(s2n_mem_free_cb(data, allocated) >= S2N_SUCCESS, S2N_ERR_CANCELLED);
381 : 7720133 : }
382 : :
383 : 65833830 : *b = (struct s2n_blob){ 0 };
384 : :
385 : 65833830 : return S2N_SUCCESS;
386 : 65833831 : }
387 : :
388 : : int s2n_free_or_wipe(struct s2n_blob *b)
389 : 32058299 : {
390 [ + + ][ + - ]: 32058299 : POSIX_ENSURE_REF(b);
391 : 32058298 : int zero_rc = s2n_blob_zero(b);
392 [ + + ]: 32058298 : if (b->allocated) {
393 [ - + ]: 11378 : POSIX_GUARD(s2n_free_without_wipe(b));
394 : 11378 : }
395 : 32058298 : return zero_rc;
396 : 32058298 : }
|