Add an Ed25519 signature maleability test
[openssl.git] / crypto / mem_sec.c
index 31c052512d513cc89d8e256cbe4fd2ce94c51c4a..9e0f6702f4069d7cdce6098e42877da8b3f417ef 100644 (file)
@@ -1,6 +1,11 @@
 /*
+ * Copyright 2015-2018 The OpenSSL Project Authors. All Rights Reserved.
  * Copyright 2004-2014, Akamai Technologies. All Rights Reserved.
- * This file is distributed under the terms of the OpenSSL license.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
  */
 
 /*
  * For details on that implementation, see below (look for uppercase
  * "SECURE HEAP IMPLEMENTATION").
  */
+#include "e_os.h"
 #include <openssl/crypto.h>
-#include <e_os.h>
 
 #include <string.h>
 
-#if defined(OPENSSL_SYS_LINUX) || defined(OPENSSL_SYS_UNIX)
-# define IMPLEMENTED
+/* e_os.h defines OPENSSL_SECURE_MEMORY if secure memory can be implemented */
+#ifdef OPENSSL_SECURE_MEMORY
 # include <stdlib.h>
 # include <assert.h>
 # include <unistd.h>
 # include <sys/types.h>
 # include <sys/mman.h>
+# if defined(OPENSSL_SYS_LINUX)
+#  include <sys/syscall.h>
+#  if defined(SYS_mlock2)
+#   include <linux/mman.h>
+#   include <errno.h>
+#  endif
+# endif
 # include <sys/param.h>
 # include <sys/stat.h>
 # include <fcntl.h>
-# include "internal/threads.h"
 #endif
 
 #define CLEAR(p, s) OPENSSL_cleanse(p, s)
 #ifndef PAGE_SIZE
 # define PAGE_SIZE    4096
 #endif
+#if !defined(MAP_ANON) && defined(MAP_ANONYMOUS)
+# define MAP_ANON MAP_ANONYMOUS
+#endif
 
-#ifdef IMPLEMENTED
+#ifdef OPENSSL_SECURE_MEMORY
 static size_t secure_mem_used;
 
 static int secure_mem_initialized;
-static int too_late;
 
 static CRYPTO_RWLOCK *sec_malloc_lock = NULL;
 
@@ -45,62 +58,66 @@ static CRYPTO_RWLOCK *sec_malloc_lock = NULL;
  * These are the functions that must be implemented by a secure heap (sh).
  */
 static int sh_init(size_t size, int minsize);
-static char *sh_malloc(size_t size);
-static void sh_free(char *ptr);
+static void *sh_malloc(size_t size);
+static void sh_free(void *ptr);
 static void sh_done(void);
-static int sh_actual_size(char *ptr);
+static size_t sh_actual_size(char *ptr);
 static int sh_allocated(const char *ptr);
 #endif
 
 int CRYPTO_secure_malloc_init(size_t size, int minsize)
 {
-#ifdef IMPLEMENTED
+#ifdef OPENSSL_SECURE_MEMORY
     int ret = 0;
 
-    if (too_late)
-        return ret;
-
-    OPENSSL_assert(!secure_mem_initialized);
     if (!secure_mem_initialized) {
         sec_malloc_lock = CRYPTO_THREAD_lock_new();
         if (sec_malloc_lock == NULL)
             return 0;
-        ret = sh_init(size, minsize);
-        secure_mem_initialized = 1;
+        if ((ret = sh_init(size, minsize)) != 0) {
+            secure_mem_initialized = 1;
+        } else {
+            CRYPTO_THREAD_lock_free(sec_malloc_lock);
+            sec_malloc_lock = NULL;
+        }
     }
 
     return ret;
 #else
     return 0;
-#endif /* IMPLEMENTED */
+#endif /* OPENSSL_SECURE_MEMORY */
 }
 
