Zero memory in |OPENSSL_free|.
Allocations by |OPENSSL_malloc| are prefixed with their length.
|OPENSSL_free| zeros the allocation before calling free(), eliminating
the need for a separate call to |OPENSSL_cleanse| for sensitive data.
This change will be followed up by the cleanup in
https://boringssl-review.googlesource.com/c/boringssl/+/19824.
Change-Id: Ie272f07e9248d7d78af9aea81dacec0fdb7484c4
Reviewed-on: https://boringssl-review.googlesource.com/19544
Reviewed-by: Martin Kreichgauer <martinkr@google.com>
Commit-Queue: Martin Kreichgauer <martinkr@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
diff --git a/crypto/cipher_extra/e_aesgcmsiv.c b/crypto/cipher_extra/e_aesgcmsiv.c
index 8c6589d..e4c5bb3 100644
--- a/crypto/cipher_extra/e_aesgcmsiv.c
+++ b/crypto/cipher_extra/e_aesgcmsiv.c
@@ -64,8 +64,10 @@
return 0;
}
+ // The asm implementation expects a 16-byte-aligned address here, so we use
+ // |malloc| rather than |OPENSSL_malloc|, which would add a length prefix.
struct aead_aes_gcm_siv_asm_ctx *gcm_siv_ctx =
- OPENSSL_malloc(sizeof(struct aead_aes_gcm_siv_asm_ctx));
+ malloc(sizeof(struct aead_aes_gcm_siv_asm_ctx));
if (gcm_siv_ctx == NULL) {
return 0;
}
@@ -87,9 +89,7 @@
}
static void aead_aes_gcm_siv_asm_cleanup(EVP_AEAD_CTX *ctx) {
- struct aead_aes_gcm_siv_asm_ctx *gcm_siv_asm_ctx = ctx->aead_state;
- OPENSSL_cleanse(gcm_siv_asm_ctx, sizeof(struct aead_aes_gcm_siv_asm_ctx));
- OPENSSL_free(gcm_siv_asm_ctx);
+ free(ctx->aead_state); // allocated with native |malloc|
}
// aesgcmsiv_polyval_horner updates the POLYVAL value in |in_out_poly| to
diff --git a/crypto/mem.c b/crypto/mem.c
index 576ab7f..1c19122 100644
--- a/crypto/mem.c
+++ b/crypto/mem.c
@@ -76,32 +76,66 @@
#include "internal.h"
-void *OPENSSL_realloc_clean(void *ptr, size_t old_size, size_t new_size) {
+#define OPENSSL_MALLOC_PREFIX 8
+
+
+void *OPENSSL_malloc(size_t size) {
+ void *ptr = malloc(size + OPENSSL_MALLOC_PREFIX);
if (ptr == NULL) {
+ return NULL;
+ }
+
+ *(size_t *)ptr = size;
+
+ return ((uint8_t *)ptr) + OPENSSL_MALLOC_PREFIX;
+}
+
+void OPENSSL_free(void *orig_ptr) {
+ if (orig_ptr == NULL) {
+ return;
+ }
+
+ void *ptr = ((uint8_t *)orig_ptr) - OPENSSL_MALLOC_PREFIX;
+
+ size_t size = *(size_t *)ptr;
+ OPENSSL_cleanse(ptr, size + OPENSSL_MALLOC_PREFIX);
+ free(ptr);
+}
+
+void *OPENSSL_realloc(void *orig_ptr, size_t new_size) {
+ if (orig_ptr == NULL) {
return OPENSSL_malloc(new_size);
}
- if (new_size == 0) {
- return NULL;
- }
-
- // We don't support shrinking the buffer. Note the memcpy that copies
- // |old_size| bytes to the new buffer, below.
- if (new_size < old_size) {
- return NULL;
- }
+ void *ptr = ((uint8_t *)orig_ptr) - OPENSSL_MALLOC_PREFIX;
+ size_t old_size = *(size_t *)ptr;
void *ret = OPENSSL_malloc(new_size);
if (ret == NULL) {
return NULL;
}
- OPENSSL_memcpy(ret, ptr, old_size);
- OPENSSL_cleanse(ptr, old_size);
- OPENSSL_free(ptr);
+ size_t to_copy = new_size;
+ if (old_size < to_copy) {
+ to_copy = old_size;
+ }
+
+ memcpy(ret, orig_ptr, to_copy);
+ OPENSSL_free(orig_ptr);
+
return ret;
}
+void *OPENSSL_realloc_clean(void *orig_ptr, size_t old_size, size_t new_size) {
+ void *ptr = ((uint8_t *)orig_ptr) - OPENSSL_MALLOC_PREFIX;
+ size_t actual_size = *(size_t *)ptr;
+ if (actual_size != old_size) {
+ return NULL;
+ }
+
+ return OPENSSL_realloc(orig_ptr, new_size);
+}
+
void OPENSSL_cleanse(void *ptr, size_t len) {
#if defined(OPENSSL_WINDOWS)
SecureZeroMemory(ptr, len);
@@ -155,15 +189,15 @@
return len;
}
-#if defined(OPENSSL_WINDOWS)
-
-char *OPENSSL_strdup(const char *s) { return _strdup(s); }
-
-#else
-
-char *OPENSSL_strdup(const char *s) { return strdup(s); }
-
-#endif
+char *OPENSSL_strdup(const char *s) {
+ const size_t len = strlen(s) + 1;
+ char *ret = OPENSSL_malloc(len);
+ if (ret == NULL) {
+ return NULL;
+ }
+ OPENSSL_memcpy(ret, s, len);
+ return ret;
+}
int OPENSSL_tolower(int c) {
if (c >= 'A' && c <= 'Z') {
diff --git a/include/openssl/mem.h b/include/openssl/mem.h
index 92cbb0d..6c21512 100644
--- a/include/openssl/mem.h
+++ b/include/openssl/mem.h
@@ -69,20 +69,28 @@
// Memory and string functions, see also buf.h.
//
-// OpenSSL has, historically, had a complex set of malloc debugging options.
-// However, that was written in a time before Valgrind and ASAN. Since we now
-// have those tools, the OpenSSL allocation functions are simply macros around
-// the standard memory functions.
+// BoringSSL has its own set of allocation functions, which keep track of
+// allocation lengths and zero them out before freeing. All memory returned by
+// BoringSSL API calls must therefore generally be freed using |OPENSSL_free|
+// unless stated otherwise.
-#define OPENSSL_malloc malloc
-#define OPENSSL_realloc realloc
-#define OPENSSL_free free
+// OPENSSL_malloc acts like a regular |malloc|.
+OPENSSL_EXPORT void *OPENSSL_malloc(size_t size);
-// OPENSSL_realloc_clean acts like |realloc|, but clears the previous memory
-// buffer. Because this is implemented as a wrapper around |malloc|, it needs
-// to be given the size of the buffer pointed to by |ptr|.
-void *OPENSSL_realloc_clean(void *ptr, size_t old_size, size_t new_size);
+// OPENSSL_free does nothing if |ptr| is NULL. Otherwise it zeros out the
+// memory allocated at |ptr| and frees it.
+OPENSSL_EXPORT void OPENSSL_free(void *ptr);
+
+// OPENSSL_realloc returns a pointer to a buffer of |new_size| bytes that
+// contains the contents of |ptr|. Unlike |realloc|, a new buffer is always
+// allocated and the data at |ptr| is always wiped and freed.
+OPENSSL_EXPORT void *OPENSSL_realloc(void *ptr, size_t new_size);
+
+// OPENSSL_realloc_clean behaves exactly like |OPENSSL_realloc|.
+// TODO(martinkr): Remove.
+OPENSSL_EXPORT void *OPENSSL_realloc_clean(void *ptr, size_t old_size,
+ size_t new_size);
// OPENSSL_cleanse zeros out |len| bytes of memory at |ptr|. This is similar to
// |memset_s| from C11.
diff --git a/ssl/ssl_buffer.cc b/ssl/ssl_buffer.cc
index d74278e..b424138 100644
--- a/ssl/ssl_buffer.cc
+++ b/ssl/ssl_buffer.cc
@@ -50,7 +50,11 @@
}
// Add up to |SSL3_ALIGN_PAYLOAD| - 1 bytes of slack for alignment.
- uint8_t *new_buf = (uint8_t *)OPENSSL_malloc(cap + SSL3_ALIGN_PAYLOAD - 1);
+ //
+ // Since this buffer gets allocated quite frequently and doesn't contain any
+ // sensitive data, we allocate with malloc rather than |OPENSSL_malloc| and
+ // avoid zeroing on free.
+ uint8_t *new_buf = (uint8_t *)malloc(cap + SSL3_ALIGN_PAYLOAD - 1);
if (new_buf == NULL) {
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
return 0;
@@ -62,7 +66,7 @@
if (buf->buf != NULL) {
OPENSSL_memcpy(new_buf + new_offset, buf->buf + buf->offset, buf->len);
- OPENSSL_free(buf->buf);
+ free(buf->buf); // Allocated with malloc().
}
buf->buf = new_buf;
@@ -81,7 +85,7 @@
}
static void clear_buffer(SSL3_BUFFER *buf) {
- OPENSSL_free(buf->buf);
+ free(buf->buf); // Allocated with malloc().
OPENSSL_memset(buf, 0, sizeof(SSL3_BUFFER));
}
diff --git a/ssl/ssl_cipher.cc b/ssl/ssl_cipher.cc
index e7a81a3..78cf60d 100644
--- a/ssl/ssl_cipher.cc
+++ b/ssl/ssl_cipher.cc
@@ -1337,11 +1337,15 @@
goto err;
}
pref_list->ciphers = cipherstack;
- pref_list->in_group_flags = (uint8_t *)OPENSSL_malloc(num_in_group_flags);
- if (!pref_list->in_group_flags) {
- goto err;
+ pref_list->in_group_flags = NULL;
+ if (num_in_group_flags) {
+ pref_list->in_group_flags = (uint8_t *)OPENSSL_malloc(num_in_group_flags);
+ if (!pref_list->in_group_flags) {
+ goto err;
+ }
+ OPENSSL_memcpy(pref_list->in_group_flags, in_group_flags,
+ num_in_group_flags);
}
- OPENSSL_memcpy(pref_list->in_group_flags, in_group_flags, num_in_group_flags);
OPENSSL_free(in_group_flags);
in_group_flags = NULL;
if (*out_cipher_list != NULL) {