diff options
Diffstat (limited to 'src/libsodium/sodium/utils.c')
-rw-r--r-- | src/libsodium/sodium/utils.c | 340 |
1 files changed, 307 insertions, 33 deletions
diff --git a/src/libsodium/sodium/utils.c b/src/libsodium/sodium/utils.c index eff9d0c..e51ae6b 100644 --- a/src/libsodium/sodium/utils.c +++ b/src/libsodium/sodium/utils.c @@ -1,8 +1,10 @@ #ifndef __STDC_WANT_LIB_EXT1__ # define __STDC_WANT_LIB_EXT1__ 1 #endif +#include <assert.h> #include <errno.h> #include <limits.h> +#include <signal.h> #include <stddef.h> #include <stdint.h> #include <stdlib.h> @@ -17,8 +19,32 @@ #ifdef _WIN32 # include <windows.h> # include <wincrypt.h> +#else +# include <unistd.h> +#endif + +#define CANARY_SIZE 16U +#define GARBAGE_VALUE 0xd0 + +#ifndef MAP_NOCORE +# define MAP_NOCORE 0 +#endif +#if !defined(MAP_ANON) && defined(MAP_ANONYMOUS) +# define MAP_ANON MAP_ANONYMOUS +#endif +#if defined(_WIN32) || defined(MAP_ANON) || defined(HAVE_POSIX_MEMALIGN) +# define HAVE_ALIGNED_MALLOC +#endif +#if defined(HAVE_MPROTECT) && !(defined(PROT_NONE) && defined(PROT_READ) && defined(PROT_WRITE)) +# undef HAVE_MPROTECT +#endif +#if defined(HAVE_ALIGNED_MALLOC) && (defined(_WIN32) || defined(HAVE_MPROTECT)) +# define HAVE_PAGE_PROTECTION #endif +static size_t page_size; +static unsigned char canary[CANARY_SIZE]; + #ifdef HAVE_WEAK_SYMBOLS __attribute__((weak)) void __sodium_dummy_symbol_to_prevent_lto(void * const pnt, const size_t len) @@ -31,11 +57,11 @@ __sodium_dummy_symbol_to_prevent_lto(void * const pnt, const size_t len) void sodium_memzero(void * const pnt, const size_t len) { -#ifdef HAVE_SECUREZEROMEMORY +#ifdef _WIN32 SecureZeroMemory(pnt, len); #elif defined(HAVE_MEMSET_S) if (memset_s(pnt, (rsize_t) len, 0, (rsize_t) len) != 0) { - abort(); + abort(); /* LCOV_EXCL_LINE */ } #elif defined(HAVE_EXPLICIT_BZERO) explicit_bzero(pnt, len); @@ -66,34 +92,6 @@ sodium_memcmp(const void * const b1_, const void * const b2_, size_t len) return (int) ((1 & ((d - 1) >> 8)) - 1); } -unsigned char * -_sodium_alignedcalloc(unsigned char ** const unaligned_p, const size_t len) -{ - unsigned char *aligned; - unsigned char *unaligned; - size_t i; - - if (SIZE_MAX - (size_t) 256U < len || - (unaligned = (unsigned char *) malloc(len + (size_t) 256U)) == NULL) { - *unaligned_p = NULL; - return NULL; - } - *unaligned_p = unaligned; -#ifdef HAVE_ARC4RANDOM_BUF - (void) i; - arc4random_buf(unaligned, len + (size_t) 256U); -#else - for (i = (size_t) 0U; i < len + (size_t) 256U; ++i) { - unaligned[i] = (unsigned char) rand(); - } -#endif - aligned = unaligned + 64; - aligned += (ptrdiff_t) 63 & (-(ptrdiff_t) aligned); - memset(aligned, 0, len); - - return aligned; -} - char * sodium_bin2hex(char * const hex, const size_t hex_maxlen, const unsigned char * const bin, const size_t bin_len) @@ -106,7 +104,7 @@ sodium_bin2hex(char * const hex, const size_t hex_maxlen, size_t j = (size_t) 0U; if (bin_len >= SIZE_MAX / 2 || hex_maxlen < bin_len * 2U) { - abort(); + abort(); /* LCOV_EXCL_LINE */ } while (i < bin_len) { hex[j++] = hexdigits[bin[i] >> 4]; @@ -178,7 +176,7 @@ sodium_mlock(void * const addr, const size_t len) #endif #ifdef HAVE_MLOCK return mlock(addr, len); -#elif defined(HAVE_VIRTUALLOCK) +#elif defined(_WIN32) return -(VirtualLock(addr, len) == 0); #else errno = ENOSYS; @@ -195,10 +193,286 @@ sodium_munlock(void * const addr, const size_t len) #endif #ifdef HAVE_MLOCK return munlock(addr, len); -#elif defined(HAVE_VIRTUALLOCK) +#elif defined(_WIN32) return -(VirtualUnlock(addr, len) == 0); #else errno = ENOSYS; return -1; #endif } + +int +_sodium_alloc_init(void) +{ +#if defined(_SC_PAGESIZE) + long page_size_ = sysconf(_SC_PAGESIZE); + if (page_size_ > 0L) { + page_size = (size_t) page_size_; + } +#elif defined(_WIN32) + SYSTEM_INFO si; + GetSystemInfo(&si); + page_size = (size_t) si.dwPageSize; +#endif + if (page_size < CANARY_SIZE) { + abort(); /* LCOV_EXCL_LINE */ + } + randombytes_buf(canary, sizeof canary); + + return 0; +} + +static inline size_t +_page_round(const size_t size) +{ + const size_t page_mask = page_size - 1U; + + return (size + page_mask) & ~page_mask; +} + +static int +_mprotect_noaccess(void *ptr, size_t size) +{ +#if defined(HAVE_MPROTECT) && defined(HAVE_PAGE_PROTECTION) + return mprotect(ptr, size, PROT_NONE); +#elif defined(_WIN32) + { + DWORD old; + return -(VirtualProtect(ptr, size, PAGE_NOACCESS, &old) == 0); + } +#else + errno = ENOSYS; + return -1; +#endif +} + +static int +_mprotect_readonly(void *ptr, size_t size) +{ +#if defined(HAVE_MPROTECT) && defined(HAVE_PAGE_PROTECTION) + return mprotect(ptr, size, PROT_READ); +#elif defined(_WIN32) + { + DWORD old; + return -(VirtualProtect(ptr, size, PAGE_READONLY, &old) == 0); + } +#else + errno = ENOSYS; + return -1; +#endif +} + +static int +_mprotect_readwrite(void *ptr, size_t size) +{ +#if defined(HAVE_MPROTECT) && defined(HAVE_PAGE_PROTECTION) + return mprotect(ptr, size, PROT_READ | PROT_WRITE); +#elif defined(_WIN32) + { + DWORD old; + return -(VirtualProtect(ptr, size, PAGE_READWRITE, &old) == 0); + } +#else + errno = ENOSYS; + return -1; +#endif +} + +static void +_out_of_bounds(void) +{ +#ifdef SIGSEGV + raise(SIGSEGV); +#elif defined(SIGKILL) + raise(SIGKILL); +#endif + abort(); +} /* LCOV_EXCL_LINE */ + +static __attribute__((malloc)) unsigned char * +_alloc_aligned(const size_t size) +{ + void *ptr; + +#ifdef MAP_ANON + if ((ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, + MAP_ANON | MAP_PRIVATE | MAP_NOCORE, -1, 0)) == MAP_FAILED) { + ptr = NULL; /* LCOV_EXCL_LINE */ + } /* LCOV_EXCL_LINE */ +#elif defined(HAVE_POSIX_MEMALIGN) + if (posix_memalign(&ptr, page_size, size) != 0) { + ptr = NULL; /* LCOV_EXCL_LINE */ + } /* LCOV_EXCL_LINE */ +#elif defined(_WIN32) + ptr = VirtualAlloc(NULL, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); +#elif !defined(HAVE_ALIGNED_MALLOC) + ptr = malloc(size); +#else +# error Bug +#endif + return (unsigned char *) ptr; +} + +static void +_free_aligned(unsigned char * const ptr, const size_t size) +{ +#ifdef MAP_ANON + (void) munmap(ptr, size); +#elif defined(HAVE_POSIX_MEMALIGN) + free(ptr); +#elif defined(_WIN32) + VirtualFree(ptr, 0U, MEM_RELEASE); +#else + free(ptr); +#endif +} + +static unsigned char * +_unprotected_ptr_from_user_ptr(const void *ptr) +{ + uintptr_t unprotected_ptr_u; + unsigned char *canary_ptr; + size_t page_mask; + + canary_ptr = ((unsigned char *) ptr) - sizeof canary; + page_mask = page_size - 1U; + unprotected_ptr_u = ((uintptr_t) canary_ptr & (uintptr_t) ~page_mask); + if (unprotected_ptr_u <= page_size * 2U) { + abort(); /* LCOV_EXCL_LINE */ + } + return (unsigned char *) unprotected_ptr_u; +} + +static __attribute__((malloc)) void * +_sodium_malloc(const size_t size) +{ + void *user_ptr; + unsigned char *base_ptr; + unsigned char *canary_ptr; + unsigned char *unprotected_ptr; + size_t page_mask; + size_t size_with_canary; + size_t total_size; + size_t unprotected_size; + + if (size >= SIZE_MAX - page_size * 4U) { + errno = ENOMEM; + return NULL; + } + if (page_size <= sizeof canary || page_size < sizeof unprotected_size) { + abort(); /* LCOV_EXCL_LINE */ + } + size_with_canary = (sizeof canary) + size; + unprotected_size = _page_round(size_with_canary); + total_size = page_size + page_size + unprotected_size + page_size; + if ((base_ptr = _alloc_aligned(total_size)) == NULL) { + return NULL; /* LCOV_EXCL_LINE */ + } + unprotected_ptr = base_ptr + page_size * 2U; + _mprotect_noaccess(base_ptr + page_size, page_size); +#ifndef HAVE_PAGE_PROTECTION + memcpy(unprotected_ptr + unprotected_size, canary, sizeof canary); +#endif + _mprotect_noaccess(unprotected_ptr + unprotected_size, page_size); + sodium_mlock(unprotected_ptr, unprotected_size); + page_mask = page_size - 1U; + canary_ptr = unprotected_ptr + _page_round(size_with_canary) - + size_with_canary; + user_ptr = canary_ptr + sizeof canary; + memcpy(canary_ptr, canary, sizeof canary); + memcpy(base_ptr, &unprotected_size, sizeof unprotected_size); + _mprotect_readonly(base_ptr, page_size); + assert(_unprotected_ptr_from_user_ptr(user_ptr) == unprotected_ptr); + + return user_ptr; +} + +__attribute__((malloc)) void * +sodium_malloc(const size_t size) +{ + void *ptr; + + if ((ptr = _sodium_malloc(size)) == NULL) { + return NULL; /* LCOV_EXCL_LINE */ + } + memset(ptr, (int) GARBAGE_VALUE, size); + + return ptr; +} + +__attribute__((malloc)) void * +sodium_allocarray(size_t count, size_t size) +{ + size_t total_size; + + if (size >= SIZE_MAX / count) { + errno = ENOMEM; + return NULL; + } + total_size = count * size; + + return sodium_malloc(total_size); +} + +void +sodium_free(void *ptr) +{ + unsigned char *base_ptr; + unsigned char *canary_ptr; + unsigned char *unprotected_ptr; + size_t total_size; + size_t unprotected_size; + + if (ptr == NULL) { + return; + } + canary_ptr = ((unsigned char *) ptr) - sizeof canary; + if (sodium_memcmp(canary_ptr, canary, sizeof canary) != 0) { + _out_of_bounds(); + } + unprotected_ptr = _unprotected_ptr_from_user_ptr(ptr); + base_ptr = unprotected_ptr - page_size * 2U; + memcpy(&unprotected_size, base_ptr, sizeof unprotected_size); + total_size = page_size + page_size + unprotected_size + page_size; + _mprotect_readwrite(base_ptr, total_size); +#ifndef HAVE_PAGE_PROTECTION + if (sodium_memcmp(unprotected_ptr + unprotected_size, + canary, sizeof canary) != 0) { + _out_of_bounds(); + } +#endif + sodium_munlock(unprotected_ptr, unprotected_size); + _free_aligned(base_ptr, total_size); +} + +static int +_sodium_mprotect(void *ptr, int (*cb)(void *ptr, size_t size)) +{ + unsigned char *base_ptr; + unsigned char *unprotected_ptr; + size_t unprotected_size; + + unprotected_ptr = _unprotected_ptr_from_user_ptr(ptr); + base_ptr = unprotected_ptr - page_size * 2U; + memcpy(&unprotected_size, base_ptr, sizeof unprotected_size); + + return cb(unprotected_ptr, unprotected_size); +} + +int +sodium_mprotect_noaccess(void *ptr) +{ + return _sodium_mprotect(ptr, _mprotect_noaccess); +} + +int +sodium_mprotect_readonly(void *ptr) +{ + return _sodium_mprotect(ptr, _mprotect_readonly); +} + +int +sodium_mprotect_readwrite(void *ptr) +{ + return _sodium_mprotect(ptr, _mprotect_readwrite); +} |