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 : : #include <sys/mman.h>
25 : : #include <sys/param.h>
26 : : #include <unistd.h>
27 : :
28 : : #include "error/s2n_errno.h"
29 : : #include "utils/s2n_blob.h"
30 : : #include "utils/s2n_mem.h"
31 : : #include "utils/s2n_safety.h"
32 : :
33 : : static uint32_t page_size = 4096;
34 : : static bool initialized = false;
35 : :
36 : : static int s2n_mem_init_impl(void);
37 : : static int s2n_mem_cleanup_impl(void);
38 : : static int s2n_mem_free_no_mlock_impl(void *ptr, uint32_t size);
39 : : static int s2n_mem_free_mlock_impl(void *ptr, uint32_t size);
40 : : static int s2n_mem_malloc_no_mlock_impl(void **ptr, uint32_t requested, uint32_t *allocated);
41 : : static int s2n_mem_malloc_mlock_impl(void **ptr, uint32_t requested, uint32_t *allocated);
42 : :
43 : : static s2n_mem_init_callback s2n_mem_init_cb = s2n_mem_init_impl;
44 : : static s2n_mem_cleanup_callback s2n_mem_cleanup_cb = s2n_mem_cleanup_impl;
45 : : static s2n_mem_malloc_callback s2n_mem_malloc_cb = s2n_mem_malloc_mlock_impl;
46 : : static s2n_mem_free_callback s2n_mem_free_cb = s2n_mem_free_mlock_impl;
47 : :
48 : : static int s2n_mem_init_impl(void)
49 : 546 : {
50 : 546 : long sysconf_rc = sysconf(_SC_PAGESIZE);
51 : :
52 : : /* sysconf must not error, and page_size cannot be 0 */
53 [ - + ][ # # ]: 546 : POSIX_ENSURE_GT(sysconf_rc, 0);
54 : :
55 : : /* page_size must be a valid uint32 */
56 : 546 : long max_page_size = MIN(UINT32_MAX, LONG_MAX);
57 [ - + ][ # # ]: 546 : POSIX_ENSURE_LTE(sysconf_rc, max_page_size);
58 : 546 : page_size = (uint32_t) sysconf_rc;
59 : :
60 [ + - ][ # # ]: 546 : if (getenv("S2N_DONT_MLOCK") || s2n_in_unit_test()) {
61 : 546 : s2n_mem_malloc_cb = s2n_mem_malloc_no_mlock_impl;
62 : 546 : s2n_mem_free_cb = s2n_mem_free_no_mlock_impl;
63 : 546 : }
64 : 546 : return S2N_SUCCESS;
65 : 546 : }
66 : :
67 : : static int s2n_mem_cleanup_impl(void)
68 : 546 : {
69 : 546 : page_size = 4096;
70 : 546 : s2n_mem_malloc_cb = s2n_mem_malloc_no_mlock_impl;
71 : 546 : s2n_mem_free_cb = s2n_mem_free_no_mlock_impl;
72 : 546 : return S2N_SUCCESS;
73 : 546 : }
74 : :
75 : : static int s2n_mem_free_mlock_impl(void *ptr, uint32_t size)
76 : 0 : {
77 : : /* Perform a best-effort `munlock`: ignore any errors during unlocking. */
78 : 0 : munlock(ptr, size);
79 : 0 : free(ptr);
80 : 0 : return S2N_SUCCESS;
81 : 0 : }
82 : :
83 : : static int s2n_mem_free_no_mlock_impl(void *ptr, uint32_t size)
84 : 7719171 : {
85 : 7719171 : free(ptr);
86 : :
87 : 7719171 : return S2N_SUCCESS;
88 : 7719171 : }
89 : :
90 : : static int s2n_mem_malloc_mlock_impl(void **ptr, uint32_t requested, uint32_t *allocated)
91 : 0 : {
92 [ # # ][ # # ]: 0 : POSIX_ENSURE_REF(ptr);
93 : :
94 : : /* Page aligned allocation required for mlock */
95 : 0 : uint32_t allocate = 0;
96 : :
97 [ # # ]: 0 : POSIX_GUARD(s2n_align_to(requested, page_size, &allocate));
98 : :
99 : 0 : *ptr = NULL;
100 [ # # ][ # # ]: 0 : POSIX_ENSURE(posix_memalign(ptr, page_size, allocate) == 0, S2N_ERR_ALLOC);
101 : 0 : *allocated = allocate;
102 : :
103 : : /*
104 : : ** We disable MAD_DONTDUMP when fuzz-testing or using the address sanitizer because
105 : : ** both need to be able to dump pages to function. It's how they map heap output.
106 : : */
107 : 0 : #if defined(MADV_DONTDUMP) && !defined(S2N_ADDRESS_SANITIZER) && !defined(S2N_FUZZ_TESTING)
108 [ # # ]: 0 : if (madvise(*ptr, *allocated, MADV_DONTDUMP) != 0) {
109 [ # # ]: 0 : POSIX_GUARD(s2n_mem_free_no_mlock_impl(*ptr, *allocated));
110 [ # # ]: 0 : POSIX_BAIL(S2N_ERR_MADVISE);
111 : 0 : }
112 : 0 : #endif
113 : :
114 [ # # ]: 0 : if (mlock(*ptr, *allocated) != 0) {
115 : : /* When mlock fails, no memory will be locked, so we don't use munlock on free */
116 [ # # ]: 0 : POSIX_GUARD(s2n_mem_free_no_mlock_impl(*ptr, *allocated));
117 [ # # ]: 0 : POSIX_BAIL(S2N_ERR_MLOCK);
118 : 0 : }
119 : :
120 [ # # ][ # # ]: 0 : POSIX_ENSURE(*ptr != NULL, S2N_ERR_ALLOC);
121 : :
122 : 0 : return S2N_SUCCESS;
123 : 0 : }
124 : :
125 : : static int s2n_mem_malloc_no_mlock_impl(void **ptr, uint32_t requested, uint32_t *allocated)
126 : 7719171 : {
127 : 7719171 : *ptr = malloc(requested);
128 [ # # ][ - + ]: 7719171 : POSIX_ENSURE(*ptr != NULL, S2N_ERR_ALLOC);
129 : 7719171 : *allocated = requested;
130 : :
131 : 7719171 : return S2N_SUCCESS;
132 : 7719171 : }
133 : :
134 : : int s2n_mem_set_callbacks(s2n_mem_init_callback mem_init_callback, s2n_mem_cleanup_callback mem_cleanup_callback,
135 : : s2n_mem_malloc_callback mem_malloc_callback, s2n_mem_free_callback mem_free_callback)
136 : 6 : {
137 [ + + ][ + - ]: 6 : POSIX_ENSURE(!initialized, S2N_ERR_INITIALIZED);
138 [ - + ]: 3 : POSIX_GUARD_RESULT(s2n_mem_override_callbacks(mem_init_callback, mem_cleanup_callback,
139 : 3 : mem_malloc_callback, mem_free_callback));
140 : 3 : return S2N_SUCCESS;
141 : 3 : }
142 : :
143 : : S2N_RESULT s2n_mem_override_callbacks(s2n_mem_init_callback mem_init_callback, s2n_mem_cleanup_callback mem_cleanup_callback,
144 : : s2n_mem_malloc_callback mem_malloc_callback, s2n_mem_free_callback mem_free_callback)
145 : 334 : {
146 [ - + ][ # # ]: 334 : RESULT_ENSURE_REF(mem_init_callback);
147 [ - + ][ # # ]: 334 : RESULT_ENSURE_REF(mem_cleanup_callback);
148 [ - + ][ # # ]: 334 : RESULT_ENSURE_REF(mem_malloc_callback);
149 [ - + ][ # # ]: 334 : RESULT_ENSURE_REF(mem_free_callback);
150 : :
151 : 334 : s2n_mem_init_cb = mem_init_callback;
152 : 334 : s2n_mem_cleanup_cb = mem_cleanup_callback;
153 : 334 : s2n_mem_malloc_cb = mem_malloc_callback;
154 : 334 : s2n_mem_free_cb = mem_free_callback;
155 : :
156 : 334 : return S2N_RESULT_OK;
157 : 334 : }
158 : :
159 : : S2N_RESULT s2n_mem_get_callbacks(s2n_mem_init_callback *mem_init_callback, s2n_mem_cleanup_callback *mem_cleanup_callback,
160 : : s2n_mem_malloc_callback *mem_malloc_callback, s2n_mem_free_callback *mem_free_callback)
161 : 330 : {
162 [ - + ][ # # ]: 330 : RESULT_ENSURE_REF(mem_init_callback);
163 [ # # ][ - + ]: 330 : RESULT_ENSURE_REF(mem_cleanup_callback);
164 [ - + ][ # # ]: 330 : RESULT_ENSURE_REF(mem_malloc_callback);
165 [ - + ][ # # ]: 330 : RESULT_ENSURE_REF(mem_free_callback);
166 : :
167 : 330 : *mem_init_callback = s2n_mem_init_cb;
168 : 330 : *mem_cleanup_callback = s2n_mem_cleanup_cb;
169 : 330 : *mem_malloc_callback = s2n_mem_malloc_cb;
170 : 330 : *mem_free_callback = s2n_mem_free_cb;
171 : :
172 : 330 : return S2N_RESULT_OK;
173 : 330 : }
174 : :
175 : : /**
176 : : * Allocate a new blob on the heap.
177 : : *
178 : : * The blob will be _growable_.
179 : : *
180 : : * This blob owns the underlying memory, which will be freed when `s2n_free` is
181 : : * called on `b`.
182 : : */
183 : : int s2n_alloc(struct s2n_blob *b, uint32_t size)
184 : 7849890 : {
185 [ # # ][ - + ]: 7849890 : POSIX_ENSURE(initialized, S2N_ERR_NOT_INITIALIZED);
186 [ - + ][ # # ]: 7849890 : POSIX_ENSURE_REF(b);
187 : 7849890 : const struct s2n_blob temp = { 0 };
188 : 7849890 : *b = temp;
189 [ - + ]: 7849890 : POSIX_GUARD(s2n_realloc(b, size));
190 : 7849890 : return S2N_SUCCESS;
191 : 7849890 : }
192 : :
193 : : /* A blob is growable if it is either explicitly marked as such, or if it contains no data */
194 : : bool s2n_blob_is_growable(const struct s2n_blob *b)
195 : 84569461 : {
196 [ + + ][ + + ]: 84569461 : return b && (b->growable || (b->data == NULL && b->size == 0 && b->allocated == 0));
[ + + ][ + - ]
[ + - ]
197 : 84569461 : }
198 : :
199 : : /**
200 : : * Resize a blob to `size`. The blob must be allocated or empty.
201 : : *
202 : : * This will allocate more memory if necessary, or reuse the existing allocation
203 : : * if the requested size is smaller than the current size.
204 : : *
205 : : * If failed, *b remains unchanged.
206 : : */
207 : : int s2n_realloc(struct s2n_blob *b, uint32_t size)
208 : 18738065 : {
209 [ # # ][ - + ]: 18738065 : POSIX_ENSURE(initialized, S2N_ERR_NOT_INITIALIZED);
210 [ + + ][ + - ]: 18738065 : POSIX_ENSURE_REF(b);
211 [ + + ][ + - ]: 18738064 : POSIX_ENSURE(s2n_blob_is_growable(b), S2N_ERR_RESIZE_STATIC_BLOB);
212 [ + + ]: 18738063 : if (size == 0) {
213 : 11014440 : return s2n_free(b);
214 : 11014440 : }
215 : :
216 : : /* blob already has space for the request */
217 [ + + ]: 7723623 : if (size <= b->allocated) {
218 [ + + ]: 2227 : if (size < b->size) {
219 : : /* Zero the existing blob memory before the we release it */
220 : 63 : struct s2n_blob slice = { 0 };
221 [ - + ]: 63 : POSIX_GUARD(s2n_blob_slice(b, &slice, size, b->size - size));
222 [ - + ]: 63 : POSIX_GUARD(s2n_blob_zero(&slice));
223 : 63 : }
224 : :
225 : 2227 : b->size = size;
226 : 2227 : return S2N_SUCCESS;
227 : 2227 : }
228 : :
229 : 7721396 : struct s2n_blob new_memory = { .data = NULL, .size = size, .allocated = 0, .growable = 1 };
230 [ - + ]: 7721396 : if (s2n_mem_malloc_cb((void **) &new_memory.data, new_memory.size, &new_memory.allocated) != 0) {
231 : 0 : S2N_ERROR_PRESERVE_ERRNO();
232 : 0 : }
233 : :
234 [ - + ][ # # ]: 7721396 : POSIX_ENSURE(new_memory.allocated >= new_memory.size, S2N_ERR_ALLOC);
235 [ - + ][ # # ]: 7721396 : POSIX_ENSURE(new_memory.data != NULL, S2N_ERR_ALLOC);
236 : :
237 [ + + ]: 7721396 : if (b->size) {
238 [ - + ][ # # ]: 10056 : POSIX_CHECKED_MEMCPY(new_memory.data, b->data, b->size);
[ + - ]
239 : 10056 : }
240 : :
241 [ + + ]: 7721396 : if (b->allocated) {
242 [ - + ]: 10057 : POSIX_GUARD(s2n_free(b));
243 : 10057 : }
244 : :
245 : 7721396 : *b = new_memory;
246 : 7721396 : return S2N_SUCCESS;
247 : 7721396 : }
248 : :
249 : : int s2n_free_object(uint8_t **p_data, uint32_t size)
250 : 678620 : {
251 [ - + ][ # # ]: 678620 : POSIX_ENSURE_REF(p_data);
252 : :
253 [ + + ]: 678620 : if (*p_data == NULL) {
254 : 8496 : return S2N_SUCCESS;
255 : 8496 : }
256 : :
257 [ - + ][ # # ]: 670124 : POSIX_ENSURE(initialized, S2N_ERR_NOT_INITIALIZED);
258 : 670124 : struct s2n_blob b = { .data = *p_data, .allocated = size, .size = size, .growable = 1 };
259 : :
260 : : /* s2n_free() will call free() even if it returns error (for a growable blob).
261 : : ** This makes sure *p_data is not used after free() */
262 : 670124 : *p_data = NULL;
263 : :
264 : 670124 : return s2n_free(&b);
265 : 670124 : }
266 : :
267 : : /**
268 : : * Allocate enough memory for `to` to contain all the data in `from`, then copy
269 : : * the data in `from` to `to`.
270 : : */
271 : : int s2n_dup(struct s2n_blob *from, struct s2n_blob *to)
272 : 43495 : {
273 [ - + ][ # # ]: 43495 : POSIX_ENSURE(initialized, S2N_ERR_NOT_INITIALIZED);
274 [ # # ][ - + ]: 43495 : POSIX_ENSURE_REF(to);
275 [ - + ][ # # ]: 43495 : POSIX_ENSURE_REF(from);
276 [ - + ][ # # ]: 43495 : POSIX_ENSURE_EQ(to->size, 0);
277 [ - + ][ # # ]: 43495 : POSIX_ENSURE_EQ(to->data, NULL);
278 [ + - ][ + + ]: 43495 : POSIX_ENSURE_NE(from->size, 0);
279 [ # # ][ - + ]: 43493 : POSIX_ENSURE_NE(from->data, NULL);
280 : :
281 [ - + ]: 43493 : POSIX_GUARD(s2n_alloc(to, from->size));
282 : :
283 [ # # ][ - + ]: 43493 : POSIX_CHECKED_MEMCPY(to->data, from->data, to->size);
[ + - ]
284 : :
285 : 43493 : return S2N_SUCCESS;
286 : 43493 : }
287 : :
288 : : int s2n_mem_init(void)
289 : 549 : {
290 [ # # ][ - + ]: 549 : POSIX_ENSURE(s2n_mem_init_cb() >= S2N_SUCCESS, S2N_ERR_CANCELLED);
291 : :
292 : 549 : initialized = true;
293 : :
294 : 549 : return S2N_SUCCESS;
295 : 549 : }
296 : :
297 : : bool s2n_mem_is_init(void)
298 : 0 : {
299 : 0 : return initialized;
300 : 0 : }
301 : :
302 : : uint32_t s2n_mem_get_page_size(void)
303 : 0 : {
304 : 0 : return page_size;
305 : 0 : }
306 : :
307 : : int s2n_mem_cleanup(void)
308 : 549 : {
309 [ - + ][ # # ]: 549 : POSIX_ENSURE(initialized, S2N_ERR_NOT_INITIALIZED);
310 [ - + ][ # # ]: 549 : POSIX_ENSURE(s2n_mem_cleanup_cb() >= S2N_SUCCESS, S2N_ERR_CANCELLED);
311 : :
312 : 549 : initialized = false;
313 : :
314 : 549 : return S2N_SUCCESS;
315 : 549 : }
316 : :
317 : : int s2n_free(struct s2n_blob *b)
318 : 65820059 : {
319 : : /* To avoid memory leaks, don't exit the function until the memory
320 : : has been freed */
321 : 65820059 : int zero_rc = s2n_blob_zero(b);
322 [ + + ]: 65820059 : POSIX_GUARD(s2n_free_without_wipe(b));
323 : 65819963 : return zero_rc;
324 : 65820059 : }
325 : :
326 : : int s2n_free_without_wipe(struct s2n_blob *b)
327 : 65831487 : {
328 [ + + ][ + + ]: 65831487 : POSIX_PRECONDITION(s2n_blob_validate(b));
329 : :
330 [ + + ][ + - ]: 65831484 : POSIX_ENSURE(initialized, S2N_ERR_NOT_INITIALIZED);
331 [ + + ][ + - ]: 65831391 : POSIX_ENSURE(s2n_blob_is_growable(b), S2N_ERR_FREE_STATIC_BLOB);
332 : :
333 [ + + ]: 65831390 : if (b->data) {
334 [ # # ][ - + ]: 7721396 : POSIX_ENSURE(s2n_mem_free_cb(b->data, b->allocated) >= S2N_SUCCESS, S2N_ERR_CANCELLED);
335 : 7721396 : }
336 : :
337 : 65831390 : *b = (struct s2n_blob){ 0 };
338 : :
339 : 65831390 : return S2N_SUCCESS;
340 : 65831390 : }
341 : :
342 : : int s2n_free_or_wipe(struct s2n_blob *b)
343 : 32056206 : {
344 [ + - ][ + + ]: 32056206 : POSIX_ENSURE_REF(b);
345 : 32056205 : int zero_rc = s2n_blob_zero(b);
346 [ + + ]: 32056205 : if (b->allocated) {
347 [ - + ]: 11414 : POSIX_GUARD(s2n_free_without_wipe(b));
348 : 11414 : }
349 : 32056205 : return zero_rc;
350 : 32056205 : }
|