2 * Copyright 2015-2016 The OpenSSL Project Authors. All Rights Reserved.
4 * Licensed under the OpenSSL license (the "License"). You may not use
5 * this file except in compliance with the License. You can obtain a copy
6 * in the file LICENSE in the source distribution or at
7 * https://www.openssl.org/source/license.html
11 * Copyright 2004-2014, Akamai Technologies. All Rights Reserved.
12 * This file is distributed under the terms of the OpenSSL license.
16 * This file is in two halves. The first half implements the public API
17 * to be used by external consumers, and to be used by OpenSSL to store
18 * data in a "secure arena." The second half implements the secure arena.
19 * For details on that implementation, see below (look for uppercase
20 * "SECURE HEAP IMPLEMENTATION").
22 #include <openssl/crypto.h>
27 #if defined(OPENSSL_SYS_LINUX) || defined(OPENSSL_SYS_UNIX)
32 # include <sys/types.h>
33 # include <sys/mman.h>
34 # include <sys/param.h>
35 # include <sys/stat.h>
39 #define CLEAR(p, s) OPENSSL_cleanse(p, s)
41 # define PAGE_SIZE 4096
45 static size_t secure_mem_used
;
47 static int secure_mem_initialized
;
49 static CRYPTO_RWLOCK
*sec_malloc_lock
= NULL
;
52 * These are the functions that must be implemented by a secure heap (sh).
54 static int sh_init(size_t size
, int minsize
);
55 static char *sh_malloc(size_t size
);
56 static void sh_free(char *ptr
);
57 static void sh_done(void);
58 static size_t sh_actual_size(char *ptr
);
59 static int sh_allocated(const char *ptr
);
62 int CRYPTO_secure_malloc_init(size_t size
, int minsize
)
67 if (!secure_mem_initialized
) {
68 sec_malloc_lock
= CRYPTO_THREAD_lock_new();
69 if (sec_malloc_lock
== NULL
)
71 ret
= sh_init(size
, minsize
);
72 secure_mem_initialized
= 1;
78 #endif /* IMPLEMENTED */
81 int CRYPTO_secure_malloc_done()
84 if (secure_mem_used
== 0) {
86 secure_mem_initialized
= 0;
87 CRYPTO_THREAD_lock_free(sec_malloc_lock
);
90 #endif /* IMPLEMENTED */
94 int CRYPTO_secure_malloc_initialized()
97 return secure_mem_initialized
;
100 #endif /* IMPLEMENTED */
103 void *CRYPTO_secure_malloc(size_t num
, const char *file
, int line
)
109 if (!secure_mem_initialized
) {
110 return CRYPTO_malloc(num
, file
, line
);
112 CRYPTO_THREAD_write_lock(sec_malloc_lock
);
113 ret
= sh_malloc(num
);
114 actual_size
= ret
? sh_actual_size(ret
) : 0;
115 secure_mem_used
+= actual_size
;
116 CRYPTO_THREAD_unlock(sec_malloc_lock
);
119 return CRYPTO_malloc(num
, file
, line
);
120 #endif /* IMPLEMENTED */
123 void *CRYPTO_secure_zalloc(size_t num
, const char *file
, int line
)
125 void *ret
= CRYPTO_secure_malloc(num
, file
, line
);
132 void CRYPTO_secure_free(void *ptr
, const char *file
, int line
)
139 if (!CRYPTO_secure_allocated(ptr
)) {
140 CRYPTO_free(ptr
, file
, line
);
143 CRYPTO_THREAD_write_lock(sec_malloc_lock
);
144 actual_size
= sh_actual_size(ptr
);
145 CLEAR(ptr
, actual_size
);
146 secure_mem_used
-= actual_size
;
148 CRYPTO_THREAD_unlock(sec_malloc_lock
);
150 CRYPTO_free(ptr
, file
, line
);
151 #endif /* IMPLEMENTED */
154 int CRYPTO_secure_allocated(const void *ptr
)
159 if (!secure_mem_initialized
)
161 CRYPTO_THREAD_write_lock(sec_malloc_lock
);
162 ret
= sh_allocated(ptr
);
163 CRYPTO_THREAD_unlock(sec_malloc_lock
);
167 #endif /* IMPLEMENTED */
170 size_t CRYPTO_secure_used()
173 return secure_mem_used
;
176 #endif /* IMPLEMENTED */
179 size_t CRYPTO_secure_actual_size(void *ptr
)
184 CRYPTO_THREAD_write_lock(sec_malloc_lock
);
185 actual_size
= sh_actual_size(ptr
);
186 CRYPTO_THREAD_unlock(sec_malloc_lock
);
197 * SECURE HEAP IMPLEMENTATION
203 * The implementation provided here uses a fixed-sized mmap() heap,
204 * which is locked into memory, not written to core files, and protected
205 * on either side by an unmapped page, which will catch pointer overruns
206 * (or underruns) and an attempt to read data out of the secure heap.
207 * Free'd memory is zero'd or otherwise cleansed.
209 * This is a pretty standard buddy allocator. We keep areas in a multiple
210 * of "sh.minsize" units. The freelist and bitmaps are kept separately,
211 * so all (and only) data is kept in the mmap'd heap.
213 * This code assumes eight-bit bytes. The numbers 3 and 7 are all over the
217 #define ONE ((size_t)1)
219 # define TESTBIT(t, b) (t[(b) >> 3] & (ONE << ((b) & 7)))
220 # define SETBIT(t, b) (t[(b) >> 3] |= (ONE << ((b) & 7)))
221 # define CLEARBIT(t, b) (t[(b) >> 3] &= (0xFF & ~(ONE << ((b) & 7))))
223 #define WITHIN_ARENA(p) \
224 ((char*)(p) >= sh.arena && (char*)(p) < &sh.arena[sh.arena_size])
225 #define WITHIN_FREELIST(p) \
226 ((char*)(p) >= (char*)sh.freelist && (char*)(p) < (char*)&sh.freelist[sh.freelist_size])
229 typedef struct sh_list_st
231 struct sh_list_st
*next
;
232 struct sh_list_st
**p_next
;
242 ossl_ssize_t freelist_size
;
244 unsigned char *bittable
;
245 unsigned char *bitmalloc
;
246 size_t bittable_size
; /* size in bits */
251 static size_t sh_getlist(char *ptr
)
253 ossl_ssize_t list
= sh
.freelist_size
- 1;
254 size_t bit
= (sh
.arena_size
+ ptr
- sh
.arena
) / sh
.minsize
;
256 for (; bit
; bit
>>= 1, list
--) {
257 if (TESTBIT(sh
.bittable
, bit
))
259 OPENSSL_assert((bit
& 1) == 0);
266 static int sh_testbit(char *ptr
, int list
, unsigned char *table
)
270 OPENSSL_assert(list
>= 0 && list
< sh
.freelist_size
);
271 OPENSSL_assert(((ptr
- sh
.arena
) & ((sh
.arena_size
>> list
) - 1)) == 0);
272 bit
= (ONE
<< list
) + ((ptr
- sh
.arena
) / (sh
.arena_size
>> list
));
273 OPENSSL_assert(bit
> 0 && bit
< sh
.bittable_size
);
274 return TESTBIT(table
, bit
);
277 static void sh_clearbit(char *ptr
, int list
, unsigned char *table
)
281 OPENSSL_assert(list
>= 0 && list
< sh
.freelist_size
);
282 OPENSSL_assert(((ptr
- sh
.arena
) & ((sh
.arena_size
>> list
) - 1)) == 0);
283 bit
= (ONE
<< list
) + ((ptr
- sh
.arena
) / (sh
.arena_size
>> list
));
284 OPENSSL_assert(bit
> 0 && bit
< sh
.bittable_size
);
285 OPENSSL_assert(TESTBIT(table
, bit
));
286 CLEARBIT(table
, bit
);
289 static void sh_setbit(char *ptr
, int list
, unsigned char *table
)
293 OPENSSL_assert(list
>= 0 && list
< sh
.freelist_size
);
294 OPENSSL_assert(((ptr
- sh
.arena
) & ((sh
.arena_size
>> list
) - 1)) == 0);
295 bit
= (ONE
<< list
) + ((ptr
- sh
.arena
) / (sh
.arena_size
>> list
));
296 OPENSSL_assert(bit
> 0 && bit
< sh
.bittable_size
);
297 OPENSSL_assert(!TESTBIT(table
, bit
));
301 static void sh_add_to_list(char **list
, char *ptr
)
305 OPENSSL_assert(WITHIN_FREELIST(list
));
306 OPENSSL_assert(WITHIN_ARENA(ptr
));
308 temp
= (SH_LIST
*)ptr
;
309 temp
->next
= *(SH_LIST
**)list
;
310 OPENSSL_assert(temp
->next
== NULL
|| WITHIN_ARENA(temp
->next
));
311 temp
->p_next
= (SH_LIST
**)list
;
313 if (temp
->next
!= NULL
) {
314 OPENSSL_assert((char **)temp
->next
->p_next
== list
);
315 temp
->next
->p_next
= &(temp
->next
);
321 static void sh_remove_from_list(char *ptr
)
323 SH_LIST
*temp
, *temp2
;
325 temp
= (SH_LIST
*)ptr
;
326 if (temp
->next
!= NULL
)
327 temp
->next
->p_next
= temp
->p_next
;
328 *temp
->p_next
= temp
->next
;
329 if (temp
->next
== NULL
)
333 OPENSSL_assert(WITHIN_FREELIST(temp2
->p_next
) || WITHIN_ARENA(temp2
->p_next
));
337 static int sh_init(size_t size
, int minsize
)
343 memset(&sh
, 0, sizeof sh
);
345 /* make sure size and minsize are powers of 2 */
346 OPENSSL_assert(size
> 0);
347 OPENSSL_assert((size
& (size
- 1)) == 0);
348 OPENSSL_assert(minsize
> 0);
349 OPENSSL_assert((minsize
& (minsize
- 1)) == 0);
350 if (size
<= 0 || (size
& (size
- 1)) != 0)
352 if (minsize
<= 0 || (minsize
& (minsize
- 1)) != 0)
355 sh
.arena_size
= size
;
356 sh
.minsize
= minsize
;
357 sh
.bittable_size
= (sh
.arena_size
/ sh
.minsize
) * 2;
359 /* Prevent allocations of size 0 later on */
360 if (sh
.bittable_size
>> 3 == 0)
363 sh
.freelist_size
= -1;
364 for (i
= sh
.bittable_size
; i
; i
>>= 1)
367 sh
.freelist
= OPENSSL_zalloc(sh
.freelist_size
* sizeof (char *));
368 OPENSSL_assert(sh
.freelist
!= NULL
);
369 if (sh
.freelist
== NULL
)
372 sh
.bittable
= OPENSSL_zalloc(sh
.bittable_size
>> 3);
373 OPENSSL_assert(sh
.bittable
!= NULL
);
374 if (sh
.bittable
== NULL
)
377 sh
.bitmalloc
= OPENSSL_zalloc(sh
.bittable_size
>> 3);
378 OPENSSL_assert(sh
.bitmalloc
!= NULL
);
379 if (sh
.bitmalloc
== NULL
)
382 /* Allocate space for heap, and two extra pages as guards */
383 #if defined(_SC_PAGE_SIZE) || defined (_SC_PAGESIZE)
385 # if defined(_SC_PAGE_SIZE)
386 long tmppgsize
= sysconf(_SC_PAGE_SIZE
);
388 long tmppgsize
= sysconf(_SC_PAGESIZE
);
393 pgsize
= (size_t)tmppgsize
;
398 sh
.map_size
= pgsize
+ sh
.arena_size
+ pgsize
;
401 sh
.map_result
= mmap(NULL
, sh
.map_size
,
402 PROT_READ
|PROT_WRITE
, MAP_ANON
|MAP_PRIVATE
, -1, 0);
407 sh
.map_result
= MAP_FAILED
;
408 if ((fd
= open("/dev/zero", O_RDWR
)) >= 0) {
409 sh
.map_result
= mmap(NULL
, sh
.map_size
,
410 PROT_READ
|PROT_WRITE
, MAP_PRIVATE
, fd
, 0);
414 OPENSSL_assert(sh
.map_result
!= MAP_FAILED
);
415 if (sh
.map_result
== MAP_FAILED
)
417 sh
.arena
= (char *)(sh
.map_result
+ pgsize
);
418 sh_setbit(sh
.arena
, 0, sh
.bittable
);
419 sh_add_to_list(&sh
.freelist
[0], sh
.arena
);
421 /* Now try to add guard pages and lock into memory. */
424 /* Starting guard is already aligned from mmap. */
425 if (mprotect(sh
.map_result
, pgsize
, PROT_NONE
) < 0)
428 /* Ending guard page - need to round up to page boundary */
429 aligned
= (pgsize
+ sh
.arena_size
+ (pgsize
- 1)) & ~(pgsize
- 1);
430 if (mprotect(sh
.map_result
+ aligned
, pgsize
, PROT_NONE
) < 0)
433 if (mlock(sh
.arena
, sh
.arena_size
) < 0)
436 if (madvise(sh
.arena
, sh
.arena_size
, MADV_DONTDUMP
) < 0)
447 static void sh_done()
449 OPENSSL_free(sh
.freelist
);
450 OPENSSL_free(sh
.bittable
);
451 OPENSSL_free(sh
.bitmalloc
);
452 if (sh
.map_result
!= NULL
&& sh
.map_size
)
453 munmap(sh
.map_result
, sh
.map_size
);
454 memset(&sh
, 0, sizeof sh
);
457 static int sh_allocated(const char *ptr
)
459 return WITHIN_ARENA(ptr
) ? 1 : 0;
462 static char *sh_find_my_buddy(char *ptr
, int list
)
467 bit
= (ONE
<< list
) + (ptr
- sh
.arena
) / (sh
.arena_size
>> list
);
470 if (TESTBIT(sh
.bittable
, bit
) && !TESTBIT(sh
.bitmalloc
, bit
))
471 chunk
= sh
.arena
+ ((bit
& ((ONE
<< list
) - 1)) * (sh
.arena_size
>> list
));
476 static char *sh_malloc(size_t size
)
478 ossl_ssize_t list
, slist
;
482 list
= sh
.freelist_size
- 1;
483 for (i
= sh
.minsize
; i
< size
; i
<<= 1)
488 /* try to find a larger entry to split */
489 for (slist
= list
; slist
>= 0; slist
--)
490 if (sh
.freelist
[slist
] != NULL
)
495 /* split larger entry */
496 while (slist
!= list
) {
497 char *temp
= sh
.freelist
[slist
];
499 /* remove from bigger list */
500 OPENSSL_assert(!sh_testbit(temp
, slist
, sh
.bitmalloc
));
501 sh_clearbit(temp
, slist
, sh
.bittable
);
502 sh_remove_from_list(temp
);
503 OPENSSL_assert(temp
!= sh
.freelist
[slist
]);
505 /* done with bigger list */
508 /* add to smaller list */
509 OPENSSL_assert(!sh_testbit(temp
, slist
, sh
.bitmalloc
));
510 sh_setbit(temp
, slist
, sh
.bittable
);
511 sh_add_to_list(&sh
.freelist
[slist
], temp
);
512 OPENSSL_assert(sh
.freelist
[slist
] == temp
);
515 temp
+= sh
.arena_size
>> slist
;
516 OPENSSL_assert(!sh_testbit(temp
, slist
, sh
.bitmalloc
));
517 sh_setbit(temp
, slist
, sh
.bittable
);
518 sh_add_to_list(&sh
.freelist
[slist
], temp
);
519 OPENSSL_assert(sh
.freelist
[slist
] == temp
);
521 OPENSSL_assert(temp
-(sh
.arena_size
>> slist
) == sh_find_my_buddy(temp
, slist
));
524 /* peel off memory to hand back */
525 chunk
= sh
.freelist
[list
];
526 OPENSSL_assert(sh_testbit(chunk
, list
, sh
.bittable
));
527 sh_setbit(chunk
, list
, sh
.bitmalloc
);
528 sh_remove_from_list(chunk
);
530 OPENSSL_assert(WITHIN_ARENA(chunk
));
535 static void sh_free(char *ptr
)
542 OPENSSL_assert(WITHIN_ARENA(ptr
));
543 if (!WITHIN_ARENA(ptr
))
546 list
= sh_getlist(ptr
);
547 OPENSSL_assert(sh_testbit(ptr
, list
, sh
.bittable
));
548 sh_clearbit(ptr
, list
, sh
.bitmalloc
);
549 sh_add_to_list(&sh
.freelist
[list
], ptr
);
551 /* Try to coalesce two adjacent free areas. */
552 while ((buddy
= sh_find_my_buddy(ptr
, list
)) != NULL
) {
553 OPENSSL_assert(ptr
== sh_find_my_buddy(buddy
, list
));
554 OPENSSL_assert(ptr
!= NULL
);
555 OPENSSL_assert(!sh_testbit(ptr
, list
, sh
.bitmalloc
));
556 sh_clearbit(ptr
, list
, sh
.bittable
);
557 sh_remove_from_list(ptr
);
558 OPENSSL_assert(!sh_testbit(ptr
, list
, sh
.bitmalloc
));
559 sh_clearbit(buddy
, list
, sh
.bittable
);
560 sh_remove_from_list(buddy
);
567 OPENSSL_assert(!sh_testbit(ptr
, list
, sh
.bitmalloc
));
568 sh_setbit(ptr
, list
, sh
.bittable
);
569 sh_add_to_list(&sh
.freelist
[list
], ptr
);
570 OPENSSL_assert(sh
.freelist
[list
] == ptr
);
574 static size_t sh_actual_size(char *ptr
)
578 OPENSSL_assert(WITHIN_ARENA(ptr
));
579 if (!WITHIN_ARENA(ptr
))
581 list
= sh_getlist(ptr
);
582 OPENSSL_assert(sh_testbit(ptr
, list
, sh
.bittable
));
583 return sh
.arena_size
/ (ONE
<< list
);
585 #endif /* IMPLEMENTED */