-void CRYPTO_secure_malloc_done()
+int CRYPTO_secure_malloc_done(void)
 {
-#ifdef IMPLEMENTED
-    sh_done();
-    secure_mem_initialized = 0;
-    CRYPTO_THREAD_lock_free(sec_malloc_lock);
-#endif /* IMPLEMENTED */
+#ifdef OPENSSL_SECURE_MEMORY
+    if (secure_mem_used == 0) {
+        sh_done();
+        secure_mem_initialized = 0;
+        CRYPTO_THREAD_lock_free(sec_malloc_lock);
+        sec_malloc_lock = NULL;
+        return 1;
+    }
+#endif /* OPENSSL_SECURE_MEMORY */
+    return 0;
 }
 
-int CRYPTO_secure_malloc_initialized()
+int CRYPTO_secure_malloc_initialized(void)
 {
-#ifdef IMPLEMENTED
+#ifdef OPENSSL_SECURE_MEMORY
     return secure_mem_initialized;
 #else
     return 0;
-#endif /* IMPLEMENTED */
+#endif /* OPENSSL_SECURE_MEMORY */
 }
 
 void *CRYPTO_secure_malloc(size_t num, const char *file, int line)
 {
-#ifdef IMPLEMENTED
+#ifdef OPENSSL_SECURE_MEMORY
     void *ret;
     size_t actual_size;
 
     if (!secure_mem_initialized) {
-        too_late = 1;
         return CRYPTO_malloc(num, file, line);
     }
     CRYPTO_THREAD_write_lock(sec_malloc_lock);
@@ -111,26 +128,51 @@ void *CRYPTO_secure_malloc(size_t num, const char *file, int line)
     return ret;
 #else
     return CRYPTO_malloc(num, file, line);
-#endif /* IMPLEMENTED */
+#endif /* OPENSSL_SECURE_MEMORY */
 }
 
 void *CRYPTO_secure_zalloc(size_t num, const char *file, int line)
 {
-    void *ret = CRYPTO_secure_malloc(num, file, line);
-
-    if (ret != NULL)
-        memset(ret, 0, num);
-    return ret;
+#ifdef OPENSSL_SECURE_MEMORY
+    if (secure_mem_initialized)
+        /* CRYPTO_secure_malloc() zeroes allocations when it is implemented */
+        return CRYPTO_secure_malloc(num, file, line);
+#endif
+    return CRYPTO_zalloc(num, file, line);
 }
 
 void CRYPTO_secure_free(void *ptr, const char *file, int line)
 {
-#ifdef IMPLEMENTED
+#ifdef OPENSSL_SECURE_MEMORY
     size_t actual_size;
 
     if (ptr == NULL)
         return;
-    if (!secure_mem_initialized) {
+    if (!CRYPTO_secure_allocated(ptr)) {
+        CRYPTO_free(ptr, file, line);
+        return;
+    }
+    CRYPTO_THREAD_write_lock(sec_malloc_lock);
+    actual_size = sh_actual_size(ptr);
+    CLEAR(ptr, actual_size);
+    secure_mem_used -= actual_size;
+    sh_free(ptr);
+    CRYPTO_THREAD_unlock(sec_malloc_lock);
+#else
+    CRYPTO_free(ptr, file, line);
+#endif /* OPENSSL_SECURE_MEMORY */
+}
+
+void CRYPTO_secure_clear_free(void *ptr, size_t num,
+                              const char *file, int line)
+{
+#ifdef OPENSSL_SECURE_MEMORY
+    size_t actual_size;
+
+    if (ptr == NULL)
+        return;
+    if (!CRYPTO_secure_allocated(ptr)) {
+        OPENSSL_cleanse(ptr, num);
         CRYPTO_free(ptr, file, line);
         return;
     }
@@ -141,13 +183,16 @@ void CRYPTO_secure_free(void *ptr, const char *file, int line)
     sh_free(ptr);
     CRYPTO_THREAD_unlock(sec_malloc_lock);
 #else
+    if (ptr == NULL)
+        return;
+    OPENSSL_cleanse(ptr, num);
     CRYPTO_free(ptr, file, line);
-#endif /* IMPLEMENTED */
+#endif /* OPENSSL_SECURE_MEMORY */
 }
 
 int CRYPTO_secure_allocated(const void *ptr)
 {
-#ifdef IMPLEMENTED
+#ifdef OPENSSL_SECURE_MEMORY
     int ret;
 
     if (!secure_mem_initialized)
@@ -158,21 +203,21 @@ int CRYPTO_secure_allocated(const void *ptr)
     return ret;
 #else
     return 0;
-#endif /* IMPLEMENTED */
+#endif /* OPENSSL_SECURE_MEMORY */
 }
 
-size_t CRYPTO_secure_used()
+size_t CRYPTO_secure_used(void)
 {
-#ifdef IMPLEMENTED
+#ifdef OPENSSL_SECURE_MEMORY
     return secure_mem_used;
 #else
     return 0;
-#endif /* IMPLEMENTED */
+#endif /* OPENSSL_SECURE_MEMORY */
 }
 
 size_t CRYPTO_secure_actual_size(void *ptr)
 {
-#ifdef IMPLEMENTED
+#ifdef OPENSSL_SECURE_MEMORY
     size_t actual_size;
 
     CRYPTO_THREAD_write_lock(sec_malloc_lock);
@@ -190,7 +235,7 @@ size_t CRYPTO_secure_actual_size(void *ptr)
 /*
  * SECURE HEAP IMPLEMENTATION
  */
-#ifdef IMPLEMENTED
+#ifdef OPENSSL_SECURE_MEMORY
 
 
 /*
@@ -208,9 +253,11 @@ size_t CRYPTO_secure_actual_size(void *ptr)
  * place.
  */
 
-# define TESTBIT(t, b)  (t[(b) >> 3] &  (1 << ((b) & 7)))
-# define SETBIT(t, b)   (t[(b) >> 3] |= (1 << ((b) & 7)))
-# define CLEARBIT(t, b) (t[(b) >> 3] &= (0xFF & ~(1 << ((b) & 7))))
+#define ONE ((size_t)1)
+
+# define TESTBIT(t, b)  (t[(b) >> 3] &  (ONE << ((b) & 7)))
+# define SETBIT(t, b)   (t[(b) >> 3] |= (ONE << ((b) & 7)))
+# define CLEARBIT(t, b) (t[(b) >> 3] &= (0xFF & ~(ONE << ((b) & 7))))
 
 #define WITHIN_ARENA(p) \
     ((char*)(p) >= sh.arena && (char*)(p) < &sh.arena[sh.arena_size])
@@ -229,21 +276,21 @@ typedef struct sh_st
     char* map_result;
     size_t map_size;
     char *arena;
-    int arena_size;
+    size_t arena_size;
     char **freelist;
-    int freelist_size;
-    int minsize;
+    ossl_ssize_t freelist_size;
+    size_t minsize;
     unsigned char *bittable;
     unsigned char *bitmalloc;
-    int bittable_size; /* size in bits */
+    size_t bittable_size; /* size in bits */
 } SH;
 
 static SH sh;
 
-static int sh_getlist(char *ptr)
+static size_t sh_getlist(char *ptr)
 {
-    int list = sh.freelist_size - 1;
-    int bit = (sh.arena_size + ptr - sh.arena) / sh.minsize;
+    ossl_ssize_t list = sh.freelist_size - 1;
+    size_t bit = (sh.arena_size + ptr - sh.arena) / sh.minsize;
 
     for (; bit; bit >>= 1, list--) {
         if (TESTBIT(sh.bittable, bit))
@@ -257,22 +304,22 @@ static int sh_getlist(char *ptr)
 
 static int sh_testbit(char *ptr, int list, unsigned char *table)
 {
-    int bit;
+    size_t bit;
 
     OPENSSL_assert(list >= 0 && list < sh.freelist_size);
     OPENSSL_assert(((ptr - sh.arena) & ((sh.arena_size >> list) - 1)) == 0);
-    bit = (1 << list) + ((ptr - sh.arena) / (sh.arena_size >> list));
+    bit = (ONE << list) + ((ptr - sh.arena) / (sh.arena_size >> list));
     OPENSSL_assert(bit > 0 && bit < sh.bittable_size);
     return TESTBIT(table, bit);
 }
 
 static void sh_clearbit(char *ptr, int list, unsigned char *table)
 {
-    int bit;
+    size_t bit;
 
     OPENSSL_assert(list >= 0 && list < sh.freelist_size);
     OPENSSL_assert(((ptr - sh.arena) & ((sh.arena_size >> list) - 1)) == 0);
-    bit = (1 << list) + ((ptr - sh.arena) / (sh.arena_size >> list));
+    bit = (ONE << list) + ((ptr - sh.arena) / (sh.arena_size >> list));
     OPENSSL_assert(bit > 0 && bit < sh.bittable_size);
     OPENSSL_assert(TESTBIT(table, bit));
     CLEARBIT(table, bit);
@@ -280,11 +327,11 @@ static void sh_clearbit(char *ptr, int list, unsigned char *table)
 
 static void sh_setbit(char *ptr, int list, unsigned char *table)
 {
-    int bit;
+    size_t bit;
 
     OPENSSL_assert(list >= 0 && list < sh.freelist_size);
     OPENSSL_assert(((ptr - sh.arena) & ((sh.arena_size >> list) - 1)) == 0);
-    bit = (1 << list) + ((ptr - sh.arena) / (sh.arena_size >> list));
+    bit = (ONE << list) + ((ptr - sh.arena) / (sh.arena_size >> list));
     OPENSSL_assert(bit > 0 && bit < sh.bittable_size);
     OPENSSL_assert(!TESTBIT(table, bit));
     SETBIT(table, bit);
@@ -328,11 +375,12 @@ static void sh_remove_from_list(char *ptr)
 
 static int sh_init(size_t size, int minsize)
 {
-    int i, ret;
+    int ret;
+    size_t i;
     size_t pgsize;
     size_t aligned;
 
-    memset(&sh, 0, sizeof sh);
+    memset(&sh, 0, sizeof(sh));
 
     /* make sure size and minsize are powers of 2 */
     OPENSSL_assert(size > 0);
@@ -344,15 +392,22 @@ static int sh_init(size_t size, int minsize)
     if (minsize <= 0 || (minsize & (minsize - 1)) != 0)
         goto err;
 
+    while (minsize < (int)sizeof(SH_LIST))
+        minsize *= 2;
+
     sh.arena_size = size;
     sh.minsize = minsize;
     sh.bittable_size = (sh.arena_size / sh.minsize) * 2;
 
+    /* Prevent allocations of size 0 later on */
+    if (sh.bittable_size >> 3 == 0)
+        goto err;
+
     sh.freelist_size = -1;
     for (i = sh.bittable_size; i; i >>= 1)
         sh.freelist_size++;
 
-    sh.freelist = OPENSSL_zalloc(sh.freelist_size * sizeof (char *));
+    sh.freelist = OPENSSL_zalloc(sh.freelist_size * sizeof(char *));
     OPENSSL_assert(sh.freelist != NULL);
     if (sh.freelist == NULL)
         goto err;
@@ -399,7 +454,6 @@ static int sh_init(size_t size, int minsize)
             close(fd);
         }
     }
-    OPENSSL_assert(sh.map_result != MAP_FAILED);
     if (sh.map_result == MAP_FAILED)
         goto err;
     sh.arena = (char *)(sh.map_result + pgsize);
@@ -418,8 +472,19 @@ static int sh_init(size_t size, int minsize)
     if (mprotect(sh.map_result + aligned, pgsize, PROT_NONE) < 0)
         ret = 2;
 
+#if defined(OPENSSL_SYS_LINUX) && defined(MLOCK_ONFAULT) && defined(SYS_mlock2)
+    if (syscall(SYS_mlock2, sh.arena, sh.arena_size, MLOCK_ONFAULT) < 0) {
+        if (errno == ENOSYS) {
+            if (mlock(sh.arena, sh.arena_size) < 0)
+                ret = 2;
+        } else {
+            ret = 2;
+        }
+    }
+#else
     if (mlock(sh.arena, sh.arena_size) < 0)
         ret = 2;
+#endif
 #ifdef MADV_DONTDUMP
     if (madvise(sh.arena, sh.arena_size, MADV_DONTDUMP) < 0)
         ret = 2;
@@ -432,14 +497,14 @@ static int sh_init(size_t size, int minsize)
     return 0;
 }
 
-static void sh_done()
+static void sh_done(void)
 {
     OPENSSL_free(sh.freelist);
     OPENSSL_free(sh.bittable);
     OPENSSL_free(sh.bitmalloc);
     if (sh.map_result != NULL && sh.map_size)
         munmap(sh.map_result, sh.map_size);
-    memset(&sh, 0, sizeof sh);
+    memset(&sh, 0, sizeof(sh));
 }
 
 static int sh_allocated(const char *ptr)
@@ -449,24 +514,27 @@ static int sh_allocated(const char *ptr)
 
 static char *sh_find_my_buddy(char *ptr, int list)
 {
-    int bit;
+    size_t bit;
     char *chunk = NULL;
 
-    bit = (1 << list) + (ptr - sh.arena) / (sh.arena_size >> list);
+    bit = (ONE << list) + (ptr - sh.arena) / (sh.arena_size >> list);
     bit ^= 1;
 
     if (TESTBIT(sh.bittable, bit) && !TESTBIT(sh.bitmalloc, bit))
-        chunk = sh.arena + ((bit & ((1 << list) - 1)) * (sh.arena_size >> list));
+        chunk = sh.arena + ((bit & ((ONE << list) - 1)) * (sh.arena_size >> list));
 
     return chunk;
 }
 
-static char *sh_malloc(size_t size)
+static void *sh_malloc(size_t size)
 {
-    int list, slist;
+    ossl_ssize_t list, slist;
     size_t i;
     char *chunk;
 
+    if (size > sh.arena_size)
+        return NULL;
+
     list = sh.freelist_size - 1;
     for (i = sh.minsize; i < size; i <<= 1)
         list--;
@@ -517,13 +585,16 @@ static char *sh_malloc(size_t size)
 
     OPENSSL_assert(WITHIN_ARENA(chunk));
 
+    /* zero the free list header as a precaution against information leakage */
+    memset(chunk, 0, sizeof(SH_LIST));
+
     return chunk;
 }
 
-static void sh_free(char *ptr)
+static void sh_free(void *ptr)
 {
-    int list;
-    char *buddy;
+    size_t list;
+    void *buddy;
 
     if (ptr == NULL)
         return;
@@ -549,6 +620,8 @@ static void sh_free(char *ptr)
 
         list--;
 
+        /* Zero the higher addressed block's free list pointers */
+        memset(ptr > buddy ? ptr : buddy, 0, sizeof(SH_LIST));
         if (ptr > buddy)
             ptr = buddy;
 
@@ -559,7 +632,7 @@ static void sh_free(char *ptr)
     }
 }
 
-static int sh_actual_size(char *ptr)
+static size_t sh_actual_size(char *ptr)
 {
     int list;
 
@@ -568,6 +641,6 @@ static int sh_actual_size(char *ptr)
         return 0;
     list = sh_getlist(ptr);
     OPENSSL_assert(sh_testbit(ptr, list, sh.bittable));
-    return sh.arena_size / (1 << list);
+    return sh.arena_size / (ONE << list);
 }
-#endif /* IMPLEMENTED */
+#endif /* OPENSSL_SECURE_MEMORY */