2 * Copyright 2015-2023 The OpenSSL Project Authors. All Rights Reserved.
3 * Copyright 2004-2014, Akamai Technologies. All Rights Reserved.
5 * Licensed under the Apache License 2.0 (the "License"). You may not use
6 * this file except in compliance with the License. You can obtain a copy
7 * in the file LICENSE in the source distribution or at
8 * https://www.openssl.org/source/license.html
12 * This file is in two halves. The first half implements the public API
13 * to be used by external consumers, and to be used by OpenSSL to store
14 * data in a "secure arena." The second half implements the secure arena.
15 * For details on that implementation, see below (look for uppercase
16 * "SECURE HEAP IMPLEMENTATION").
18 #include "internal/e_os.h"
19 #include <openssl/crypto.h>
20 #include <openssl/err.h>
24 #ifndef OPENSSL_NO_SECURE_MEMORY
27 # if defined(WINAPI_FAMILY_PARTITION)
28 # if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
30 * While VirtualLock is available under the app partition (e.g. UWP),
31 * the headers do not define the API. Define it ourselves instead.
37 _In_ LPVOID lpAddress
,
45 # if defined(OPENSSL_SYS_UNIX)
48 # include <sys/types.h>
49 # if defined(OPENSSL_SYS_UNIX)
50 # include <sys/mman.h>
51 # if defined(__FreeBSD__)
52 # define MADV_DONTDUMP MADV_NOCORE
54 # if !defined(MAP_CONCEAL)
55 # define MAP_CONCEAL 0
58 # if defined(OPENSSL_SYS_LINUX)
59 # include <sys/syscall.h>
60 # if defined(SYS_mlock2)
61 # include <linux/mman.h>
64 # include <sys/param.h>
66 # include <sys/stat.h>
70 # if defined(MADV_DONTDUMP)
71 # define HAVE_MADVISE 1
73 # define HAVE_MADVISE 0
82 #define CLEAR(p, s) OPENSSL_cleanse(p, s)
84 # define PAGE_SIZE 4096
86 #if !defined(MAP_ANON) && defined(MAP_ANONYMOUS)
87 # define MAP_ANON MAP_ANONYMOUS
90 #ifndef OPENSSL_NO_SECURE_MEMORY
91 static size_t secure_mem_used
;
93 static int secure_mem_initialized
;
95 static CRYPTO_RWLOCK
*sec_malloc_lock
= NULL
;
98 * These are the functions that must be implemented by a secure heap (sh).
100 static int sh_init(size_t size
, size_t minsize
);
101 static void *sh_malloc(size_t size
);
102 static void sh_free(void *ptr
);
103 static void sh_done(void);
104 static size_t sh_actual_size(char *ptr
);
105 static int sh_allocated(const char *ptr
);
108 int CRYPTO_secure_malloc_init(size_t size
, size_t minsize
)
110 #ifndef OPENSSL_NO_SECURE_MEMORY
113 if (!secure_mem_initialized
) {
114 sec_malloc_lock
= CRYPTO_THREAD_lock_new();
115 if (sec_malloc_lock
== NULL
)
117 if ((ret
= sh_init(size
, minsize
)) != 0) {
118 secure_mem_initialized
= 1;
120 CRYPTO_THREAD_lock_free(sec_malloc_lock
);
121 sec_malloc_lock
= NULL
;
128 #endif /* OPENSSL_NO_SECURE_MEMORY */
131 int CRYPTO_secure_malloc_done(void)
133 #ifndef OPENSSL_NO_SECURE_MEMORY
134 if (secure_mem_used
== 0) {
136 secure_mem_initialized
= 0;
137 CRYPTO_THREAD_lock_free(sec_malloc_lock
);
138 sec_malloc_lock
= NULL
;
141 #endif /* OPENSSL_NO_SECURE_MEMORY */
145 int CRYPTO_secure_malloc_initialized(void)
147 #ifndef OPENSSL_NO_SECURE_MEMORY
148 return secure_mem_initialized
;
151 #endif /* OPENSSL_NO_SECURE_MEMORY */
154 void *CRYPTO_secure_malloc(size_t num
, const char *file
, int line
)
156 #ifndef OPENSSL_NO_SECURE_MEMORY
159 int reason
= CRYPTO_R_SECURE_MALLOC_FAILURE
;
161 if (!secure_mem_initialized
) {
162 return CRYPTO_malloc(num
, file
, line
);
164 if (!CRYPTO_THREAD_write_lock(sec_malloc_lock
)) {
165 reason
= ERR_R_CRYPTO_LIB
;
168 ret
= sh_malloc(num
);
169 actual_size
= ret
? sh_actual_size(ret
) : 0;
170 secure_mem_used
+= actual_size
;
171 CRYPTO_THREAD_unlock(sec_malloc_lock
);
173 if (ret
== NULL
&& (file
!= NULL
|| line
!= 0)) {
175 ERR_set_debug(file
, line
, NULL
);
176 ERR_set_error(ERR_LIB_CRYPTO
, reason
, NULL
);
180 return CRYPTO_malloc(num
, file
, line
);
181 #endif /* OPENSSL_NO_SECURE_MEMORY */
184 void *CRYPTO_secure_zalloc(size_t num
, const char *file
, int line
)
186 #ifndef OPENSSL_NO_SECURE_MEMORY
187 if (secure_mem_initialized
)
188 /* CRYPTO_secure_malloc() zeroes allocations when it is implemented */
189 return CRYPTO_secure_malloc(num
, file
, line
);
191 return CRYPTO_zalloc(num
, file
, line
);
194 void CRYPTO_secure_free(void *ptr
, const char *file
, int line
)
196 #ifndef OPENSSL_NO_SECURE_MEMORY
201 if (!CRYPTO_secure_allocated(ptr
)) {
202 CRYPTO_free(ptr
, file
, line
);
205 if (!CRYPTO_THREAD_write_lock(sec_malloc_lock
))
207 actual_size
= sh_actual_size(ptr
);
208 CLEAR(ptr
, actual_size
);
209 secure_mem_used
-= actual_size
;
211 CRYPTO_THREAD_unlock(sec_malloc_lock
);
213 CRYPTO_free(ptr
, file
, line
);
214 #endif /* OPENSSL_NO_SECURE_MEMORY */
217 void CRYPTO_secure_clear_free(void *ptr
, size_t num
,
218 const char *file
, int line
)
220 #ifndef OPENSSL_NO_SECURE_MEMORY
225 if (!CRYPTO_secure_allocated(ptr
)) {
226 OPENSSL_cleanse(ptr
, num
);
227 CRYPTO_free(ptr
, file
, line
);
230 if (!CRYPTO_THREAD_write_lock(sec_malloc_lock
))
232 actual_size
= sh_actual_size(ptr
);
233 CLEAR(ptr
, actual_size
);
234 secure_mem_used
-= actual_size
;
236 CRYPTO_THREAD_unlock(sec_malloc_lock
);
240 OPENSSL_cleanse(ptr
, num
);
241 CRYPTO_free(ptr
, file
, line
);
242 #endif /* OPENSSL_NO_SECURE_MEMORY */
245 int CRYPTO_secure_allocated(const void *ptr
)
247 #ifndef OPENSSL_NO_SECURE_MEMORY
248 if (!secure_mem_initialized
)
251 * Only read accesses to the arena take place in sh_allocated() and this
252 * is only changed by the sh_init() and sh_done() calls which are not
253 * locked. Hence, it is safe to make this check without a lock too.
255 return sh_allocated(ptr
);
258 #endif /* OPENSSL_NO_SECURE_MEMORY */
261 size_t CRYPTO_secure_used(void)
265 #ifndef OPENSSL_NO_SECURE_MEMORY
266 if (!CRYPTO_THREAD_read_lock(sec_malloc_lock
))
269 ret
= secure_mem_used
;
271 CRYPTO_THREAD_unlock(sec_malloc_lock
);
272 #endif /* OPENSSL_NO_SECURE_MEMORY */
276 size_t CRYPTO_secure_actual_size(void *ptr
)
278 #ifndef OPENSSL_NO_SECURE_MEMORY
281 if (!CRYPTO_THREAD_write_lock(sec_malloc_lock
))
283 actual_size
= sh_actual_size(ptr
);
284 CRYPTO_THREAD_unlock(sec_malloc_lock
);
292 * SECURE HEAP IMPLEMENTATION
294 #ifndef OPENSSL_NO_SECURE_MEMORY
298 * The implementation provided here uses a fixed-sized mmap() heap,
299 * which is locked into memory, not written to core files, and protected
300 * on either side by an unmapped page, which will catch pointer overruns
301 * (or underruns) and an attempt to read data out of the secure heap.
302 * Free'd memory is zero'd or otherwise cleansed.
304 * This is a pretty standard buddy allocator. We keep areas in a multiple
305 * of "sh.minsize" units. The freelist and bitmaps are kept separately,
306 * so all (and only) data is kept in the mmap'd heap.
308 * This code assumes eight-bit bytes. The numbers 3 and 7 are all over the
312 #define ONE ((size_t)1)
314 # define TESTBIT(t, b) (t[(b) >> 3] & (ONE << ((b) & 7)))
315 # define SETBIT(t, b) (t[(b) >> 3] |= (ONE << ((b) & 7)))
316 # define CLEARBIT(t, b) (t[(b) >> 3] &= (0xFF & ~(ONE << ((b) & 7))))
318 #define WITHIN_ARENA(p) \
319 ((char*)(p) >= sh.arena && (char*)(p) < &sh.arena[sh.arena_size])
320 #define WITHIN_FREELIST(p) \
321 ((char*)(p) >= (char*)sh.freelist && (char*)(p) < (char*)&sh.freelist[sh.freelist_size])
324 typedef struct sh_list_st
326 struct sh_list_st
*next
;
327 struct sh_list_st
**p_next
;
337 ossl_ssize_t freelist_size
;
339 unsigned char *bittable
;
340 unsigned char *bitmalloc
;
341 size_t bittable_size
; /* size in bits */
346 static size_t sh_getlist(char *ptr
)
348 ossl_ssize_t list
= sh
.freelist_size
- 1;
349 size_t bit
= (sh
.arena_size
+ ptr
- sh
.arena
) / sh
.minsize
;
351 for (; bit
; bit
>>= 1, list
--) {
352 if (TESTBIT(sh
.bittable
, bit
))
354 OPENSSL_assert((bit
& 1) == 0);
361 static int sh_testbit(char *ptr
, int list
, unsigned char *table
)
365 OPENSSL_assert(list
>= 0 && list
< sh
.freelist_size
);
366 OPENSSL_assert(((ptr
- sh
.arena
) & ((sh
.arena_size
>> list
) - 1)) == 0);
367 bit
= (ONE
<< list
) + ((ptr
- sh
.arena
) / (sh
.arena_size
>> list
));
368 OPENSSL_assert(bit
> 0 && bit
< sh
.bittable_size
);
369 return TESTBIT(table
, bit
);
372 static void sh_clearbit(char *ptr
, int list
, unsigned char *table
)
376 OPENSSL_assert(list
>= 0 && list
< sh
.freelist_size
);
377 OPENSSL_assert(((ptr
- sh
.arena
) & ((sh
.arena_size
>> list
) - 1)) == 0);
378 bit
= (ONE
<< list
) + ((ptr
- sh
.arena
) / (sh
.arena_size
>> list
));
379 OPENSSL_assert(bit
> 0 && bit
< sh
.bittable_size
);
380 OPENSSL_assert(TESTBIT(table
, bit
));
381 CLEARBIT(table
, bit
);
384 static void sh_setbit(char *ptr
, int list
, unsigned char *table
)
388 OPENSSL_assert(list
>= 0 && list
< sh
.freelist_size
);
389 OPENSSL_assert(((ptr
- sh
.arena
) & ((sh
.arena_size
>> list
) - 1)) == 0);
390 bit
= (ONE
<< list
) + ((ptr
- sh
.arena
) / (sh
.arena_size
>> list
));
391 OPENSSL_assert(bit
> 0 && bit
< sh
.bittable_size
);
392 OPENSSL_assert(!TESTBIT(table
, bit
));
396 static void sh_add_to_list(char **list
, char *ptr
)
400 OPENSSL_assert(WITHIN_FREELIST(list
));
401 OPENSSL_assert(WITHIN_ARENA(ptr
));
403 temp
= (SH_LIST
*)ptr
;
404 temp
->next
= *(SH_LIST
**)list
;
405 OPENSSL_assert(temp
->next
== NULL
|| WITHIN_ARENA(temp
->next
));
406 temp
->p_next
= (SH_LIST
**)list
;
408 if (temp
->next
!= NULL
) {
409 OPENSSL_assert((char **)temp
->next
->p_next
== list
);
410 temp
->next
->p_next
= &(temp
->next
);
416 static void sh_remove_from_list(char *ptr
)
418 SH_LIST
*temp
, *temp2
;
420 temp
= (SH_LIST
*)ptr
;
421 if (temp
->next
!= NULL
)
422 temp
->next
->p_next
= temp
->p_next
;
423 *temp
->p_next
= temp
->next
;
424 if (temp
->next
== NULL
)
428 OPENSSL_assert(WITHIN_FREELIST(temp2
->p_next
) || WITHIN_ARENA(temp2
->p_next
));
432 static int sh_init(size_t size
, size_t minsize
)
440 SYSTEM_INFO systemInfo
;
443 memset(&sh
, 0, sizeof(sh
));
445 /* make sure size is a powers of 2 */
446 OPENSSL_assert(size
> 0);
447 OPENSSL_assert((size
& (size
- 1)) == 0);
448 if (size
== 0 || (size
& (size
- 1)) != 0)
451 if (minsize
<= sizeof(SH_LIST
)) {
452 OPENSSL_assert(sizeof(SH_LIST
) <= 65536);
454 * Compute the minimum possible allocation size.
455 * This must be a power of 2 and at least as large as the SH_LIST
458 minsize
= sizeof(SH_LIST
) - 1;
459 minsize
|= minsize
>> 1;
460 minsize
|= minsize
>> 2;
461 if (sizeof(SH_LIST
) > 16)
462 minsize
|= minsize
>> 4;
463 if (sizeof(SH_LIST
) > 256)
464 minsize
|= minsize
>> 8;
467 /* make sure minsize is a powers of 2 */
468 OPENSSL_assert((minsize
& (minsize
- 1)) == 0);
469 if ((minsize
& (minsize
- 1)) != 0)
473 sh
.arena_size
= size
;
474 sh
.minsize
= minsize
;
475 sh
.bittable_size
= (sh
.arena_size
/ sh
.minsize
) * 2;
477 /* Prevent allocations of size 0 later on */
478 if (sh
.bittable_size
>> 3 == 0)
481 sh
.freelist_size
= -1;
482 for (i
= sh
.bittable_size
; i
; i
>>= 1)
485 sh
.freelist
= OPENSSL_zalloc(sh
.freelist_size
* sizeof(char *));
486 OPENSSL_assert(sh
.freelist
!= NULL
);
487 if (sh
.freelist
== NULL
)
490 sh
.bittable
= OPENSSL_zalloc(sh
.bittable_size
>> 3);
491 OPENSSL_assert(sh
.bittable
!= NULL
);
492 if (sh
.bittable
== NULL
)
495 sh
.bitmalloc
= OPENSSL_zalloc(sh
.bittable_size
>> 3);
496 OPENSSL_assert(sh
.bitmalloc
!= NULL
);
497 if (sh
.bitmalloc
== NULL
)
500 /* Allocate space for heap, and two extra pages as guards */
501 #if defined(_SC_PAGE_SIZE) || defined (_SC_PAGESIZE)
503 # if defined(_SC_PAGE_SIZE)
504 long tmppgsize
= sysconf(_SC_PAGE_SIZE
);
506 long tmppgsize
= sysconf(_SC_PAGESIZE
);
511 pgsize
= (size_t)tmppgsize
;
513 #elif defined(_WIN32)
514 GetSystemInfo(&systemInfo
);
515 pgsize
= (size_t)systemInfo
.dwPageSize
;
519 sh
.map_size
= pgsize
+ sh
.arena_size
+ pgsize
;
523 sh
.map_result
= mmap(NULL
, sh
.map_size
,
524 PROT_READ
|PROT_WRITE
, MAP_ANON
|MAP_PRIVATE
|MAP_CONCEAL
, -1, 0);
529 sh
.map_result
= MAP_FAILED
;
530 if ((fd
= open("/dev/zero", O_RDWR
)) >= 0) {
531 sh
.map_result
= mmap(NULL
, sh
.map_size
,
532 PROT_READ
|PROT_WRITE
, MAP_PRIVATE
, fd
, 0);
537 if (sh
.map_result
== MAP_FAILED
)
540 sh
.map_result
= VirtualAlloc(NULL
, sh
.map_size
, MEM_COMMIT
| MEM_RESERVE
, PAGE_READWRITE
);
542 if (sh
.map_result
== NULL
)
546 sh
.arena
= (char *)(sh
.map_result
+ pgsize
);
547 sh_setbit(sh
.arena
, 0, sh
.bittable
);
548 sh_add_to_list(&sh
.freelist
[0], sh
.arena
);
550 /* Now try to add guard pages and lock into memory. */
554 /* Starting guard is already aligned from mmap. */
555 if (mprotect(sh
.map_result
, pgsize
, PROT_NONE
) < 0)
558 if (VirtualProtect(sh
.map_result
, pgsize
, PAGE_NOACCESS
, &flOldProtect
) == FALSE
)
562 /* Ending guard page - need to round up to page boundary */
563 aligned
= (pgsize
+ sh
.arena_size
+ (pgsize
- 1)) & ~(pgsize
- 1);
565 if (mprotect(sh
.map_result
+ aligned
, pgsize
, PROT_NONE
) < 0)
568 if (VirtualProtect(sh
.map_result
+ aligned
, pgsize
, PAGE_NOACCESS
, &flOldProtect
) == FALSE
)
572 #if defined(OPENSSL_SYS_LINUX) && defined(MLOCK_ONFAULT) && defined(SYS_mlock2)
573 if (syscall(SYS_mlock2
, sh
.arena
, sh
.arena_size
, MLOCK_ONFAULT
) < 0) {
574 if (errno
== ENOSYS
) {
575 if (mlock(sh
.arena
, sh
.arena_size
) < 0)
581 #elif defined(_WIN32)
582 if (VirtualLock(sh
.arena
, sh
.arena_size
) == FALSE
)
585 if (mlock(sh
.arena
, sh
.arena_size
) < 0)
589 if (madvise(sh
.arena
, sh
.arena_size
, MADV_DONTDUMP
) < 0)
600 static void sh_done(void)
602 OPENSSL_free(sh
.freelist
);
603 OPENSSL_free(sh
.bittable
);
604 OPENSSL_free(sh
.bitmalloc
);
606 if (sh
.map_result
!= MAP_FAILED
&& sh
.map_size
)
607 munmap(sh
.map_result
, sh
.map_size
);
609 if (sh
.map_result
!= NULL
&& sh
.map_size
)
610 VirtualFree(sh
.map_result
, 0, MEM_RELEASE
);
612 memset(&sh
, 0, sizeof(sh
));
615 static int sh_allocated(const char *ptr
)
617 return WITHIN_ARENA(ptr
) ? 1 : 0;
620 static char *sh_find_my_buddy(char *ptr
, int list
)
625 bit
= (ONE
<< list
) + (ptr
- sh
.arena
) / (sh
.arena_size
>> list
);
628 if (TESTBIT(sh
.bittable
, bit
) && !TESTBIT(sh
.bitmalloc
, bit
))
629 chunk
= sh
.arena
+ ((bit
& ((ONE
<< list
) - 1)) * (sh
.arena_size
>> list
));
634 static void *sh_malloc(size_t size
)
636 ossl_ssize_t list
, slist
;
640 if (size
> sh
.arena_size
)
643 list
= sh
.freelist_size
- 1;
644 for (i
= sh
.minsize
; i
< size
; i
<<= 1)
649 /* try to find a larger entry to split */
650 for (slist
= list
; slist
>= 0; slist
--)
651 if (sh
.freelist
[slist
] != NULL
)
656 /* split larger entry */
657 while (slist
!= list
) {
658 char *temp
= sh
.freelist
[slist
];
660 /* remove from bigger list */
661 OPENSSL_assert(!sh_testbit(temp
, slist
, sh
.bitmalloc
));
662 sh_clearbit(temp
, slist
, sh
.bittable
);
663 sh_remove_from_list(temp
);
664 OPENSSL_assert(temp
!= sh
.freelist
[slist
]);
666 /* done with bigger list */
669 /* add to smaller list */
670 OPENSSL_assert(!sh_testbit(temp
, slist
, sh
.bitmalloc
));
671 sh_setbit(temp
, slist
, sh
.bittable
);
672 sh_add_to_list(&sh
.freelist
[slist
], temp
);
673 OPENSSL_assert(sh
.freelist
[slist
] == temp
);
676 temp
+= sh
.arena_size
>> slist
;
677 OPENSSL_assert(!sh_testbit(temp
, slist
, sh
.bitmalloc
));
678 sh_setbit(temp
, slist
, sh
.bittable
);
679 sh_add_to_list(&sh
.freelist
[slist
], temp
);
680 OPENSSL_assert(sh
.freelist
[slist
] == temp
);
682 OPENSSL_assert(temp
-(sh
.arena_size
>> slist
) == sh_find_my_buddy(temp
, slist
));
685 /* peel off memory to hand back */
686 chunk
= sh
.freelist
[list
];
687 OPENSSL_assert(sh_testbit(chunk
, list
, sh
.bittable
));
688 sh_setbit(chunk
, list
, sh
.bitmalloc
);
689 sh_remove_from_list(chunk
);
691 OPENSSL_assert(WITHIN_ARENA(chunk
));
693 /* zero the free list header as a precaution against information leakage */
694 memset(chunk
, 0, sizeof(SH_LIST
));
699 static void sh_free(void *ptr
)
706 OPENSSL_assert(WITHIN_ARENA(ptr
));
707 if (!WITHIN_ARENA(ptr
))
710 list
= sh_getlist(ptr
);
711 OPENSSL_assert(sh_testbit(ptr
, list
, sh
.bittable
));
712 sh_clearbit(ptr
, list
, sh
.bitmalloc
);
713 sh_add_to_list(&sh
.freelist
[list
], ptr
);
715 /* Try to coalesce two adjacent free areas. */
716 while ((buddy
= sh_find_my_buddy(ptr
, list
)) != NULL
) {
717 OPENSSL_assert(ptr
== sh_find_my_buddy(buddy
, list
));
718 OPENSSL_assert(ptr
!= NULL
);
719 OPENSSL_assert(!sh_testbit(ptr
, list
, sh
.bitmalloc
));
720 sh_clearbit(ptr
, list
, sh
.bittable
);
721 sh_remove_from_list(ptr
);
722 OPENSSL_assert(!sh_testbit(ptr
, list
, sh
.bitmalloc
));
723 sh_clearbit(buddy
, list
, sh
.bittable
);
724 sh_remove_from_list(buddy
);
728 /* Zero the higher addressed block's free list pointers */
729 memset(ptr
> buddy
? ptr
: buddy
, 0, sizeof(SH_LIST
));
733 OPENSSL_assert(!sh_testbit(ptr
, list
, sh
.bitmalloc
));
734 sh_setbit(ptr
, list
, sh
.bittable
);
735 sh_add_to_list(&sh
.freelist
[list
], ptr
);
736 OPENSSL_assert(sh
.freelist
[list
] == ptr
);
740 static size_t sh_actual_size(char *ptr
)
744 OPENSSL_assert(WITHIN_ARENA(ptr
));
745 if (!WITHIN_ARENA(ptr
))
747 list
= sh_getlist(ptr
);
748 OPENSSL_assert(sh_testbit(ptr
, list
, sh
.bittable
));
749 return sh
.arena_size
/ (ONE
<< list
);
751 #endif /* OPENSSL_NO_SECURE_MEMORY */