summaryrefslogtreecommitdiff
path: root/src/libsodium/sodium/utils.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libsodium/sodium/utils.c')
-rw-r--r--src/libsodium/sodium/utils.c340
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);
+}