Add BUF_MEM_reserve.

BUF_MEM is actually a rather silly API for the SSL stack. There's separate
length and max fields, but init_buf effectively treats length as max and max as
nothing.

We possibly don't want to be using it long-term anyway (if nothing else, the
char*/uint8_t* thing is irritating), but in the meantime, it'll be easier to
separately fix up get_message's book-keeping and state tracking from where the
handshake gets its messages from.

Change-Id: I9e56ea008173991edc8312ec707505ead410a9ee
Reviewed-on: https://boringssl-review.googlesource.com/7947
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/crypto/buf/buf.c b/crypto/buf/buf.c
index b918f01..efe9952 100644
--- a/crypto/buf/buf.c
+++ b/crypto/buf/buf.c
@@ -88,34 +88,26 @@
   OPENSSL_free(buf);
 }
 
-static size_t buf_mem_grow(BUF_MEM *buf, size_t len, char clean) {
-  char *new_buf;
-  size_t n, alloc_size;
-
-  if (buf->length >= len) {
-    buf->length = len;
-    return len;
-  }
-  if (buf->max >= len) {
-    memset(&buf->data[buf->length], 0, len - buf->length);
-    buf->length = len;
-    return len;
+static int buf_mem_reserve(BUF_MEM *buf, size_t cap, int clean) {
+  if (buf->max >= cap) {
+    return 1;
   }
 
-  n = len + 3;
-  if (n < len) {
+  size_t n = cap + 3;
+  if (n < cap) {
     /* overflow */
     OPENSSL_PUT_ERROR(BUF, ERR_R_MALLOC_FAILURE);
     return 0;
   }
   n = n / 3;
-  alloc_size = n * 4;
+  size_t alloc_size = n * 4;
   if (alloc_size / 4 != n) {
     /* overflow */
     OPENSSL_PUT_ERROR(BUF, ERR_R_MALLOC_FAILURE);
     return 0;
   }
 
+  char *new_buf;
   if (buf->data == NULL) {
     new_buf = OPENSSL_malloc(alloc_size);
   } else {
@@ -128,14 +120,26 @@
 
   if (new_buf == NULL) {
     OPENSSL_PUT_ERROR(BUF, ERR_R_MALLOC_FAILURE);
-    len = 0;
-  } else {
-    buf->data = new_buf;
-    buf->max = alloc_size;
-    memset(&buf->data[buf->length], 0, len - buf->length);
-    buf->length = len;
+    return 0;
   }
 
+  buf->data = new_buf;
+  buf->max = alloc_size;
+  return 1;
+}
+
+int BUF_MEM_reserve(BUF_MEM *buf, size_t cap) {
+  return buf_mem_reserve(buf, cap, 0 /* don't clear old buffer contents. */);
+}
+
+static size_t buf_mem_grow(BUF_MEM *buf, size_t len, int clean) {
+  if (!buf_mem_reserve(buf, len, clean)) {
+    return 0;
+  }
+  if (buf->length < len) {
+    memset(&buf->data[buf->length], 0, len - buf->length);
+  }
+  buf->length = len;
   return len;
 }
 
diff --git a/include/openssl/buf.h b/include/openssl/buf.h
index f4e315c..8ae856b 100644
--- a/include/openssl/buf.h
+++ b/include/openssl/buf.h
@@ -80,6 +80,10 @@
 /* BUF_MEM_free frees |buf->data| if needed and then frees |buf| itself. */
 OPENSSL_EXPORT void BUF_MEM_free(BUF_MEM *buf);
 
+/* BUF_MEM_reserve ensures |buf| has capacity |cap| and allocates memory if
+ * needed. It returns one on success and zero on error. */
+OPENSSL_EXPORT int BUF_MEM_reserve(BUF_MEM *buf, size_t cap);
+
 /* BUF_MEM_grow ensures that |buf| has length |len| and allocates memory if
  * needed. If the length of |buf| increased, the new bytes are filled with
  * zeros. It returns the length of |buf|, or zero if there's an error. */