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 : 542 : {
50 : 542 : long sysconf_rc = sysconf(_SC_PAGESIZE);
51 : :
52 : : /* sysconf must not error, and page_size cannot be 0 */
53 [ - + ][ # # ]: 542 : POSIX_ENSURE_GT(sysconf_rc, 0);
54 : :
55 : : /* page_size must be a valid uint32 */
56 : 542 : long max_page_size = MIN(UINT32_MAX, LONG_MAX);
57 [ - + ][ # # ]: 542 : POSIX_ENSURE_LTE(sysconf_rc, max_page_size);
58 : 542 : page_size = (uint32_t) sysconf_rc;
59 : :
60 [ + - ][ # # ]: 542 : if (getenv("S2N_DONT_MLOCK") || s2n_in_unit_test()) {
61 : 542 : s2n_mem_malloc_cb = s2n_mem_malloc_no_mlock_impl;
62 : 542 : s2n_mem_free_cb = s2n_mem_free_no_mlock_impl;
63 : 542 : }
64 : 542 : return S2N_SUCCESS;
65 : 542 : }
66 : :
67 : : static int s2n_mem_cleanup_impl(void)
68 : 542 : {
69 : 542 : page_size = 4096;
70 : 542 : s2n_mem_malloc_cb = s2n_mem_malloc_no_mlock_impl;
71 : 542 : s2n_mem_free_cb = s2n_mem_free_no_mlock_impl;
72 : 542 : return S2N_SUCCESS;
73 : 542 : }
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 : 7702431 : {
85 : 7702431 : free(ptr);
86 : :
87 : 7702431 : return S2N_SUCCESS;
88 : 7702431 : }
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 : 7702431 : {
127 : 7702431 : *ptr = malloc(requested);
128 [ # # ][ - + ]: 7702431 : POSIX_ENSURE(*ptr != NULL, S2N_ERR_ALLOC);
129 : 7702431 : *allocated = requested;
130 : :
131 : 7702431 : return S2N_SUCCESS;
132 : 7702431 : }
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 : : int s2n_alloc(struct s2n_blob *b, uint32_t size)
176 : 7833058 : {
177 [ # # ][ - + ]: 7833058 : POSIX_ENSURE(initialized, S2N_ERR_NOT_INITIALIZED);
178 [ - + ][ # # ]: 7833058 : POSIX_ENSURE_REF(b);
179 : 7833058 : const struct s2n_blob temp = { 0 };
180 : 7833058 : *b = temp;
181 [ - + ]: 7833058 : POSIX_GUARD(s2n_realloc(b, size));
182 : 7833058 : return S2N_SUCCESS;
183 : 7833058 : }
184 : :
185 : : /* A blob is growable if it is either explicitly marked as such, or if it contains no data */
186 : : bool s2n_blob_is_growable(const struct s2n_blob *b)
187 : 84470996 : {
188 [ + + ][ + + ]: 84470996 : return b && (b->growable || (b->data == NULL && b->size == 0 && b->allocated == 0));
[ + + ][ + - ]
[ + - ]
189 : 84470996 : }
190 : :
191 : : /* Tries to realloc the requested bytes.
192 : : * If successful, updates *b.
193 : : * If failed, *b remains unchanged
194 : : */
195 : : int s2n_realloc(struct s2n_blob *b, uint32_t size)
196 : 18711834 : {
197 [ # # ][ - + ]: 18711834 : POSIX_ENSURE(initialized, S2N_ERR_NOT_INITIALIZED);
198 [ + + ][ + - ]: 18711834 : POSIX_ENSURE_REF(b);
199 [ + + ][ + - ]: 18711833 : POSIX_ENSURE(s2n_blob_is_growable(b), S2N_ERR_RESIZE_STATIC_BLOB);
200 [ + + ]: 18711832 : if (size == 0) {
201 : 11004949 : return s2n_free(b);
202 : 11004949 : }
203 : :
204 : : /* blob already has space for the request */
205 [ + + ]: 7706883 : if (size <= b->allocated) {
206 [ + + ]: 2227 : if (size < b->size) {
207 : : /* Zero the existing blob memory before the we release it */
208 : 63 : struct s2n_blob slice = { 0 };
209 [ - + ]: 63 : POSIX_GUARD(s2n_blob_slice(b, &slice, size, b->size - size));
210 [ - + ]: 63 : POSIX_GUARD(s2n_blob_zero(&slice));
211 : 63 : }
212 : :
213 : 2227 : b->size = size;
214 : 2227 : return S2N_SUCCESS;
215 : 2227 : }
216 : :
217 : 7704656 : struct s2n_blob new_memory = { .data = NULL, .size = size, .allocated = 0, .growable = 1 };
218 [ - + ]: 7704656 : if (s2n_mem_malloc_cb((void **) &new_memory.data, new_memory.size, &new_memory.allocated) != 0) {
219 : 0 : S2N_ERROR_PRESERVE_ERRNO();
220 : 0 : }
221 : :
222 [ - + ][ # # ]: 7704656 : POSIX_ENSURE(new_memory.allocated >= new_memory.size, S2N_ERR_ALLOC);
223 [ - + ][ # # ]: 7704656 : POSIX_ENSURE(new_memory.data != NULL, S2N_ERR_ALLOC);
224 : :
225 [ + + ]: 7704656 : if (b->size) {
226 [ - + ][ # # ]: 9919 : POSIX_CHECKED_MEMCPY(new_memory.data, b->data, b->size);
[ + - ]
227 : 9919 : }
228 : :
229 [ + + ]: 7704656 : if (b->allocated) {
230 [ - + ]: 9920 : POSIX_GUARD(s2n_free(b));
231 : 9920 : }
232 : :
233 : 7704656 : *b = new_memory;
234 : 7704656 : return S2N_SUCCESS;
235 : 7704656 : }
236 : :
237 : : int s2n_free_object(uint8_t **p_data, uint32_t size)
238 : 667171 : {
239 [ - + ][ # # ]: 667171 : POSIX_ENSURE_REF(p_data);
240 : :
241 [ + + ]: 667171 : if (*p_data == NULL) {
242 : 7320 : return S2N_SUCCESS;
243 : 7320 : }
244 : :
245 [ - + ][ # # ]: 659851 : POSIX_ENSURE(initialized, S2N_ERR_NOT_INITIALIZED);
246 : 659851 : struct s2n_blob b = { .data = *p_data, .allocated = size, .size = size, .growable = 1 };
247 : :
248 : : /* s2n_free() will call free() even if it returns error (for a growable blob).
249 : : ** This makes sure *p_data is not used after free() */
250 : 659851 : *p_data = NULL;
251 : :
252 : 659851 : return s2n_free(&b);
253 : 659851 : }
254 : :
255 : : int s2n_dup(struct s2n_blob *from, struct s2n_blob *to)
256 : 42838 : {
257 [ - + ][ # # ]: 42838 : POSIX_ENSURE(initialized, S2N_ERR_NOT_INITIALIZED);
258 [ # # ][ - + ]: 42838 : POSIX_ENSURE_REF(to);
259 [ - + ][ # # ]: 42838 : POSIX_ENSURE_REF(from);
260 [ - + ][ # # ]: 42838 : POSIX_ENSURE_EQ(to->size, 0);
261 [ - + ][ # # ]: 42838 : POSIX_ENSURE_EQ(to->data, NULL);
262 [ + - ][ + + ]: 42838 : POSIX_ENSURE_NE(from->size, 0);
263 [ # # ][ - + ]: 42836 : POSIX_ENSURE_NE(from->data, NULL);
264 : :
265 [ - + ]: 42836 : POSIX_GUARD(s2n_alloc(to, from->size));
266 : :
267 [ # # ][ - + ]: 42836 : POSIX_CHECKED_MEMCPY(to->data, from->data, to->size);
[ + - ]
268 : :
269 : 42836 : return S2N_SUCCESS;
270 : 42836 : }
271 : :
272 : : int s2n_mem_init(void)
273 : 545 : {
274 [ # # ][ - + ]: 545 : POSIX_ENSURE(s2n_mem_init_cb() >= S2N_SUCCESS, S2N_ERR_CANCELLED);
275 : :
276 : 545 : initialized = true;
277 : :
278 : 545 : return S2N_SUCCESS;
279 : 545 : }
280 : :
281 : : bool s2n_mem_is_init(void)
282 : 0 : {
283 : 0 : return initialized;
284 : 0 : }
285 : :
286 : : uint32_t s2n_mem_get_page_size(void)
287 : 0 : {
288 : 0 : return page_size;
289 : 0 : }
290 : :
291 : : int s2n_mem_cleanup(void)
292 : 545 : {
293 [ - + ][ # # ]: 545 : POSIX_ENSURE(initialized, S2N_ERR_NOT_INITIALIZED);
294 [ - + ][ # # ]: 545 : POSIX_ENSURE(s2n_mem_cleanup_cb() >= S2N_SUCCESS, S2N_ERR_CANCELLED);
295 : :
296 : 545 : initialized = false;
297 : :
298 : 545 : return S2N_SUCCESS;
299 : 545 : }
300 : :
301 : : int s2n_free(struct s2n_blob *b)
302 : 65748415 : {
303 : : /* To avoid memory leaks, don't exit the function until the memory
304 : : has been freed */
305 : 65748415 : int zero_rc = s2n_blob_zero(b);
306 [ + + ]: 65748415 : POSIX_GUARD(s2n_free_without_wipe(b));
307 : 65748319 : return zero_rc;
308 : 65748415 : }
309 : :
310 : : int s2n_free_without_wipe(struct s2n_blob *b)
311 : 65759253 : {
312 [ + + ][ + + ]: 65759253 : POSIX_PRECONDITION(s2n_blob_validate(b));
313 : :
314 [ + + ][ + - ]: 65759250 : POSIX_ENSURE(initialized, S2N_ERR_NOT_INITIALIZED);
315 [ + + ][ + - ]: 65759157 : POSIX_ENSURE(s2n_blob_is_growable(b), S2N_ERR_FREE_STATIC_BLOB);
316 : :
317 [ + + ]: 65759156 : if (b->data) {
318 [ # # ][ - + ]: 7704656 : POSIX_ENSURE(s2n_mem_free_cb(b->data, b->allocated) >= S2N_SUCCESS, S2N_ERR_CANCELLED);
319 : 7704656 : }
320 : :
321 : 65759156 : *b = (struct s2n_blob){ 0 };
322 : :
323 : 65759156 : return S2N_SUCCESS;
324 : 65759156 : }
325 : :
326 : : int s2n_free_or_wipe(struct s2n_blob *b)
327 : 32028462 : {
328 [ + - ][ + + ]: 32028462 : POSIX_ENSURE_REF(b);
329 : 32028461 : int zero_rc = s2n_blob_zero(b);
330 [ + + ]: 32028461 : if (b->allocated) {
331 [ - + ]: 10824 : POSIX_GUARD(s2n_free_without_wipe(b));
332 : 10824 : }
333 : 32028461 : return zero_rc;
334 : 32028461 : }
|