Remove uses of `strcpy`, `strcat`, and `sprintf`, and handle NULL in some functions.

This change reimplements some OpenSSL changes based only on the
description of the work in base.h.

Change-Id: I1a8b3d2774216c43ab446aa56b31cbb40d58b29d
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/74847
Reviewed-by: David Benjamin <davidben@google.com>
Reviewed-by: Bob Beck <bbe@google.com>
diff --git a/crypto/asn1/a_gentm.cc b/crypto/asn1/a_gentm.cc
index e7ec9f7..ae4086f 100644
--- a/crypto/asn1/a_gentm.cc
+++ b/crypto/asn1/a_gentm.cc
@@ -58,7 +58,8 @@
 }
 
 ASN1_GENERALIZEDTIME *ASN1_GENERALIZEDTIME_adj(ASN1_GENERALIZEDTIME *s,
-                                               int64_t posix_time, int offset_day,
+                                               int64_t posix_time,
+                                               int offset_day,
                                                long offset_sec) {
   struct tm data;
   if (!OPENSSL_posix_to_tm(posix_time, &data)) {
@@ -77,12 +78,11 @@
   }
 
   char buf[16];
-  int ret = sprintf(buf, "%04d%02d%02d%02d%02d%02dZ", data.tm_year + 1900,
-                    data.tm_mon + 1, data.tm_mday, data.tm_hour, data.tm_min,
-                    data.tm_sec);
-  if (ret != (int)(sizeof(buf) - 1)) {
-    abort();  // |sprintf| should write exactly the expected number of bytes.
-  }
+  int ret = snprintf(buf, sizeof(buf), "%04d%02d%02d%02d%02d%02dZ",
+                     data.tm_year + 1900, data.tm_mon + 1, data.tm_mday,
+                     data.tm_hour, data.tm_min, data.tm_sec);
+  // |snprintf| must write exactly 15 bytes (plus the NUL) to the buffer.
+  BSSL_CHECK(ret == static_cast<int>(sizeof(buf) - 1));
 
   int free_s = 0;
   if (s == NULL) {
diff --git a/crypto/asn1/a_time.cc b/crypto/asn1/a_time.cc
index 6aa1de9..6ab5d93 100644
--- a/crypto/asn1/a_time.cc
+++ b/crypto/asn1/a_time.cc
@@ -69,15 +69,13 @@
 }
 
 // Convert an ASN1_TIME structure to GeneralizedTime
-ASN1_GENERALIZEDTIME *ASN1_TIME_to_generalizedtime(const ASN1_TIME *t,
+ASN1_GENERALIZEDTIME *ASN1_TIME_to_generalizedtime(const ASN1_TIME *in,
                                                    ASN1_GENERALIZEDTIME **out) {
-  ASN1_GENERALIZEDTIME *ret = NULL;
-  char *str;
-
-  if (!ASN1_TIME_check(t)) {
+  if (!ASN1_TIME_check(in)) {
     return NULL;
   }
 
+  ASN1_GENERALIZEDTIME *ret = NULL;
   if (!out || !*out) {
     if (!(ret = ASN1_GENERALIZEDTIME_new())) {
       goto err;
@@ -87,26 +85,30 @@
   }
 
   // If already GeneralizedTime just copy across
-  if (t->type == V_ASN1_GENERALIZEDTIME) {
-    if (!ASN1_STRING_set(ret, t->data, t->length)) {
+  if (in->type == V_ASN1_GENERALIZEDTIME) {
+    if (!ASN1_STRING_set(ret, in->data, in->length)) {
       goto err;
     }
     goto done;
   }
 
-  // grow the string
-  if (!ASN1_STRING_set(ret, NULL, t->length + 2)) {
+  // Grow the string to accomodate the two-digit century.
+  if (!ASN1_STRING_set(ret, NULL, in->length + 2)) {
     goto err;
   }
-  str = (char *)ret->data;
-  // Work out the century and prepend
-  if (t->data[0] >= '5') {
-    strcpy(str, "19");
-  } else {
-    strcpy(str, "20");
-  }
 
-  strcat(str, (const char *)t->data);
+  {
+    char *const out_str = (char *)ret->data;
+    // |ASN1_STRING_set| also allocates an additional byte for a trailing NUL.
+    const size_t out_str_capacity = in->length + 2 + 1;
+    // Work out the century and prepend
+    if (in->data[0] >= '5') {
+      OPENSSL_strlcpy(out_str, "19", out_str_capacity);
+    } else {
+      OPENSSL_strlcpy(out_str, "20", out_str_capacity);
+    }
+    OPENSSL_strlcat(out_str, (const char *)in->data, out_str_capacity);
+  }
 
 done:
   if (out != NULL && *out == NULL) {
@@ -128,7 +130,7 @@
 
 int ASN1_TIME_set_string_X509(ASN1_TIME *s, const char *str) {
   CBS cbs;
-  CBS_init(&cbs, (const uint8_t*)str, strlen(str));
+  CBS_init(&cbs, (const uint8_t *)str, strlen(str));
   int type;
   struct tm tm;
   if (CBS_parse_utc_time(&cbs, /*out_tm=*/NULL,
diff --git a/crypto/asn1/a_utctm.cc b/crypto/asn1/a_utctm.cc
index c25c39f..993dd5d 100644
--- a/crypto/asn1/a_utctm.cc
+++ b/crypto/asn1/a_utctm.cc
@@ -60,8 +60,8 @@
   return ASN1_UTCTIME_adj(s, posix_time, 0, 0);
 }
 
-ASN1_UTCTIME *ASN1_UTCTIME_adj(ASN1_UTCTIME *s, int64_t posix_time, int offset_day,
-                               long offset_sec) {
+ASN1_UTCTIME *ASN1_UTCTIME_adj(ASN1_UTCTIME *s, int64_t posix_time,
+                               int offset_day, long offset_sec) {
   struct tm data;
   if (!OPENSSL_posix_to_tm(posix_time, &data)) {
     return NULL;
@@ -78,12 +78,11 @@
   }
 
   char buf[14];
-  int ret = sprintf(buf, "%02d%02d%02d%02d%02d%02dZ", data.tm_year % 100,
-                    data.tm_mon + 1, data.tm_mday, data.tm_hour, data.tm_min,
-                    data.tm_sec);
-  if (ret != (int)(sizeof(buf) - 1)) {
-    abort();  // |sprintf| should write exactly the expected number of bytes.
-  }
+  int ret = snprintf(buf, sizeof(buf), "%02d%02d%02d%02d%02d%02dZ",
+                     data.tm_year % 100, data.tm_mon + 1, data.tm_mday,
+                     data.tm_hour, data.tm_min, data.tm_sec);
+  // |snprintf| must write exactly 15 bytes (plus the NUL) to the buffer.
+  BSSL_CHECK(ret == static_cast<int>(sizeof(buf) - 1));
 
   int free_s = 0;
   if (s == NULL) {
diff --git a/crypto/bio/connect.cc b/crypto/bio/connect.cc
index c97f320..9056fff 100644
--- a/crypto/bio/connect.cc
+++ b/crypto/bio/connect.cc
@@ -260,6 +260,9 @@
 }
 
 static void BIO_CONNECT_free(BIO_CONNECT *c) {
+  if (c == nullptr) {
+    return;
+  }
   OPENSSL_free(c->param_hostname);
   OPENSSL_free(c->param_port);
   OPENSSL_free(c);
diff --git a/crypto/buf/buf.cc b/crypto/buf/buf.cc
index 0010260..8c00082 100644
--- a/crypto/buf/buf.cc
+++ b/crypto/buf/buf.cc
@@ -22,6 +22,9 @@
 }
 
 void BUF_MEM_free(BUF_MEM *buf) {
+  if (buf == nullptr) {
+    return;
+  }
   OPENSSL_free(buf->data);
   OPENSSL_free(buf);
 }
diff --git a/crypto/conf/conf.cc b/crypto/conf/conf.cc
index dfb690a..9b28697 100644
--- a/crypto/conf/conf.cc
+++ b/crypto/conf/conf.cc
@@ -370,7 +370,6 @@
   char *s, *p, *end;
   int again;
   long eline = 0;
-  char btmp[DECIMAL_SIZE(eline) + 1];
   CONF_VALUE *v = NULL;
   CONF_SECTION *sv = NULL;
   char *section = NULL, *buf;
@@ -551,8 +550,7 @@
   if (out_error_line != NULL) {
     *out_error_line = eline;
   }
-  sprintf(btmp, "%ld", eline);
-  ERR_add_error_data(2, "line ", btmp);
+  ERR_add_error_dataf("line %ld", eline);
   value_free(v);
   return 0;
 }
diff --git a/crypto/fipsmodule/bn/ctx.cc.inc b/crypto/fipsmodule/bn/ctx.cc.inc
index 7d012bb..8ddcf10 100644
--- a/crypto/fipsmodule/bn/ctx.cc.inc
+++ b/crypto/fipsmodule/bn/ctx.cc.inc
@@ -79,6 +79,9 @@
   // All |BN_CTX_start| calls must be matched with |BN_CTX_end|, otherwise the
   // function may use more memory than expected, potentially without bound if
   // done in a loop. Assert that all |BIGNUM|s have been released.
+  if (ctx == nullptr) {
+    return;
+  }
   assert(ctx->used == 0 || ctx->error);
   sk_BIGNUM_pop_free(ctx->bignums, BN_free);
   BN_STACK_cleanup(&ctx->stack);
diff --git a/crypto/fipsmodule/bn/exponentiation.cc.inc b/crypto/fipsmodule/bn/exponentiation.cc.inc
index df19716..f193e55 100644
--- a/crypto/fipsmodule/bn/exponentiation.cc.inc
+++ b/crypto/fipsmodule/bn/exponentiation.cc.inc
@@ -136,6 +136,9 @@
 }
 
 static void BN_RECP_CTX_free(BN_RECP_CTX *recp) {
+  if (recp == nullptr) {
+    return;
+  }
   BN_free(&recp->N);
   BN_free(&recp->Nr);
 }
diff --git a/crypto/fipsmodule/bn/montgomery.cc.inc b/crypto/fipsmodule/bn/montgomery.cc.inc
index 22927ee..9cf42f1 100644
--- a/crypto/fipsmodule/bn/montgomery.cc.inc
+++ b/crypto/fipsmodule/bn/montgomery.cc.inc
@@ -45,6 +45,9 @@
 }
 
 void BN_MONT_CTX_free(BN_MONT_CTX *mont) {
+  if (mont == nullptr) {
+    return;
+  }
   bn_mont_ctx_cleanup(mont);
   OPENSSL_free(mont);
 }
diff --git a/crypto/fipsmodule/rsa/blinding.cc.inc b/crypto/fipsmodule/rsa/blinding.cc.inc
index 628f1ce..12ae91e 100644
--- a/crypto/fipsmodule/rsa/blinding.cc.inc
+++ b/crypto/fipsmodule/rsa/blinding.cc.inc
@@ -58,6 +58,9 @@
 }
 
 void BN_BLINDING_free(BN_BLINDING *r) {
+  if (r == nullptr) {
+    return;
+  }
   BN_free(r->A);
   BN_free(r->Ai);
   OPENSSL_free(r);
diff --git a/crypto/pem/pem_lib.cc b/crypto/pem/pem_lib.cc
index 1c3e1ef..61ab551 100644
--- a/crypto/pem/pem_lib.cc
+++ b/crypto/pem/pem_lib.cc
@@ -48,9 +48,9 @@
     str = "BAD-TYPE";
   }
 
-  strcat(buf, "Proc-Type: 4,");
-  strcat(buf, str);
-  strcat(buf, "\n");
+  OPENSSL_strlcat(buf, "Proc-Type: 4,", PEM_BUFSIZE);
+  OPENSSL_strlcat(buf, str, PEM_BUFSIZE);
+  OPENSSL_strlcat(buf, "\n", PEM_BUFSIZE);
 }
 
 // PEM_dek_info appends a DEK-Info header to |buf|, with an algorithm of |type|
@@ -59,16 +59,22 @@
                          char *str) {
   static const unsigned char map[17] = "0123456789ABCDEF";
 
-  strcat(buf, "DEK-Info: ");
-  strcat(buf, type);
-  strcat(buf, ",");
-  size_t buf_len = strlen(buf);
-  for (size_t i = 0; i < len; i++) {
-    buf[buf_len + i * 2] = map[(str[i] >> 4) & 0x0f];
-    buf[buf_len + i * 2 + 1] = map[(str[i]) & 0x0f];
+  OPENSSL_strlcat(buf, "DEK-Info: ", PEM_BUFSIZE);
+  OPENSSL_strlcat(buf, type, PEM_BUFSIZE);
+  OPENSSL_strlcat(buf, ",", PEM_BUFSIZE);
+
+  const size_t used = strlen(buf);
+  const size_t available = PEM_BUFSIZE - used;
+  if (len * 2 < len || len * 2 + 2 < len || available < len * 2 + 2) {
+    return;
   }
-  buf[buf_len + len * 2] = '\n';
-  buf[buf_len + len * 2 + 1] = '\0';
+
+  for (size_t i = 0; i < len; i++) {
+    buf[used + i * 2] = map[(str[i] >> 4) & 0x0f];
+    buf[used + i * 2 + 1] = map[(str[i]) & 0x0f];
+  }
+  buf[used + len * 2] = '\n';
+  buf[used + len * 2 + 1] = '\0';
 }
 
 void *PEM_ASN1_read(d2i_of_void *d2i, const char *name, FILE *fp, void **x,
diff --git a/crypto/x509/by_dir.cc b/crypto/x509/by_dir.cc
index f7abf21..7f87814 100644
--- a/crypto/x509/by_dir.cc
+++ b/crypto/x509/by_dir.cc
@@ -199,7 +199,7 @@
   } data;
   int ok = 0;
   size_t i;
-  int j, k;
+  int k;
   uint32_t h;
   uint32_t hash_array[2];
   int hash_index;
@@ -242,8 +242,10 @@
       size_t idx;
       BY_DIR_HASH htmp, *hent;
       ent = sk_BY_DIR_ENTRY_value(ctx->dirs, i);
-      j = strlen(ent->dir) + 1 + 8 + 6 + 1 + 1;
-      if (!BUF_MEM_grow(b, j)) {
+      if (!BUF_MEM_grow(b, strlen(ent->dir) + /* foreslash */ 1 +
+                               /* h, in hex */ 8 + /* period */ 1 +
+                               /* optional `postfix` */ 1 +
+                               /* k */ DECIMAL_SIZE(k) + /* NUL */ 1)) {
         goto finish;
       }
       if (type == X509_LU_CRL && ent->hashes) {
@@ -262,7 +264,8 @@
         hent = NULL;
       }
       for (;;) {
-        sprintf(b->data, "%s/%08" PRIx32 ".%s%d", ent->dir, h, postfix, k);
+        snprintf(b->data, b->max, "%s/%08" PRIx32 ".%s%d", ent->dir, h, postfix,
+                 k);
         if (type == X509_LU_X509) {
           if ((X509_load_cert_file(xl, b->data, ent->dir_type)) == 0) {
             // Don't expose the lower level error, All of these boil
diff --git a/crypto/x509/v3_alt.cc b/crypto/x509/v3_alt.cc
index adc8f6d..7b9fb0a 100644
--- a/crypto/x509/v3_alt.cc
+++ b/crypto/x509/v3_alt.cc
@@ -126,12 +126,12 @@
     case GEN_IPADD:
       p = gen->d.ip->data;
       if (gen->d.ip->length == 4) {
-        sprintf(oline, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);
+        snprintf(oline, sizeof(oline), "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);
       } else if (gen->d.ip->length == 16) {
         oline[0] = 0;
         for (i = 0; i < 8; i++) {
           uint16_t v = ((uint16_t)p[0] << 8) | p[1];
-          sprintf(htmp, "%X", v);
+          snprintf(htmp, sizeof(htmp), "%X", v);
           p += 2;
           OPENSSL_strlcat(oline, htmp, sizeof(oline));
           if (i != 7) {
diff --git a/crypto/x509/v3_info.cc b/crypto/x509/v3_info.cc
index 59a854b..ca52d01 100644
--- a/crypto/x509/v3_info.cc
+++ b/crypto/x509/v3_info.cc
@@ -80,8 +80,8 @@
   const AUTHORITY_INFO_ACCESS *ainfo =
       reinterpret_cast<const AUTHORITY_INFO_ACCESS *>(ext);
   ACCESS_DESCRIPTION *desc;
-  int nlen;
-  char objtmp[80], *ntmp;
+  int name_len;
+  char objtmp[80], *name;
   CONF_VALUE *vtmp;
   STACK_OF(CONF_VALUE) *tret = ret;
 
@@ -96,16 +96,17 @@
     tret = tmp;
     vtmp = sk_CONF_VALUE_value(tret, i);
     i2t_ASN1_OBJECT(objtmp, sizeof objtmp, desc->method);
-    nlen = strlen(objtmp) + 3 + strlen(vtmp->name) + 1;
-    ntmp = reinterpret_cast<char *>(OPENSSL_malloc(nlen));
-    if (ntmp == NULL) {
+
+    name_len = strlen(objtmp) + 3 + strlen(vtmp->name) + 1;
+    name = reinterpret_cast<char *>(OPENSSL_malloc(name_len));
+    if (name == NULL) {
       goto err;
     }
-    strcpy(ntmp, objtmp);
-    strcat(ntmp, " - ");
-    strcat(ntmp, vtmp->name);
+    OPENSSL_strlcpy(name, objtmp, name_len);
+    OPENSSL_strlcat(name, " - ", name_len);
+    OPENSSL_strlcat(name, vtmp->name, name_len);
     OPENSSL_free(vtmp->name);
-    vtmp->name = ntmp;
+    vtmp->name = name;
   }
   if (ret == NULL && tret == NULL) {
     return sk_CONF_VALUE_new_null();
diff --git a/crypto/x509/x509_lu.cc b/crypto/x509/x509_lu.cc
index 373015b..4e2df3b 100644
--- a/crypto/x509/x509_lu.cc
+++ b/crypto/x509/x509_lu.cc
@@ -130,7 +130,7 @@
 }
 
 void X509_STORE_free(X509_STORE *vfy) {
-  if (!CRYPTO_refcount_dec_and_test_zero(&vfy->references)) {
+  if (vfy == nullptr || !CRYPTO_refcount_dec_and_test_zero(&vfy->references)) {
     return;
   }
 
diff --git a/include/openssl/base.h b/include/openssl/base.h
index 21afad8..8f16fb7 100644
--- a/include/openssl/base.h
+++ b/include/openssl/base.h
@@ -13,25 +13,6 @@
 
 // This file should be the first included by all BoringSSL headers.
 
-// Remove this after importing the following to remove all instances of
-// |sprintf|, |strcat|, and |strcpy| from the codebase.
-// * https://github.com/openssl/openssl/commit/86ba26c80a49aee3c588d286d91eb3843529f7e2
-// * https://github.com/openssl/openssl/commit/60eba30f60de55e3c782469fa555eede82606099
-// * https://github.com/openssl/openssl/commit/a2371fa93365cc0bc0e46b9d65f3a47a074b1c30
-//
-// Also after importing
-// https://github.com/openssl/openssl/commit/e6e9170d6e28038768895e1af18e3aad8093bf4b
-// to make the following functions accept a NULL parameter:
-// * BIO_CONNECT_free
-// * BUF_MEM_free
-// * BN_CTX_free
-// * BN_RECP_CTX_free
-// * BN_MONT_CTX_free
-// * BN_BLINDING_free
-// * X509_STORE_free
-// * SSL_SESSION_free
-#error "Do not build BoringSSL at this revision"
-
 #include <stddef.h>
 #include <stdint.h>
 #include <stdlib.h>
@@ -421,7 +402,7 @@
 #define BORINGSSL_NO_CXX
 #endif
 
-}       // extern C++
+}  // extern C++
 #endif  // !BORINGSSL_NO_CXX
 
 #if defined(BORINGSSL_NO_CXX)
diff --git a/ssl/ssl_session.cc b/ssl/ssl_session.cc
index cf57365..52ce2d1 100644
--- a/ssl/ssl_session.cc
+++ b/ssl/ssl_session.cc
@@ -830,6 +830,9 @@
 }
 
 void SSL_SESSION_free(SSL_SESSION *session) {
+  if (session == nullptr) {
+    return;
+  }
   session->DecRefInternal();
 }