external/boringssl: Sync to 2d98d49cf712ca7dc6f4b23b9c5f5542385d8dbe.
am: f068def344

Change-Id: I1183f293beaf7bf7733a1fa61a0c7a144ecc2493
diff --git a/BORINGSSL_REVISION b/BORINGSSL_REVISION
index f5cb25e..37d1136 100644
--- a/BORINGSSL_REVISION
+++ b/BORINGSSL_REVISION
@@ -1 +1 @@
-5ede28c8a422801ace3ba5e466ba963005351145
+2d98d49cf712ca7dc6f4b23b9c5f5542385d8dbe
diff --git a/ios-aarch64/crypto/fipsmodule/sha1-armv8.S b/ios-aarch64/crypto/fipsmodule/sha1-armv8.S
index 61a5a80..17e1a56 100644
--- a/ios-aarch64/crypto/fipsmodule/sha1-armv8.S
+++ b/ios-aarch64/crypto/fipsmodule/sha1-armv8.S
@@ -1230,4 +1230,5 @@
 .align	2
 .align	2
 .comm	_OPENSSL_armcap_P,4,4
+.private_extern	_OPENSSL_armcap_P
 #endif  // !OPENSSL_NO_ASM
diff --git a/ios-aarch64/crypto/fipsmodule/sha256-armv8.S b/ios-aarch64/crypto/fipsmodule/sha256-armv8.S
index 08c8ea2..9076eeb 100644
--- a/ios-aarch64/crypto/fipsmodule/sha256-armv8.S
+++ b/ios-aarch64/crypto/fipsmodule/sha256-armv8.S
@@ -1208,5 +1208,6 @@
 #endif
 #ifndef	__KERNEL__
 .comm	_OPENSSL_armcap_P,4,4
+.private_extern	_OPENSSL_armcap_P
 #endif
 #endif  // !OPENSSL_NO_ASM
diff --git a/ios-aarch64/crypto/fipsmodule/sha512-armv8.S b/ios-aarch64/crypto/fipsmodule/sha512-armv8.S
index 0343672..d4fd317 100644
--- a/ios-aarch64/crypto/fipsmodule/sha512-armv8.S
+++ b/ios-aarch64/crypto/fipsmodule/sha512-armv8.S
@@ -1080,5 +1080,6 @@
 .align	2
 #ifndef	__KERNEL__
 .comm	_OPENSSL_armcap_P,4,4
+.private_extern	_OPENSSL_armcap_P
 #endif
 #endif  // !OPENSSL_NO_ASM
diff --git a/linux-aarch64/crypto/fipsmodule/sha1-armv8.S b/linux-aarch64/crypto/fipsmodule/sha1-armv8.S
index 5cd02b7..3b6cf6a 100644
--- a/linux-aarch64/crypto/fipsmodule/sha1-armv8.S
+++ b/linux-aarch64/crypto/fipsmodule/sha1-armv8.S
@@ -1231,5 +1231,6 @@
 .align	2
 .align	2
 .comm	OPENSSL_armcap_P,4,4
+.hidden	OPENSSL_armcap_P
 #endif
 #endif  // !OPENSSL_NO_ASM
diff --git a/linux-aarch64/crypto/fipsmodule/sha256-armv8.S b/linux-aarch64/crypto/fipsmodule/sha256-armv8.S
index aefcc88..8bb535c 100644
--- a/linux-aarch64/crypto/fipsmodule/sha256-armv8.S
+++ b/linux-aarch64/crypto/fipsmodule/sha256-armv8.S
@@ -1209,6 +1209,7 @@
 #endif
 #ifndef	__KERNEL__
 .comm	OPENSSL_armcap_P,4,4
+.hidden	OPENSSL_armcap_P
 #endif
 #endif
 #endif  // !OPENSSL_NO_ASM
diff --git a/linux-aarch64/crypto/fipsmodule/sha512-armv8.S b/linux-aarch64/crypto/fipsmodule/sha512-armv8.S
index 18f909a..ac9d5f0 100644
--- a/linux-aarch64/crypto/fipsmodule/sha512-armv8.S
+++ b/linux-aarch64/crypto/fipsmodule/sha512-armv8.S
@@ -1081,6 +1081,7 @@
 .align	2
 #ifndef	__KERNEL__
 .comm	OPENSSL_armcap_P,4,4
+.hidden	OPENSSL_armcap_P
 #endif
 #endif
 #endif  // !OPENSSL_NO_ASM
diff --git a/sources.bp b/sources.bp
index ee44f4e..15c6648 100644
--- a/sources.bp
+++ b/sources.bp
@@ -431,6 +431,7 @@
         "src/crypto/refcount_test.cc",
         "src/crypto/rsa_extra/rsa_test.cc",
         "src/crypto/self_test.cc",
+        "src/crypto/stack/stack_test.cc",
         "src/crypto/test/file_test_gtest.cc",
         "src/crypto/test/gtest_main.cc",
         "src/crypto/thread_test.cc",
diff --git a/src/BUILDING.md b/src/BUILDING.md
index ab9b71d..01f5480 100644
--- a/src/BUILDING.md
+++ b/src/BUILDING.md
@@ -128,7 +128,11 @@
 `/path/to/symbols.txt`.
 
 It is currently the caller's responsibility to create and maintain the list of
-symbols to be prefixed.
+symbols to be prefixed. Alternatively, `util/read_symbols.go` reads the list of
+exported symbols from a `.a` file, and can be used in a build script to generate
+the symbol list on the fly (by building without prefixing, using
+`read_symbols.go` to construct a symbol list, and then building again with
+prefixing).
 
 This mechanism is under development and may change over time. Please contact the
 BoringSSL maintainers if making use of it.
diff --git a/src/crypto/CMakeLists.txt b/src/crypto/CMakeLists.txt
index 78b835c..ee9626a 100644
--- a/src/crypto/CMakeLists.txt
+++ b/src/crypto/CMakeLists.txt
@@ -467,6 +467,7 @@
   refcount_test.cc
   rsa_extra/rsa_test.cc
   self_test.cc
+  stack/stack_test.cc
   test/file_test_gtest.cc
   thread_test.cc
   x509/x509_test.cc
diff --git a/src/crypto/asn1/a_d2i_fp.c b/src/crypto/asn1/a_d2i_fp.c
index 3da6df9..fd423e2 100644
--- a/src/crypto/asn1/a_d2i_fp.c
+++ b/src/crypto/asn1/a_d2i_fp.c
@@ -58,240 +58,36 @@
 
 #include <limits.h>
 
-#include <openssl/buf.h>
+#include <openssl/bio.h>
 #include <openssl/err.h>
 #include <openssl/mem.h>
 
-static int asn1_d2i_read_bio(BIO *in, BUF_MEM **pb);
-
-#ifndef NO_OLD_ASN1
-# ifndef OPENSSL_NO_FP_API
-
-void *ASN1_d2i_fp(void *(*xnew) (void), d2i_of_void *d2i, FILE *in, void **x)
-{
-    BIO *b;
-    void *ret;
-
-    if ((b = BIO_new(BIO_s_file())) == NULL) {
-        OPENSSL_PUT_ERROR(ASN1, ERR_R_BUF_LIB);
-        return (NULL);
-    }
-    BIO_set_fp(b, in, BIO_NOCLOSE);
-    ret = ASN1_d2i_bio(xnew, d2i, b, x);
-    BIO_free(b);
-    return (ret);
-}
-# endif
-
-void *ASN1_d2i_bio(void *(*xnew) (void), d2i_of_void *d2i, BIO *in, void **x)
-{
-    BUF_MEM *b = NULL;
-    const unsigned char *p;
-    void *ret = NULL;
-    int len;
-
-    len = asn1_d2i_read_bio(in, &b);
-    if (len < 0)
-        goto err;
-
-    p = (unsigned char *)b->data;
-    ret = d2i(x, &p, len);
- err:
-    if (b != NULL)
-        BUF_MEM_free(b);
-    return (ret);
-}
-
-#endif
 
 void *ASN1_item_d2i_bio(const ASN1_ITEM *it, BIO *in, void *x)
 {
-    BUF_MEM *b = NULL;
-    const unsigned char *p;
-    void *ret = NULL;
-    int len;
-
-    len = asn1_d2i_read_bio(in, &b);
-    if (len < 0)
-        goto err;
-
-    p = (const unsigned char *)b->data;
-    ret = ASN1_item_d2i(x, &p, len, it);
- err:
-    if (b != NULL)
-        BUF_MEM_free(b);
-    return (ret);
+    uint8_t *data;
+    size_t len;
+    // Historically, this function did not impose a limit in OpenSSL and is used
+    // to read CRLs, so we leave this without an external bound.
+    if (!BIO_read_asn1(in, &data, &len, INT_MAX)) {
+        return NULL;
+    }
+    const uint8_t *ptr = data;
+    void *ret = ASN1_item_d2i(x, &ptr, len, it);
+    OPENSSL_free(data);
+    return ret;
 }
 
 #ifndef OPENSSL_NO_FP_API
 void *ASN1_item_d2i_fp(const ASN1_ITEM *it, FILE *in, void *x)
 {
-    BIO *b;
-    char *ret;
-
-    if ((b = BIO_new(BIO_s_file())) == NULL) {
+    BIO *b = BIO_new_fp(in, BIO_NOCLOSE);
+    if (b == NULL) {
         OPENSSL_PUT_ERROR(ASN1, ERR_R_BUF_LIB);
-        return (NULL);
+        return NULL;
     }
-    BIO_set_fp(b, in, BIO_NOCLOSE);
-    ret = ASN1_item_d2i_bio(it, b, x);
+    void *ret = ASN1_item_d2i_bio(it, b, x);
     BIO_free(b);
-    return (ret);
+    return ret;
 }
 #endif
-
-typedef struct asn1_const_ctx_st
-    {
-    const unsigned char *p;/* work char pointer */
-    int eos;    /* end of sequence read for indefinite encoding */
-    int error;  /* error code to use when returning an error */
-    int inf;    /* constructed if 0x20, indefinite is 0x21 */
-    int tag;    /* tag from last 'get object' */
-    int xclass; /* class from last 'get object' */
-    long slen;  /* length of last 'get object' */
-    const unsigned char *max; /* largest value of p allowed */
-    const unsigned char *q;/* temporary variable */
-    const unsigned char **pp;/* variable */
-    int line;   /* used in error processing */
-    } ASN1_const_CTX;
-
-#define HEADER_SIZE   8
-#define ASN1_CHUNK_INITIAL_SIZE (16 * 1024)
-static int asn1_d2i_read_bio(BIO *in, BUF_MEM **pb)
-{
-    BUF_MEM *b;
-    unsigned char *p;
-    int i;
-    ASN1_const_CTX c;
-    size_t want = HEADER_SIZE;
-    int eos = 0;
-    size_t off = 0;
-    size_t len = 0;
-
-    b = BUF_MEM_new();
-    if (b == NULL) {
-        OPENSSL_PUT_ERROR(ASN1, ERR_R_MALLOC_FAILURE);
-        return -1;
-    }
-
-    ERR_clear_error();
-    for (;;) {
-        if (want >= (len - off)) {
-            want -= (len - off);
-
-            if (len + want < len || !BUF_MEM_grow_clean(b, len + want)) {
-                OPENSSL_PUT_ERROR(ASN1, ERR_R_MALLOC_FAILURE);
-                goto err;
-            }
-            i = BIO_read(in, &(b->data[len]), want);
-            if ((i < 0) && ((len - off) == 0)) {
-                OPENSSL_PUT_ERROR(ASN1, ASN1_R_NOT_ENOUGH_DATA);
-                goto err;
-            }
-            if (i > 0) {
-                if (len + i < len) {
-                    OPENSSL_PUT_ERROR(ASN1, ASN1_R_TOO_LONG);
-                    goto err;
-                }
-                len += i;
-            }
-        }
-        /* else data already loaded */
-
-        p = (unsigned char *)&(b->data[off]);
-        c.p = p;
-        c.inf = ASN1_get_object(&(c.p), &(c.slen), &(c.tag), &(c.xclass),
-                                len - off);
-        if (c.inf & 0x80) {
-            uint32_t e;
-
-            e = ERR_GET_REASON(ERR_peek_error());
-            if (e != ASN1_R_TOO_LONG)
-                goto err;
-            else
-                ERR_clear_error(); /* clear error */
-        }
-        i = c.p - p;            /* header length */
-        off += i;               /* end of data */
-
-        if (c.inf & 1) {
-            /* no data body so go round again */
-            eos++;
-            if (eos < 0) {
-                OPENSSL_PUT_ERROR(ASN1, ASN1_R_HEADER_TOO_LONG);
-                goto err;
-            }
-            want = HEADER_SIZE;
-        } else if (eos && (c.slen == 0) && (c.tag == V_ASN1_EOC)) {
-            /* eos value, so go back and read another header */
-            eos--;
-            if (eos <= 0)
-                break;
-            else
-                want = HEADER_SIZE;
-        } else {
-            /* suck in c.slen bytes of data */
-            want = c.slen;
-            if (want > (len - off)) {
-                size_t chunk_max = ASN1_CHUNK_INITIAL_SIZE;
-                want -= (len - off);
-                if (want > INT_MAX /* BIO_read takes an int length */  ||
-                    len + want < len) {
-                    OPENSSL_PUT_ERROR(ASN1, ASN1_R_TOO_LONG);
-                    goto err;
-                }
-                while (want > 0) {
-                    /*
-                     * Read content in chunks of increasing size
-                     * so we can return an error for EOF without
-                     * having to allocate the entire content length
-                     * in one go.
-                     */
-                    size_t chunk = want > chunk_max ? chunk_max : want;
-
-                    if (!BUF_MEM_grow_clean(b, len + chunk)) {
-                        OPENSSL_PUT_ERROR(ASN1, ERR_R_MALLOC_FAILURE);
-                        goto err;
-                    }
-                    want -= chunk;
-                    while (chunk > 0) {
-                        i = BIO_read(in, &(b->data[len]), chunk);
-                        if (i <= 0) {
-                            OPENSSL_PUT_ERROR(ASN1, ASN1_R_NOT_ENOUGH_DATA);
-                            goto err;
-                        }
-                        /*
-                         * This can't overflow because |len+want| didn't
-                         * overflow.
-                         */
-                        len += i;
-                        chunk -= i;
-                    }
-                    if (chunk_max < INT_MAX/2)
-                        chunk_max *= 2;
-                }
-            }
-            if (off + c.slen < off) {
-                OPENSSL_PUT_ERROR(ASN1, ASN1_R_TOO_LONG);
-                goto err;
-            }
-            off += c.slen;
-            if (eos <= 0) {
-                break;
-            } else
-                want = HEADER_SIZE;
-        }
-    }
-
-    if (off > INT_MAX) {
-        OPENSSL_PUT_ERROR(ASN1, ASN1_R_TOO_LONG);
-        goto err;
-    }
-
-    *pb = b;
-    return off;
- err:
-    if (b != NULL)
-        BUF_MEM_free(b);
-    return -1;
-}
diff --git a/src/crypto/asn1/a_dup.c b/src/crypto/asn1/a_dup.c
index 57394f5..9ede851 100644
--- a/src/crypto/asn1/a_dup.c
+++ b/src/crypto/asn1/a_dup.c
@@ -59,30 +59,6 @@
 #include <openssl/err.h>
 #include <openssl/mem.h>
 
-void *ASN1_dup(i2d_of_void *i2d, d2i_of_void *d2i, void *x)
-{
-    unsigned char *b, *p;
-    const unsigned char *p2;
-    int i;
-    char *ret;
-
-    if (x == NULL)
-        return (NULL);
-
-    i = i2d(x, NULL);
-    b = OPENSSL_malloc(i + 10);
-    if (b == NULL) {
-        OPENSSL_PUT_ERROR(ASN1, ERR_R_MALLOC_FAILURE);
-        return (NULL);
-    }
-    p = b;
-    i = i2d(x, &p);
-    p2 = b;
-    ret = d2i(NULL, &p2, i);
-    OPENSSL_free(b);
-    return (ret);
-}
-
 /*
  * ASN1_ITEM version of dup: this follows the model above except we don't
  * need to allocate the buffer. At some point this could be rewritten to
diff --git a/src/crypto/asn1/a_i2d_fp.c b/src/crypto/asn1/a_i2d_fp.c
index 7b76d0c..db0d812 100644
--- a/src/crypto/asn1/a_i2d_fp.c
+++ b/src/crypto/asn1/a_i2d_fp.c
@@ -56,95 +56,33 @@
 
 #include <openssl/asn1.h>
 
+#include <openssl/bio.h>
 #include <openssl/err.h>
 #include <openssl/mem.h>
 
-int ASN1_i2d_fp(i2d_of_void *i2d, FILE *out, void *x)
-{
-    BIO *b;
-    int ret;
-
-    if ((b = BIO_new(BIO_s_file())) == NULL) {
-        OPENSSL_PUT_ERROR(ASN1, ERR_R_BUF_LIB);
-        return (0);
-    }
-    BIO_set_fp(b, out, BIO_NOCLOSE);
-    ret = ASN1_i2d_bio(i2d, b, x);
-    BIO_free(b);
-    return (ret);
-}
-
-int ASN1_i2d_bio(i2d_of_void *i2d, BIO *out, void *x)
-{
-    char *b;
-    unsigned char *p;
-    int i, j = 0, n, ret = 1;
-
-    n = i2d(x, NULL);
-    if (n <= 0)
-        return 0;
-
-    b = (char *)OPENSSL_malloc(n);
-    if (b == NULL) {
-        OPENSSL_PUT_ERROR(ASN1, ERR_R_MALLOC_FAILURE);
-        return (0);
-    }
-
-    p = (unsigned char *)b;
-    i2d(x, &p);
-
-    for (;;) {
-        i = BIO_write(out, &(b[j]), n);
-        if (i == n)
-            break;
-        if (i <= 0) {
-            ret = 0;
-            break;
-        }
-        j += i;
-        n -= i;
-    }
-    OPENSSL_free(b);
-    return (ret);
-}
 
 int ASN1_item_i2d_fp(const ASN1_ITEM *it, FILE *out, void *x)
 {
-    BIO *b;
-    int ret;
-
-    if ((b = BIO_new(BIO_s_file())) == NULL) {
+    BIO *b = BIO_new_fp(out, BIO_NOCLOSE);
+    if (b == NULL) {
         OPENSSL_PUT_ERROR(ASN1, ERR_R_BUF_LIB);
-        return (0);
+        return 0;
     }
-    BIO_set_fp(b, out, BIO_NOCLOSE);
-    ret = ASN1_item_i2d_bio(it, b, x);
+    int ret = ASN1_item_i2d_bio(it, b, x);
     BIO_free(b);
-    return (ret);
+    return ret;
 }
 
 int ASN1_item_i2d_bio(const ASN1_ITEM *it, BIO *out, void *x)
 {
     unsigned char *b = NULL;
-    int i, j = 0, n, ret = 1;
-
-    n = ASN1_item_i2d(x, &b, it);
+    int n = ASN1_item_i2d(x, &b, it);
     if (b == NULL) {
         OPENSSL_PUT_ERROR(ASN1, ERR_R_MALLOC_FAILURE);
-        return (0);
+        return 0;
     }
 
-    for (;;) {
-        i = BIO_write(out, &(b[j]), n);
-        if (i == n)
-            break;
-        if (i <= 0) {
-            ret = 0;
-            break;
-        }
-        j += i;
-        n -= i;
-    }
+    int ret = BIO_write_all(out, b, n);
     OPENSSL_free(b);
-    return (ret);
+    return ret;
 }
diff --git a/src/crypto/bio/bio.c b/src/crypto/bio/bio.c
index 3e788b8..881c14e 100644
--- a/src/crypto/bio/bio.c
+++ b/src/crypto/bio/bio.c
@@ -177,6 +177,19 @@
   return ret;
 }
 
+int BIO_write_all(BIO *bio, const void *data, size_t len) {
+  const uint8_t *data_u8 = data;
+  while (len > 0) {
+    int ret = BIO_write(bio, data_u8, len > INT_MAX ? INT_MAX : (int)len);
+    if (ret <= 0) {
+      return 0;
+    }
+    data_u8 += ret;
+    len -= ret;
+  }
+  return 1;
+}
+
 int BIO_puts(BIO *bio, const char *in) {
   return BIO_write(bio, in, strlen(in));
 }
diff --git a/src/crypto/bio/file.c b/src/crypto/bio/file.c
index 6a0b9a9..a177763 100644
--- a/src/crypto/bio/file.c
+++ b/src/crypto/bio/file.c
@@ -107,13 +107,12 @@
     return NULL;
   }
 
-  ret = BIO_new(BIO_s_file());
+  ret = BIO_new_fp(file, BIO_CLOSE);
   if (ret == NULL) {
     fclose(file);
     return NULL;
   }
 
-  BIO_set_fp(ret, file, BIO_CLOSE);
   return ret;
 }
 
diff --git a/src/crypto/bn_extra/convert.c b/src/crypto/bn_extra/convert.c
index c70ff8b..9a1a69e 100644
--- a/src/crypto/bn_extra/convert.c
+++ b/src/crypto/bn_extra/convert.c
@@ -367,17 +367,13 @@
 }
 
 int BN_print_fp(FILE *fp, const BIGNUM *a) {
-  BIO *b;
-  int ret;
-
-  b = BIO_new(BIO_s_file());
+  BIO *b = BIO_new_fp(fp, BIO_NOCLOSE);
   if (b == NULL) {
     return 0;
   }
-  BIO_set_fp(b, fp, BIO_NOCLOSE);
-  ret = BN_print(b, a);
-  BIO_free(b);
 
+  int ret = BN_print(b, a);
+  BIO_free(b);
   return ret;
 }
 
diff --git a/src/crypto/conf/conf.c b/src/crypto/conf/conf.c
index b1982f8..4c27ddf 100644
--- a/src/crypto/conf/conf.c
+++ b/src/crypto/conf/conf.c
@@ -62,6 +62,7 @@
 #include <openssl/bio.h>
 #include <openssl/buf.h>
 #include <openssl/err.h>
+#include <openssl/lhash.h>
 #include <openssl/mem.h>
 
 #include "conf_def.h"
@@ -69,6 +70,10 @@
 #include "../internal.h"
 
 
+struct conf_st {
+  LHASH_OF(CONF_VALUE) *data;
+};
+
 // The maximum length we can grow a value to after variable expansion. 64k
 // should be more than enough for all reasonable uses.
 #define MAX_CONF_VALUE_LENGTH 65536
diff --git a/src/crypto/fipsmodule/CMakeLists.txt b/src/crypto/fipsmodule/CMakeLists.txt
index 9868dd8..e6c8cc6 100644
--- a/src/crypto/fipsmodule/CMakeLists.txt
+++ b/src/crypto/fipsmodule/CMakeLists.txt
@@ -162,12 +162,12 @@
   set_target_properties(bcm_hashunset PROPERTIES POSITION_INDEPENDENT_CODE ON)
   set_target_properties(bcm_hashunset PROPERTIES LINKER_LANGUAGE C)
 
-  go_executable(inject-hash
-	        boringssl.googlesource.com/boringssl/util/fipstools/inject-hash)
+  go_executable(inject_hash
+	        boringssl.googlesource.com/boringssl/util/fipstools/inject_hash)
   add_custom_command(
     OUTPUT bcm.o
-    COMMAND ./inject-hash -o bcm.o -in-archive $<TARGET_FILE:bcm_hashunset>
-    DEPENDS bcm_hashunset inject-hash
+    COMMAND ./inject_hash -o bcm.o -in-archive $<TARGET_FILE:bcm_hashunset>
+    DEPENDS bcm_hashunset inject_hash
     WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
   )
 
diff --git a/src/crypto/fipsmodule/FIPS.md b/src/crypto/fipsmodule/FIPS.md
index a691d18..426b38e 100644
--- a/src/crypto/fipsmodule/FIPS.md
+++ b/src/crypto/fipsmodule/FIPS.md
@@ -103,7 +103,7 @@
 
 In order to actually implement the integrity test, a constructor function within the module calculates an HMAC from `module_start` to `module_end` using a fixed, all-zero key. It compares the result with the known-good value added (by the script) to the unhashed portion of the text segment. If they don't match, it calls `exit` in an infinite loop.
 
-Initially the known-good value will be incorrect. Another script (`inject-hash.go`) calculates the correct value from the assembled object and injects it back into the object.
+Initially the known-good value will be incorrect. Another script (`inject_hash.go`) calculates the correct value from the assembled object and injects it back into the object.
 
 ![build process](/crypto/fipsmodule/intcheck2.png)
 
diff --git a/src/crypto/fipsmodule/aes/mode_wrappers.c b/src/crypto/fipsmodule/aes/mode_wrappers.c
index 5c50c85..0140c70 100644
--- a/src/crypto/fipsmodule/aes/mode_wrappers.c
+++ b/src/crypto/fipsmodule/aes/mode_wrappers.c
@@ -57,8 +57,7 @@
 void AES_ctr128_encrypt(const uint8_t *in, uint8_t *out, size_t len,
                         const AES_KEY *key, uint8_t ivec[AES_BLOCK_SIZE],
                         uint8_t ecount_buf[AES_BLOCK_SIZE], unsigned int *num) {
-  CRYPTO_ctr128_encrypt(in, out, len, key, ivec, ecount_buf, num,
-                        (block128_f)AES_encrypt);
+  CRYPTO_ctr128_encrypt(in, out, len, key, ivec, ecount_buf, num, AES_encrypt);
 }
 
 void AES_ecb_encrypt(const uint8_t *in, uint8_t *out, const AES_KEY *key,
@@ -90,9 +89,9 @@
   aes_nohw_cbc_encrypt(in, out, len, key, ivec, enc);
 #else
   if (enc) {
-    CRYPTO_cbc128_encrypt(in, out, len, key, ivec, (block128_f)AES_encrypt);
+    CRYPTO_cbc128_encrypt(in, out, len, key, ivec, AES_encrypt);
   } else {
-    CRYPTO_cbc128_decrypt(in, out, len, key, ivec, (block128_f)AES_decrypt);
+    CRYPTO_cbc128_decrypt(in, out, len, key, ivec, AES_decrypt);
   }
 #endif
 }
@@ -100,8 +99,7 @@
 void AES_ofb128_encrypt(const uint8_t *in, uint8_t *out, size_t length,
                         const AES_KEY *key, uint8_t *ivec, int *num) {
   unsigned num_u = (unsigned)(*num);
-  CRYPTO_ofb128_encrypt(in, out, length, key, ivec, &num_u,
-                        (block128_f)AES_encrypt);
+  CRYPTO_ofb128_encrypt(in, out, length, key, ivec, &num_u, AES_encrypt);
   *num = (int)num_u;
 }
 
@@ -109,7 +107,6 @@
                         const AES_KEY *key, uint8_t *ivec, int *num,
                         int enc) {
   unsigned num_u = (unsigned)(*num);
-  CRYPTO_cfb128_encrypt(in, out, length, key, ivec, &num_u, enc,
-                        (block128_f)AES_encrypt);
+  CRYPTO_cfb128_encrypt(in, out, length, key, ivec, &num_u, enc, AES_encrypt);
   *num = (int)num_u;
 }
diff --git a/src/crypto/fipsmodule/bcm.c b/src/crypto/fipsmodule/bcm.c
index 6b21f06..e15ecb8 100644
--- a/src/crypto/fipsmodule/bcm.c
+++ b/src/crypto/fipsmodule/bcm.c
@@ -124,6 +124,7 @@
   if (!HMAC(EVP_sha512(), kHMACKey, sizeof(kHMACKey), start, end - start,
             result, &result_len) ||
       result_len != sizeof(result)) {
+    fprintf(stderr, "HMAC failed.\n");
     goto err;
   }
 
diff --git a/src/crypto/fipsmodule/bn/div.c b/src/crypto/fipsmodule/bn/div.c
index 57485bd..27b591c 100644
--- a/src/crypto/fipsmodule/bn/div.c
+++ b/src/crypto/fipsmodule/bn/div.c
@@ -64,7 +64,7 @@
 #include "internal.h"
 
 
-#if !defined(BN_ULLONG)
+#if !defined(BN_CAN_DIVIDE_ULLONG) && !defined(BN_CAN_USE_INLINE_ASM)
 // bn_div_words divides a double-width |h|,|l| by |d| and returns the result,
 // which must fit in a |BN_ULONG|.
 static BN_ULONG bn_div_words(BN_ULONG h, BN_ULONG l, BN_ULONG d) {
@@ -135,7 +135,7 @@
   ret |= q;
   return ret;
 }
-#endif  // !defined(BN_ULLONG)
+#endif  // !defined(BN_CAN_DIVIDE_ULLONG) && !defined(BN_CAN_USE_INLINE_ASM)
 
 static inline void bn_div_rem_words(BN_ULONG *quotient_out, BN_ULONG *rem_out,
                                     BN_ULONG n0, BN_ULONG n1, BN_ULONG d0) {
@@ -155,20 +155,18 @@
   //
   // These issues aren't specific to x86 and x86_64, so it might be worthwhile
   // to add more assembly language implementations.
-#if !defined(OPENSSL_NO_ASM) && defined(OPENSSL_X86) && \
-    (defined(__GNUC__) || defined(__clang__))
+#if defined(BN_CAN_USE_INLINE_ASM) && defined(OPENSSL_X86)
   __asm__ volatile("divl %4"
                    : "=a"(*quotient_out), "=d"(*rem_out)
                    : "a"(n1), "d"(n0), "rm"(d0)
                    : "cc");
-#elif !defined(OPENSSL_NO_ASM) && defined(OPENSSL_X86_64) && \
-    (defined(__GNUC__) || defined(__clang__))
+#elif defined(BN_CAN_USE_INLINE_ASM) && defined(OPENSSL_X86_64)
   __asm__ volatile("divq %4"
                    : "=a"(*quotient_out), "=d"(*rem_out)
                    : "a"(n1), "d"(n0), "rm"(d0)
                    : "cc");
 #else
-#if defined(BN_ULLONG)
+#if defined(BN_CAN_DIVIDE_ULLONG)
   BN_ULLONG n = (((BN_ULLONG)n0) << BN_BITS2) | n1;
   *quotient_out = (BN_ULONG)(n / d0);
 #else
diff --git a/src/crypto/fipsmodule/bn/internal.h b/src/crypto/fipsmodule/bn/internal.h
index 9796831..fb8d11f 100644
--- a/src/crypto/fipsmodule/bn/internal.h
+++ b/src/crypto/fipsmodule/bn/internal.h
@@ -185,6 +185,10 @@
 #error "Must define either OPENSSL_32_BIT or OPENSSL_64_BIT"
 #endif
 
+#if !defined(OPENSSL_NO_ASM) && (defined(__GNUC__) || defined(__clang__))
+#define BN_CAN_USE_INLINE_ASM
+#endif
+
 // |BN_mod_exp_mont_consttime| is based on the assumption that the L1 data
 // cache line width of the target processor is at least the following value.
 #define MOD_EXP_CTIME_MIN_CACHE_LINE_WIDTH 64
diff --git a/src/crypto/fipsmodule/cipher/e_aes.c b/src/crypto/fipsmodule/cipher/e_aes.c
index 734a517..0ced193 100644
--- a/src/crypto/fipsmodule/cipher/e_aes.c
+++ b/src/crypto/fipsmodule/cipher/e_aes.c
@@ -198,49 +198,45 @@
   if ((mode == EVP_CIPH_ECB_MODE || mode == EVP_CIPH_CBC_MODE) && !enc) {
     if (hwaes_capable()) {
       ret = aes_hw_set_decrypt_key(key, ctx->key_len * 8, &dat->ks.ks);
-      dat->block = (block128_f)aes_hw_decrypt;
+      dat->block = aes_hw_decrypt;
       dat->stream.cbc = NULL;
       if (mode == EVP_CIPH_CBC_MODE) {
-        dat->stream.cbc = (cbc128_f)aes_hw_cbc_encrypt;
+        dat->stream.cbc = aes_hw_cbc_encrypt;
       }
     } else if (bsaes_capable() && mode == EVP_CIPH_CBC_MODE) {
       ret = AES_set_decrypt_key(key, ctx->key_len * 8, &dat->ks.ks);
-      dat->block = (block128_f)AES_decrypt;
-      dat->stream.cbc = (cbc128_f)bsaes_cbc_encrypt;
+      dat->block = AES_decrypt;
+      dat->stream.cbc = bsaes_cbc_encrypt;
     } else if (vpaes_capable()) {
       ret = vpaes_set_decrypt_key(key, ctx->key_len * 8, &dat->ks.ks);
-      dat->block = (block128_f)vpaes_decrypt;
-      dat->stream.cbc =
-          mode == EVP_CIPH_CBC_MODE ? (cbc128_f)vpaes_cbc_encrypt : NULL;
+      dat->block = vpaes_decrypt;
+      dat->stream.cbc = mode == EVP_CIPH_CBC_MODE ? vpaes_cbc_encrypt : NULL;
     } else {
       ret = AES_set_decrypt_key(key, ctx->key_len * 8, &dat->ks.ks);
-      dat->block = (block128_f)AES_decrypt;
-      dat->stream.cbc =
-          mode == EVP_CIPH_CBC_MODE ? (cbc128_f)AES_cbc_encrypt : NULL;
+      dat->block = AES_decrypt;
+      dat->stream.cbc = mode == EVP_CIPH_CBC_MODE ? AES_cbc_encrypt : NULL;
     }
   } else if (hwaes_capable()) {
     ret = aes_hw_set_encrypt_key(key, ctx->key_len * 8, &dat->ks.ks);
-    dat->block = (block128_f)aes_hw_encrypt;
+    dat->block = aes_hw_encrypt;
     dat->stream.cbc = NULL;
     if (mode == EVP_CIPH_CBC_MODE) {
-      dat->stream.cbc = (cbc128_f)aes_hw_cbc_encrypt;
+      dat->stream.cbc = aes_hw_cbc_encrypt;
     } else if (mode == EVP_CIPH_CTR_MODE) {
-      dat->stream.ctr = (ctr128_f)aes_hw_ctr32_encrypt_blocks;
+      dat->stream.ctr = aes_hw_ctr32_encrypt_blocks;
     }
   } else if (bsaes_capable() && mode == EVP_CIPH_CTR_MODE) {
     ret = AES_set_encrypt_key(key, ctx->key_len * 8, &dat->ks.ks);
-    dat->block = (block128_f)AES_encrypt;
-    dat->stream.ctr = (ctr128_f)bsaes_ctr32_encrypt_blocks;
+    dat->block = AES_encrypt;
+    dat->stream.ctr = bsaes_ctr32_encrypt_blocks;
   } else if (vpaes_capable()) {
     ret = vpaes_set_encrypt_key(key, ctx->key_len * 8, &dat->ks.ks);
-    dat->block = (block128_f)vpaes_encrypt;
-    dat->stream.cbc =
-        mode == EVP_CIPH_CBC_MODE ? (cbc128_f)vpaes_cbc_encrypt : NULL;
+    dat->block = vpaes_encrypt;
+    dat->stream.cbc = mode == EVP_CIPH_CBC_MODE ? vpaes_cbc_encrypt : NULL;
   } else {
     ret = AES_set_encrypt_key(key, ctx->key_len * 8, &dat->ks.ks);
-    dat->block = (block128_f)AES_encrypt;
-    dat->stream.cbc =
-        mode == EVP_CIPH_CBC_MODE ? (cbc128_f)AES_cbc_encrypt : NULL;
+    dat->block = AES_encrypt;
+    dat->stream.cbc = mode == EVP_CIPH_CBC_MODE ? AES_cbc_encrypt : NULL;
   }
 
   if (ret < 0) {
@@ -256,11 +252,11 @@
   EVP_AES_KEY *dat = (EVP_AES_KEY *)ctx->cipher_data;
 
   if (dat->stream.cbc) {
-    (*dat->stream.cbc)(in, out, len, &dat->ks, ctx->iv, ctx->encrypt);
+    (*dat->stream.cbc)(in, out, len, &dat->ks.ks, ctx->iv, ctx->encrypt);
   } else if (ctx->encrypt) {
-    CRYPTO_cbc128_encrypt(in, out, len, &dat->ks, ctx->iv, dat->block);
+    CRYPTO_cbc128_encrypt(in, out, len, &dat->ks.ks, ctx->iv, dat->block);
   } else {
-    CRYPTO_cbc128_decrypt(in, out, len, &dat->ks, ctx->iv, dat->block);
+    CRYPTO_cbc128_decrypt(in, out, len, &dat->ks.ks, ctx->iv, dat->block);
   }
 
   return 1;
@@ -277,7 +273,7 @@
 
   len -= bl;
   for (size_t i = 0; i <= len; i += bl) {
-    (*dat->block)(in + i, out + i, &dat->ks);
+    (*dat->block)(in + i, out + i, &dat->ks.ks);
   }
 
   return 1;
@@ -288,11 +284,11 @@
   EVP_AES_KEY *dat = (EVP_AES_KEY *)ctx->cipher_data;
 
   if (dat->stream.ctr) {
-    CRYPTO_ctr128_encrypt_ctr32(in, out, len, &dat->ks, ctx->iv, ctx->buf,
+    CRYPTO_ctr128_encrypt_ctr32(in, out, len, &dat->ks.ks, ctx->iv, ctx->buf,
                                 &ctx->num, dat->stream.ctr);
   } else {
-    CRYPTO_ctr128_encrypt(in, out, len, &dat->ks, ctx->iv, ctx->buf, &ctx->num,
-                          dat->block);
+    CRYPTO_ctr128_encrypt(in, out, len, &dat->ks.ks, ctx->iv, ctx->buf,
+                          &ctx->num, dat->block);
   }
   return 1;
 }
@@ -301,7 +297,8 @@
                           size_t len) {
   EVP_AES_KEY *dat = (EVP_AES_KEY *)ctx->cipher_data;
 
-  CRYPTO_ofb128_encrypt(in, out, len, &dat->ks, ctx->iv, &ctx->num, dat->block);
+  CRYPTO_ofb128_encrypt(in, out, len, &dat->ks.ks, ctx->iv, &ctx->num,
+                        dat->block);
   return 1;
 }
 
@@ -311,42 +308,42 @@
   if (hwaes_capable()) {
     aes_hw_set_encrypt_key(key, key_bytes * 8, aes_key);
     if (gcm_key != NULL) {
-      CRYPTO_gcm128_init_key(gcm_key, aes_key, (block128_f)aes_hw_encrypt, 1);
+      CRYPTO_gcm128_init_key(gcm_key, aes_key, aes_hw_encrypt, 1);
     }
     if (out_block) {
-      *out_block = (block128_f) aes_hw_encrypt;
+      *out_block = aes_hw_encrypt;
     }
-    return (ctr128_f)aes_hw_ctr32_encrypt_blocks;
+    return aes_hw_ctr32_encrypt_blocks;
   }
 
   if (bsaes_capable()) {
     AES_set_encrypt_key(key, key_bytes * 8, aes_key);
     if (gcm_key != NULL) {
-      CRYPTO_gcm128_init_key(gcm_key, aes_key, (block128_f)AES_encrypt, 0);
+      CRYPTO_gcm128_init_key(gcm_key, aes_key, AES_encrypt, 0);
     }
     if (out_block) {
-      *out_block = (block128_f) AES_encrypt;
+      *out_block = AES_encrypt;
     }
-    return (ctr128_f)bsaes_ctr32_encrypt_blocks;
+    return bsaes_ctr32_encrypt_blocks;
   }
 
   if (vpaes_capable()) {
     vpaes_set_encrypt_key(key, key_bytes * 8, aes_key);
     if (out_block) {
-      *out_block = (block128_f) vpaes_encrypt;
+      *out_block = vpaes_encrypt;
     }
     if (gcm_key != NULL) {
-      CRYPTO_gcm128_init_key(gcm_key, aes_key, (block128_f)vpaes_encrypt, 0);
+      CRYPTO_gcm128_init_key(gcm_key, aes_key, vpaes_encrypt, 0);
     }
     return NULL;
   }
 
   AES_set_encrypt_key(key, key_bytes * 8, aes_key);
   if (gcm_key != NULL) {
-    CRYPTO_gcm128_init_key(gcm_key, aes_key, (block128_f)AES_encrypt, 0);
+    CRYPTO_gcm128_init_key(gcm_key, aes_key, AES_encrypt, 0);
   }
   if (out_block) {
-    *out_block = (block128_f) AES_encrypt;
+    *out_block = AES_encrypt;
   }
   return NULL;
 }
diff --git a/src/crypto/fipsmodule/modes/cbc.c b/src/crypto/fipsmodule/modes/cbc.c
index db9f024..64ea505 100644
--- a/src/crypto/fipsmodule/modes/cbc.c
+++ b/src/crypto/fipsmodule/modes/cbc.c
@@ -53,7 +53,7 @@
 
 
 void CRYPTO_cbc128_encrypt(const uint8_t *in, uint8_t *out, size_t len,
-                           const void *key, uint8_t ivec[16],
+                           const AES_KEY *key, uint8_t ivec[16],
                            block128_f block) {
   size_t n;
   const uint8_t *iv = ivec;
@@ -108,7 +108,7 @@
 }
 
 void CRYPTO_cbc128_decrypt(const uint8_t *in, uint8_t *out, size_t len,
-                           const void *key, uint8_t ivec[16],
+                           const AES_KEY *key, uint8_t ivec[16],
                            block128_f block) {
   size_t n;
   union {
diff --git a/src/crypto/fipsmodule/modes/ccm.c b/src/crypto/fipsmodule/modes/ccm.c
index 784e4fa..5a153f4 100644
--- a/src/crypto/fipsmodule/modes/ccm.c
+++ b/src/crypto/fipsmodule/modes/ccm.c
@@ -64,8 +64,8 @@
   } nonce, cmac;
 };
 
-int CRYPTO_ccm128_init(CCM128_CONTEXT *ctx, const void *key, block128_f block,
-                       ctr128_f ctr, unsigned M, unsigned L) {
+int CRYPTO_ccm128_init(CCM128_CONTEXT *ctx, const AES_KEY *key,
+                       block128_f block, ctr128_f ctr, unsigned M, unsigned L) {
   if (M < 4 || M > 16 || (M & 1) != 0 || L < 2 || L > 8) {
     return 0;
   }
@@ -82,7 +82,7 @@
 }
 
 static int ccm128_init_state(const CCM128_CONTEXT *ctx,
-                             struct ccm128_state *state, const void *key,
+                             struct ccm128_state *state, const AES_KEY *key,
                              const uint8_t *nonce, size_t nonce_len,
                              const uint8_t *aad, size_t aad_len,
                              size_t plaintext_len) {
@@ -170,7 +170,7 @@
 }
 
 static int ccm128_encrypt(const CCM128_CONTEXT *ctx, struct ccm128_state *state,
-                          const void *key, uint8_t *out, const uint8_t *in,
+                          const AES_KEY *key, uint8_t *out, const uint8_t *in,
                           size_t len) {
   // The counter for encryption begins at one.
   for (unsigned i = 0; i < ctx->L; i++) {
@@ -191,7 +191,7 @@
 }
 
 static int ccm128_compute_mac(const CCM128_CONTEXT *ctx,
-                              struct ccm128_state *state, const void *key,
+                              struct ccm128_state *state, const AES_KEY *key,
                               uint8_t *out_tag, size_t tag_len,
                               const uint8_t *in, size_t len) {
   block128_f block = ctx->block;
@@ -231,7 +231,7 @@
   return 1;
 }
 
-int CRYPTO_ccm128_encrypt(const CCM128_CONTEXT *ctx, const void *key,
+int CRYPTO_ccm128_encrypt(const CCM128_CONTEXT *ctx, const AES_KEY *key,
                           uint8_t *out, uint8_t *out_tag, size_t tag_len,
                           const uint8_t *nonce, size_t nonce_len,
                           const uint8_t *in, size_t len, const uint8_t *aad,
@@ -243,7 +243,7 @@
          ccm128_encrypt(ctx, &state, key, out, in, len);
 }
 
-int CRYPTO_ccm128_decrypt(const CCM128_CONTEXT *ctx, const void *key,
+int CRYPTO_ccm128_decrypt(const CCM128_CONTEXT *ctx, const AES_KEY *key,
                           uint8_t *out, uint8_t *out_tag, size_t tag_len,
                           const uint8_t *nonce, size_t nonce_len,
                           const uint8_t *in, size_t len, const uint8_t *aad,
diff --git a/src/crypto/fipsmodule/modes/cfb.c b/src/crypto/fipsmodule/modes/cfb.c
index e1b0a80..d3a38d6 100644
--- a/src/crypto/fipsmodule/modes/cfb.c
+++ b/src/crypto/fipsmodule/modes/cfb.c
@@ -57,7 +57,7 @@
 OPENSSL_COMPILE_ASSERT((16 % sizeof(size_t)) == 0, bad_size_t_size_cfb);
 
 void CRYPTO_cfb128_encrypt(const uint8_t *in, uint8_t *out, size_t len,
-                           const void *key, uint8_t ivec[16], unsigned *num,
+                           const AES_KEY *key, uint8_t ivec[16], unsigned *num,
                            int enc, block128_f block) {
   size_t l = 0;
 
@@ -161,7 +161,7 @@
 /* This expects a single block of size nbits for both in and out. Note that
    it corrupts any extra bits in the last byte of out */
 static void cfbr_encrypt_block(const uint8_t *in, uint8_t *out, unsigned nbits,
-                               const void *key, uint8_t ivec[16], int enc,
+                               const AES_KEY *key, uint8_t ivec[16], int enc,
                                block128_f block) {
   int n, rem, num;
   uint8_t ovec[16 * 2 + 1]; /* +1 because we dererefence (but don't use) one
@@ -203,8 +203,8 @@
 
 // N.B. This expects the input to be packed, MS bit first
 void CRYPTO_cfb128_1_encrypt(const uint8_t *in, uint8_t *out, size_t bits,
-                             const void *key, uint8_t ivec[16], unsigned *num,
-                             int enc, block128_f block) {
+                             const AES_KEY *key, uint8_t ivec[16],
+                             unsigned *num, int enc, block128_f block) {
   size_t n;
   uint8_t c[1], d[1];
 
@@ -220,7 +220,7 @@
 }
 
 void CRYPTO_cfb128_8_encrypt(const unsigned char *in, unsigned char *out,
-                             size_t length, const void *key,
+                             size_t length, const AES_KEY *key,
                              unsigned char ivec[16], unsigned *num, int enc,
                              block128_f block) {
   size_t n;
diff --git a/src/crypto/fipsmodule/modes/ctr.c b/src/crypto/fipsmodule/modes/ctr.c
index 63907b4..5093408 100644
--- a/src/crypto/fipsmodule/modes/ctr.c
+++ b/src/crypto/fipsmodule/modes/ctr.c
@@ -82,7 +82,7 @@
 // of the IV.  This implementation takes NO responsibility for checking that
 // the counter doesn't overflow into the rest of the IV when incremented.
 void CRYPTO_ctr128_encrypt(const uint8_t *in, uint8_t *out, size_t len,
-                           const void *key, uint8_t ivec[16],
+                           const AES_KEY *key, uint8_t ivec[16],
                            uint8_t ecount_buf[16], unsigned int *num,
                            block128_f block) {
   unsigned int n;
@@ -153,11 +153,10 @@
   } while (n);
 }
 
-void CRYPTO_ctr128_encrypt_ctr32(const uint8_t *in, uint8_t *out,
-                                 size_t len, const void *key,
-                                 uint8_t ivec[16],
-                                 uint8_t ecount_buf[16],
-                                 unsigned int *num, ctr128_f func) {
+void CRYPTO_ctr128_encrypt_ctr32(const uint8_t *in, uint8_t *out, size_t len,
+                                 const AES_KEY *key, uint8_t ivec[16],
+                                 uint8_t ecount_buf[16], unsigned int *num,
+                                 ctr128_f func) {
   unsigned int n, ctr32;
 
   assert(key && ecount_buf && num);
diff --git a/src/crypto/fipsmodule/modes/gcm.c b/src/crypto/fipsmodule/modes/gcm.c
index 6eff479..5e556df 100644
--- a/src/crypto/fipsmodule/modes/gcm.c
+++ b/src/crypto/fipsmodule/modes/gcm.c
@@ -271,9 +271,9 @@
                    size_t len);
 #define AESNI_GCM
 size_t aesni_gcm_encrypt(const uint8_t *in, uint8_t *out, size_t len,
-                         const void *key, uint8_t ivec[16], uint64_t *Xi);
+                         const AES_KEY *key, uint8_t ivec[16], uint64_t *Xi);
 size_t aesni_gcm_decrypt(const uint8_t *in, uint8_t *out, size_t len,
-                         const void *key, uint8_t ivec[16], uint64_t *Xi);
+                         const AES_KEY *key, uint8_t ivec[16], uint64_t *Xi);
 #endif
 
 #if defined(OPENSSL_X86)
@@ -419,7 +419,7 @@
 #endif
 }
 
-void CRYPTO_gcm128_init_key(GCM128_KEY *gcm_key, const void *aes_key,
+void CRYPTO_gcm128_init_key(GCM128_KEY *gcm_key, const AES_KEY *aes_key,
                             block128_f block, int block_is_hwaes) {
   OPENSSL_memset(gcm_key, 0, sizeof(*gcm_key));
   gcm_key->block = block;
@@ -435,7 +435,7 @@
   gcm_key->use_aesni_gcm_crypt = (is_avx && block_is_hwaes) ? 1 : 0;
 }
 
-void CRYPTO_gcm128_setiv(GCM128_CONTEXT *ctx, const void *key,
+void CRYPTO_gcm128_setiv(GCM128_CONTEXT *ctx, const AES_KEY *key,
                          const uint8_t *iv, size_t len) {
   unsigned int ctr;
 #ifdef GCM_FUNCREF_4BIT
@@ -553,7 +553,7 @@
   return 1;
 }
 
-int CRYPTO_gcm128_encrypt(GCM128_CONTEXT *ctx, const void *key,
+int CRYPTO_gcm128_encrypt(GCM128_CONTEXT *ctx, const AES_KEY *key,
                           const uint8_t *in, uint8_t *out, size_t len) {
   unsigned int n, ctr;
   uint64_t mlen = ctx->len.u[1];
@@ -679,7 +679,7 @@
   return 1;
 }
 
-int CRYPTO_gcm128_decrypt(GCM128_CONTEXT *ctx, const void *key,
+int CRYPTO_gcm128_decrypt(GCM128_CONTEXT *ctx, const AES_KEY *key,
                           const unsigned char *in, unsigned char *out,
                           size_t len) {
   unsigned int n, ctr;
@@ -813,7 +813,7 @@
   return 1;
 }
 
-int CRYPTO_gcm128_encrypt_ctr32(GCM128_CONTEXT *ctx, const void *key,
+int CRYPTO_gcm128_encrypt_ctr32(GCM128_CONTEXT *ctx, const AES_KEY *key,
                                 const uint8_t *in, uint8_t *out, size_t len,
                                 ctr128_f stream) {
   unsigned int n, ctr;
@@ -915,7 +915,7 @@
   return 1;
 }
 
-int CRYPTO_gcm128_decrypt_ctr32(GCM128_CONTEXT *ctx, const void *key,
+int CRYPTO_gcm128_decrypt_ctr32(GCM128_CONTEXT *ctx, const AES_KEY *key,
                                 const uint8_t *in, uint8_t *out, size_t len,
                                 ctr128_f stream) {
   unsigned int n, ctr;
diff --git a/src/crypto/fipsmodule/modes/gcm_test.cc b/src/crypto/fipsmodule/modes/gcm_test.cc
index ab8cc34..30308f8 100644
--- a/src/crypto/fipsmodule/modes/gcm_test.cc
+++ b/src/crypto/fipsmodule/modes/gcm_test.cc
@@ -88,7 +88,7 @@
 
     GCM128_CONTEXT ctx;
     OPENSSL_memset(&ctx, 0, sizeof(ctx));
-    CRYPTO_gcm128_init_key(&ctx.gcm_key, &aes_key, (block128_f)AES_encrypt, 0);
+    CRYPTO_gcm128_init_key(&ctx.gcm_key, &aes_key, AES_encrypt, 0);
     CRYPTO_gcm128_setiv(&ctx, &aes_key, nonce.data(), nonce.size());
     if (!additional_data.empty()) {
       CRYPTO_gcm128_aad(&ctx, additional_data.data(), additional_data.size());
diff --git a/src/crypto/fipsmodule/modes/internal.h b/src/crypto/fipsmodule/modes/internal.h
index 788960b..23aaca2 100644
--- a/src/crypto/fipsmodule/modes/internal.h
+++ b/src/crypto/fipsmodule/modes/internal.h
@@ -50,6 +50,7 @@
 #define OPENSSL_HEADER_MODES_INTERNAL_H
 
 #include <openssl/base.h>
+#include <openssl/aes.h>
 
 #include <string.h>
 
@@ -87,16 +88,24 @@
   OPENSSL_memcpy(out, &v, sizeof(v));
 }
 
-// block128_f is the type of a 128-bit, block cipher.
+// block128_f is the type of an AES block cipher implementation.
+//
+// Unlike upstream OpenSSL, it and the other functions in this file hard-code
+// |AES_KEY|. It is undefined in C to call a function pointer with anything
+// other than the original type. Thus we either must match |block128_f| to the
+// type signature of |AES_encrypt| and friends or pass in |void*| wrapper
+// functions.
+//
+// These functions are called exclusively with AES, so we use the former.
 typedef void (*block128_f)(const uint8_t in[16], uint8_t out[16],
-                           const void *key);
+                           const AES_KEY *key);
 
 
 // CTR.
 
 // ctr128_f is the type of a function that performs CTR-mode encryption.
 typedef void (*ctr128_f)(const uint8_t *in, uint8_t *out, size_t blocks,
-                         const void *key, const uint8_t ivec[16]);
+                         const AES_KEY *key, const uint8_t ivec[16]);
 
 // CRYPTO_ctr128_encrypt encrypts (or decrypts, it's the same in CTR mode)
 // |len| bytes from |in| to |out| using |block| in counter mode. There's no
@@ -105,7 +114,7 @@
 // call. The counter is a 128-bit, big-endian value in |ivec| and is
 // incremented by this function.
 void CRYPTO_ctr128_encrypt(const uint8_t *in, uint8_t *out, size_t len,
-                           const void *key, uint8_t ivec[16],
+                           const AES_KEY *key, uint8_t ivec[16],
                            uint8_t ecount_buf[16], unsigned *num,
                            block128_f block);
 
@@ -114,14 +123,14 @@
 // bits of the counter. This is useful when |ctr| can be an optimised
 // function.
 void CRYPTO_ctr128_encrypt_ctr32(const uint8_t *in, uint8_t *out, size_t len,
-                                 const void *key, uint8_t ivec[16],
+                                 const AES_KEY *key, uint8_t ivec[16],
                                  uint8_t ecount_buf[16], unsigned *num,
                                  ctr128_f ctr);
 
 #if !defined(OPENSSL_NO_ASM) && \
     (defined(OPENSSL_X86) || defined(OPENSSL_X86_64))
 void aesni_ctr32_encrypt_blocks(const uint8_t *in, uint8_t *out, size_t blocks,
-                                const void *key, const uint8_t *ivec);
+                                const AES_KEY *key, const uint8_t *ivec);
 #endif
 
 
@@ -194,13 +203,13 @@
 
 // CRYPTO_gcm128_init_key initialises |gcm_key| to use |block| (typically AES)
 // with the given key. |block_is_hwaes| is one if |block| is |aes_hw_encrypt|.
-OPENSSL_EXPORT void CRYPTO_gcm128_init_key(GCM128_KEY *gcm_key, const void *key,
-                                           block128_f block,
+OPENSSL_EXPORT void CRYPTO_gcm128_init_key(GCM128_KEY *gcm_key,
+                                           const AES_KEY *key, block128_f block,
                                            int block_is_hwaes);
 
 // CRYPTO_gcm128_setiv sets the IV (nonce) for |ctx|. The |key| must be the
 // same key that was passed to |CRYPTO_gcm128_init|.
-OPENSSL_EXPORT void CRYPTO_gcm128_setiv(GCM128_CONTEXT *ctx, const void *key,
+OPENSSL_EXPORT void CRYPTO_gcm128_setiv(GCM128_CONTEXT *ctx, const AES_KEY *key,
                                         const uint8_t *iv, size_t iv_len);
 
 // CRYPTO_gcm128_aad sets the authenticated data for an instance of GCM.
@@ -212,16 +221,16 @@
 // CRYPTO_gcm128_encrypt encrypts |len| bytes from |in| to |out|. The |key|
 // must be the same key that was passed to |CRYPTO_gcm128_init|. It returns one
 // on success and zero otherwise.
-OPENSSL_EXPORT int CRYPTO_gcm128_encrypt(GCM128_CONTEXT *ctx, const void *key,
-                                         const uint8_t *in, uint8_t *out,
-                                         size_t len);
+OPENSSL_EXPORT int CRYPTO_gcm128_encrypt(GCM128_CONTEXT *ctx,
+                                         const AES_KEY *key, const uint8_t *in,
+                                         uint8_t *out, size_t len);
 
 // CRYPTO_gcm128_decrypt decrypts |len| bytes from |in| to |out|. The |key|
 // must be the same key that was passed to |CRYPTO_gcm128_init|. It returns one
 // on success and zero otherwise.
-OPENSSL_EXPORT int CRYPTO_gcm128_decrypt(GCM128_CONTEXT *ctx, const void *key,
-                                         const uint8_t *in, uint8_t *out,
-                                         size_t len);
+OPENSSL_EXPORT int CRYPTO_gcm128_decrypt(GCM128_CONTEXT *ctx,
+                                         const AES_KEY *key, const uint8_t *in,
+                                         uint8_t *out, size_t len);
 
 // CRYPTO_gcm128_encrypt_ctr32 encrypts |len| bytes from |in| to |out| using
 // a CTR function that only handles the bottom 32 bits of the nonce, like
@@ -229,7 +238,7 @@
 // passed to |CRYPTO_gcm128_init|. It returns one on success and zero
 // otherwise.
 OPENSSL_EXPORT int CRYPTO_gcm128_encrypt_ctr32(GCM128_CONTEXT *ctx,
-                                               const void *key,
+                                               const AES_KEY *key,
                                                const uint8_t *in, uint8_t *out,
                                                size_t len, ctr128_f stream);
 
@@ -239,7 +248,7 @@
 // passed to |CRYPTO_gcm128_init|. It returns one on success and zero
 // otherwise.
 OPENSSL_EXPORT int CRYPTO_gcm128_decrypt_ctr32(GCM128_CONTEXT *ctx,
-                                               const void *key,
+                                               const AES_KEY *key,
                                                const uint8_t *in, uint8_t *out,
                                                size_t len, ctr128_f stream);
 
@@ -265,8 +274,8 @@
 // CRYPTO_ccm128_init initialises |ctx| to use |block| (typically AES) with the
 // specified |M| and |L| parameters. It returns one on success and zero if |M|
 // or |L| is invalid.
-int CRYPTO_ccm128_init(CCM128_CONTEXT *ctx, const void *key, block128_f block,
-                       ctr128_f ctr, unsigned M, unsigned L);
+int CRYPTO_ccm128_init(CCM128_CONTEXT *ctx, const AES_KEY *key,
+                       block128_f block, ctr128_f ctr, unsigned M, unsigned L);
 
 // CRYPTO_ccm128_max_input returns the maximum input length accepted by |ctx|.
 size_t CRYPTO_ccm128_max_input(const CCM128_CONTEXT *ctx);
@@ -274,7 +283,7 @@
 // CRYPTO_ccm128_encrypt encrypts |len| bytes from |in| to |out| writing the tag
 // to |out_tag|. |key| must be the same key that was passed to
 // |CRYPTO_ccm128_init|. It returns one on success and zero otherwise.
-int CRYPTO_ccm128_encrypt(const CCM128_CONTEXT *ctx, const void *key,
+int CRYPTO_ccm128_encrypt(const CCM128_CONTEXT *ctx, const AES_KEY *key,
                           uint8_t *out, uint8_t *out_tag, size_t tag_len,
                           const uint8_t *nonce, size_t nonce_len,
                           const uint8_t *in, size_t len, const uint8_t *aad,
@@ -283,7 +292,7 @@
 // CRYPTO_ccm128_decrypt decrypts |len| bytes from |in| to |out|, writing the
 // expected tag to |out_tag|. |key| must be the same key that was passed to
 // |CRYPTO_ccm128_init|. It returns one on success and zero otherwise.
-int CRYPTO_ccm128_decrypt(const CCM128_CONTEXT *ctx, const void *key,
+int CRYPTO_ccm128_decrypt(const CCM128_CONTEXT *ctx, const AES_KEY *key,
                           uint8_t *out, uint8_t *out_tag, size_t tag_len,
                           const uint8_t *nonce, size_t nonce_len,
                           const uint8_t *in, size_t len, const uint8_t *aad,
@@ -294,21 +303,23 @@
 
 // cbc128_f is the type of a function that performs CBC-mode encryption.
 typedef void (*cbc128_f)(const uint8_t *in, uint8_t *out, size_t len,
-                         const void *key, uint8_t ivec[16], int enc);
+                         const AES_KEY *key, uint8_t ivec[16], int enc);
 
 // CRYPTO_cbc128_encrypt encrypts |len| bytes from |in| to |out| using the
 // given IV and block cipher in CBC mode. The input need not be a multiple of
 // 128 bits long, but the output will round up to the nearest 128 bit multiple,
 // zero padding the input if needed. The IV will be updated on return.
 void CRYPTO_cbc128_encrypt(const uint8_t *in, uint8_t *out, size_t len,
-                           const void *key, uint8_t ivec[16], block128_f block);
+                           const AES_KEY *key, uint8_t ivec[16],
+                           block128_f block);
 
 // CRYPTO_cbc128_decrypt decrypts |len| bytes from |in| to |out| using the
 // given IV and block cipher in CBC mode. If |len| is not a multiple of 128
 // bits then only that many bytes will be written, but a multiple of 128 bits
 // is always read from |in|. The IV will be updated on return.
 void CRYPTO_cbc128_decrypt(const uint8_t *in, uint8_t *out, size_t len,
-                           const void *key, uint8_t ivec[16], block128_f block);
+                           const AES_KEY *key, uint8_t ivec[16],
+                           block128_f block);
 
 
 // OFB.
@@ -318,9 +329,9 @@
 // requirement that |len| be a multiple of any value and any partial blocks are
 // stored in |ivec| and |*num|, the latter must be zero before the initial
 // call.
-void CRYPTO_ofb128_encrypt(const uint8_t *in, uint8_t *out,
-                           size_t len, const void *key, uint8_t ivec[16],
-                           unsigned *num, block128_f block);
+void CRYPTO_ofb128_encrypt(const uint8_t *in, uint8_t *out, size_t len,
+                           const AES_KEY *key, uint8_t ivec[16], unsigned *num,
+                           block128_f block);
 
 
 // CFB.
@@ -330,25 +341,25 @@
 // |len| be a multiple of any value and any partial blocks are stored in |ivec|
 // and |*num|, the latter must be zero before the initial call.
 void CRYPTO_cfb128_encrypt(const uint8_t *in, uint8_t *out, size_t len,
-                           const void *key, uint8_t ivec[16], unsigned *num,
+                           const AES_KEY *key, uint8_t ivec[16], unsigned *num,
                            int enc, block128_f block);
 
 // CRYPTO_cfb128_8_encrypt encrypts (or decrypts, if |enc| is zero) |len| bytes
 // from |in| to |out| using |block| in CFB-8 mode. Prior to the first call
 // |num| should be set to zero.
 void CRYPTO_cfb128_8_encrypt(const uint8_t *in, uint8_t *out, size_t len,
-                             const void *key, uint8_t ivec[16], unsigned *num,
-                             int enc, block128_f block);
+                             const AES_KEY *key, uint8_t ivec[16],
+                             unsigned *num, int enc, block128_f block);
 
 // CRYPTO_cfb128_1_encrypt encrypts (or decrypts, if |enc| is zero) |len| bytes
 // from |in| to |out| using |block| in CFB-1 mode. Prior to the first call
 // |num| should be set to zero.
 void CRYPTO_cfb128_1_encrypt(const uint8_t *in, uint8_t *out, size_t bits,
-                             const void *key, uint8_t ivec[16], unsigned *num,
-                             int enc, block128_f block);
+                             const AES_KEY *key, uint8_t ivec[16],
+                             unsigned *num, int enc, block128_f block);
 
 size_t CRYPTO_cts128_encrypt_block(const uint8_t *in, uint8_t *out, size_t len,
-                                   const void *key, uint8_t ivec[16],
+                                   const AES_KEY *key, uint8_t ivec[16],
                                    block128_f block);
 
 
diff --git a/src/crypto/fipsmodule/modes/ofb.c b/src/crypto/fipsmodule/modes/ofb.c
index 63bba68..b1b4d87 100644
--- a/src/crypto/fipsmodule/modes/ofb.c
+++ b/src/crypto/fipsmodule/modes/ofb.c
@@ -57,7 +57,7 @@
 OPENSSL_COMPILE_ASSERT((16 % sizeof(size_t)) == 0, bad_size_t_size_ofb);
 
 void CRYPTO_ofb128_encrypt(const uint8_t *in, uint8_t *out, size_t len,
-                           const void *key, uint8_t ivec[16], unsigned *num,
+                           const AES_KEY *key, uint8_t ivec[16], unsigned *num,
                            block128_f block) {
   assert(in && out && key && ivec && num);
 
diff --git a/src/crypto/fipsmodule/self_check/self_check.c b/src/crypto/fipsmodule/self_check/self_check.c
index 2c5b363..1bbefa9 100644
--- a/src/crypto/fipsmodule/self_check/self_check.c
+++ b/src/crypto/fipsmodule/self_check/self_check.c
@@ -394,6 +394,7 @@
   // AES-CBC Encryption KAT
   memcpy(aes_iv, kAESIV, sizeof(kAESIV));
   if (AES_set_encrypt_key(kAESKey, 8 * sizeof(kAESKey), &aes_key) != 0) {
+    fprintf(stderr, "AES_set_encrypt_key failed.\n");
     goto err;
   }
   AES_cbc_encrypt(kPlaintext, output, sizeof(kPlaintext), &aes_key, aes_iv,
@@ -406,6 +407,7 @@
   // AES-CBC Decryption KAT
   memcpy(aes_iv, kAESIV, sizeof(kAESIV));
   if (AES_set_decrypt_key(kAESKey, 8 * sizeof(kAESKey), &aes_key) != 0) {
+    fprintf(stderr, "AES_set_decrypt_key failed.\n");
     goto err;
   }
   AES_cbc_encrypt(kAESCBCCiphertext, output, sizeof(kAESCBCCiphertext),
@@ -420,6 +422,7 @@
   OPENSSL_memset(nonce, 0, sizeof(nonce));
   if (!EVP_AEAD_CTX_init(&aead_ctx, EVP_aead_aes_128_gcm(), kAESKey,
                          sizeof(kAESKey), 0, NULL)) {
+    fprintf(stderr, "EVP_AEAD_CTX_init for AES-128-GCM failed.\n");
     goto err;
   }
 
@@ -429,6 +432,7 @@
                          kPlaintext, sizeof(kPlaintext), NULL, 0) ||
       !check_test(kAESGCMCiphertext, output, sizeof(kAESGCMCiphertext),
                   "AES-GCM Encryption KAT")) {
+    fprintf(stderr, "EVP_AEAD_CTX_seal for AES-128-GCM failed.\n");
     goto err;
   }
 
@@ -439,6 +443,7 @@
                          0) ||
       !check_test(kPlaintext, output, sizeof(kPlaintext),
                   "AES-GCM Decryption KAT")) {
+    fprintf(stderr, "EVP_AEAD_CTX_open for AES-128-GCM failed.\n");
     goto err;
   }
 
@@ -504,6 +509,7 @@
                 &sig_len, rsa_key) ||
       !check_test(kRSASignature, output, sizeof(kRSASignature),
                   "RSA Sign KAT")) {
+    fprintf(stderr, "RSA signing test failed.\n");
     goto err;
   }
 
@@ -558,6 +564,7 @@
                          sizeof(kDRBGAD)) ||
       !check_test(kDRBGReseedOutput, output, sizeof(kDRBGReseedOutput),
                   "DRBG Reseed KAT")) {
+    fprintf(stderr, "CTR-DRBG failed.\n");
     goto err;
   }
   CTR_DRBG_clear(&drbg);
diff --git a/src/crypto/fipsmodule/sha/asm/sha1-armv8.pl b/src/crypto/fipsmodule/sha/asm/sha1-armv8.pl
index cec3669..80567d9 100644
--- a/src/crypto/fipsmodule/sha/asm/sha1-armv8.pl
+++ b/src/crypto/fipsmodule/sha/asm/sha1-armv8.pl
@@ -330,6 +330,7 @@
 .asciz	"SHA1 block transform for ARMv8, CRYPTOGAMS by <appro\@openssl.org>"
 .align	2
 .comm	OPENSSL_armcap_P,4,4
+.hidden	OPENSSL_armcap_P
 ___
 }}}
 
diff --git a/src/crypto/fipsmodule/sha/asm/sha512-armv8.pl b/src/crypto/fipsmodule/sha/asm/sha512-armv8.pl
index e3244f7..22c47d7 100644
--- a/src/crypto/fipsmodule/sha/asm/sha512-armv8.pl
+++ b/src/crypto/fipsmodule/sha/asm/sha512-armv8.pl
@@ -428,6 +428,7 @@
 $code.=<<___;
 #ifndef	__KERNEL__
 .comm	OPENSSL_armcap_P,4,4
+.hidden	OPENSSL_armcap_P
 #endif
 ___
 
diff --git a/src/crypto/internal.h b/src/crypto/internal.h
index 0e8ae3a..6944d56 100644
--- a/src/crypto/internal.h
+++ b/src/crypto/internal.h
@@ -132,13 +132,13 @@
 #endif
 #endif
 
-#if !defined(OPENSSL_NO_THREADS) && \
+#if defined(OPENSSL_THREADS) && \
     (!defined(OPENSSL_WINDOWS) || defined(__MINGW32__))
 #include <pthread.h>
 #define OPENSSL_PTHREADS
 #endif
 
-#if !defined(OPENSSL_NO_THREADS) && !defined(OPENSSL_PTHREADS) && \
+#if defined(OPENSSL_THREADS) && !defined(OPENSSL_PTHREADS) && \
     defined(OPENSSL_WINDOWS)
 #define OPENSSL_WINDOWS_THREADS
 OPENSSL_MSVC_PRAGMA(warning(push, 3))
@@ -367,7 +367,7 @@
 
 // Thread-safe initialisation.
 
-#if defined(OPENSSL_NO_THREADS)
+#if !defined(OPENSSL_THREADS)
 typedef uint32_t CRYPTO_once_t;
 #define CRYPTO_ONCE_INIT 0
 #elif defined(OPENSSL_WINDOWS_THREADS)
@@ -423,7 +423,7 @@
 // thread.h as a structure large enough to fit the real type. The global lock is
 // a different type so it may be initialized with platform initializer macros.
 
-#if defined(OPENSSL_NO_THREADS)
+#if !defined(OPENSSL_THREADS)
 struct CRYPTO_STATIC_MUTEX {
   char padding;  // Empty structs have different sizes in C and C++.
 };
diff --git a/src/crypto/pem/pem_info.c b/src/crypto/pem/pem_info.c
index d707e42..3627a45 100644
--- a/src/crypto/pem/pem_info.c
+++ b/src/crypto/pem/pem_info.c
@@ -75,221 +75,203 @@
 STACK_OF(X509_INFO) *PEM_X509_INFO_read(FILE *fp, STACK_OF(X509_INFO) *sk,
                                         pem_password_cb *cb, void *u)
 {
-    BIO *b;
-    STACK_OF(X509_INFO) *ret;
-
-    if ((b = BIO_new(BIO_s_file())) == NULL) {
+    BIO *b = BIO_new_fp(fp, BIO_NOCLOSE);
+    if (b == NULL) {
         OPENSSL_PUT_ERROR(PEM, ERR_R_BUF_LIB);
-        return (0);
+        return 0;
     }
-    BIO_set_fp(b, fp, BIO_NOCLOSE);
-    ret = PEM_X509_INFO_read_bio(b, sk, cb, u);
+    STACK_OF(X509_INFO) *ret = PEM_X509_INFO_read_bio(b, sk, cb, u);
     BIO_free(b);
-    return (ret);
+    return ret;
 }
 #endif
 
+enum parse_result_t {
+    parse_ok,
+    parse_error,
+    parse_new_entry,
+};
+
+static enum parse_result_t parse_x509(X509_INFO *info, const uint8_t *data,
+                                      size_t len, int key_type)
+{
+    if (info->x509 != NULL) {
+        return parse_new_entry;
+    }
+    info->x509 = d2i_X509(NULL, &data, len);
+    return info->x509 != NULL ? parse_ok : parse_error;
+}
+
+static enum parse_result_t parse_x509_aux(X509_INFO *info, const uint8_t *data,
+                                          size_t len, int key_type)
+{
+    if (info->x509 != NULL) {
+        return parse_new_entry;
+    }
+    info->x509 = d2i_X509_AUX(NULL, &data, len);
+    return info->x509 != NULL ? parse_ok : parse_error;
+}
+
+static enum parse_result_t parse_crl(X509_INFO *info, const uint8_t *data,
+                                     size_t len, int key_type)
+{
+    if (info->crl != NULL) {
+        return parse_new_entry;
+    }
+    info->crl = d2i_X509_CRL(NULL, &data, len);
+    return info->crl != NULL ? parse_ok : parse_error;
+}
+
+static enum parse_result_t parse_key(X509_INFO *info, const uint8_t *data,
+                                     size_t len, int key_type)
+{
+    if (info->x_pkey != NULL) {
+        return parse_new_entry;
+    }
+    info->x_pkey = X509_PKEY_new();
+    if (info->x_pkey == NULL) {
+        return parse_error;
+    }
+    info->x_pkey->dec_pkey = d2i_PrivateKey(key_type, NULL, &data, len);
+    return info->x_pkey->dec_pkey != NULL ? parse_ok : parse_error;
+}
+
 STACK_OF(X509_INFO) *PEM_X509_INFO_read_bio(BIO *bp, STACK_OF(X509_INFO) *sk,
                                             pem_password_cb *cb, void *u)
 {
-    X509_INFO *xi = NULL;
+    X509_INFO *info = NULL;
     char *name = NULL, *header = NULL;
-    void *pp;
     unsigned char *data = NULL;
-    const unsigned char *p;
-    long len, error = 0;
+    long len;
     int ok = 0;
     STACK_OF(X509_INFO) *ret = NULL;
-    unsigned int i, raw, ptype;
-    d2i_of_void *d2i = 0;
 
     if (sk == NULL) {
-        if ((ret = sk_X509_INFO_new_null()) == NULL) {
+        ret = sk_X509_INFO_new_null();
+        if (ret == NULL) {
             OPENSSL_PUT_ERROR(PEM, ERR_R_MALLOC_FAILURE);
-            goto err;
+            return NULL;
         }
-    } else
+    } else {
         ret = sk;
+    }
+    size_t orig_num = sk_X509_INFO_num(ret);
 
-    if ((xi = X509_INFO_new()) == NULL)
+    info = X509_INFO_new();
+    if (info == NULL) {
         goto err;
+    }
+
     for (;;) {
-        raw = 0;
-        ptype = 0;
-        i = PEM_read_bio(bp, &name, &header, &data, &len);
-        if (i == 0) {
-            error = ERR_GET_REASON(ERR_peek_last_error());
-            if (error == PEM_R_NO_START_LINE) {
+        if (!PEM_read_bio(bp, &name, &header, &data, &len)) {
+            uint32_t error = ERR_peek_last_error();
+            if (ERR_GET_LIB(error) == ERR_LIB_PEM &&
+                ERR_GET_REASON(error) == PEM_R_NO_START_LINE) {
                 ERR_clear_error();
                 break;
             }
             goto err;
         }
- start:
-        if ((strcmp(name, PEM_STRING_X509) == 0) ||
-            (strcmp(name, PEM_STRING_X509_OLD) == 0)) {
-            d2i = (D2I_OF(void)) d2i_X509;
-            if (xi->x509 != NULL) {
-                if (!sk_X509_INFO_push(ret, xi))
-                    goto err;
-                if ((xi = X509_INFO_new()) == NULL)
-                    goto err;
-                goto start;
-            }
-            pp = &(xi->x509);
-        } else if ((strcmp(name, PEM_STRING_X509_TRUSTED) == 0)) {
-            d2i = (D2I_OF(void)) d2i_X509_AUX;
-            if (xi->x509 != NULL) {
-                if (!sk_X509_INFO_push(ret, xi))
-                    goto err;
-                if ((xi = X509_INFO_new()) == NULL)
-                    goto err;
-                goto start;
-            }
-            pp = &(xi->x509);
+
+        enum parse_result_t (*parse_function)(X509_INFO *, const uint8_t *,
+                                              size_t, int) = NULL;
+        int key_type = EVP_PKEY_NONE;
+        if (strcmp(name, PEM_STRING_X509) == 0 ||
+            strcmp(name, PEM_STRING_X509_OLD) == 0) {
+            parse_function = parse_x509;
+        } else if (strcmp(name, PEM_STRING_X509_TRUSTED) == 0) {
+            parse_function = parse_x509_aux;
         } else if (strcmp(name, PEM_STRING_X509_CRL) == 0) {
-            d2i = (D2I_OF(void)) d2i_X509_CRL;
-            if (xi->crl != NULL) {
-                if (!sk_X509_INFO_push(ret, xi))
-                    goto err;
-                if ((xi = X509_INFO_new()) == NULL)
-                    goto err;
-                goto start;
-            }
-            pp = &(xi->crl);
+            parse_function = parse_crl;
         } else if (strcmp(name, PEM_STRING_RSA) == 0) {
-            d2i = (D2I_OF(void)) d2i_RSAPrivateKey;
-            if (xi->x_pkey != NULL) {
-                if (!sk_X509_INFO_push(ret, xi))
-                    goto err;
-                if ((xi = X509_INFO_new()) == NULL)
-                    goto err;
-                goto start;
-            }
-
-            xi->enc_data = NULL;
-            xi->enc_len = 0;
-
-            xi->x_pkey = X509_PKEY_new();
-            ptype = EVP_PKEY_RSA;
-            pp = &xi->x_pkey->dec_pkey;
-            if ((int)strlen(header) > 10) /* assume encrypted */
-                raw = 1;
-        } else
-#ifndef OPENSSL_NO_DSA
-        if (strcmp(name, PEM_STRING_DSA) == 0) {
-            d2i = (D2I_OF(void)) d2i_DSAPrivateKey;
-            if (xi->x_pkey != NULL) {
-                if (!sk_X509_INFO_push(ret, xi))
-                    goto err;
-                if ((xi = X509_INFO_new()) == NULL)
-                    goto err;
-                goto start;
-            }
-
-            xi->enc_data = NULL;
-            xi->enc_len = 0;
-
-            xi->x_pkey = X509_PKEY_new();
-            ptype = EVP_PKEY_DSA;
-            pp = &xi->x_pkey->dec_pkey;
-            if ((int)strlen(header) > 10) /* assume encrypted */
-                raw = 1;
-        } else
-#endif
-        if (strcmp(name, PEM_STRING_ECPRIVATEKEY) == 0) {
-            d2i = (D2I_OF(void)) d2i_ECPrivateKey;
-            if (xi->x_pkey != NULL) {
-                if (!sk_X509_INFO_push(ret, xi))
-                    goto err;
-                if ((xi = X509_INFO_new()) == NULL)
-                    goto err;
-                goto start;
-            }
-
-            xi->enc_data = NULL;
-            xi->enc_len = 0;
-
-            xi->x_pkey = X509_PKEY_new();
-            ptype = EVP_PKEY_EC;
-            pp = &xi->x_pkey->dec_pkey;
-            if ((int)strlen(header) > 10) /* assume encrypted */
-                raw = 1;
-        } else {
-            d2i = NULL;
-            pp = NULL;
+            parse_function = parse_key;
+            key_type = EVP_PKEY_RSA;
+        } else if (strcmp(name, PEM_STRING_DSA) == 0) {
+            parse_function = parse_key;
+            key_type = EVP_PKEY_DSA;
+        } else if (strcmp(name, PEM_STRING_ECPRIVATEKEY) == 0) {
+            parse_function = parse_key;
+            key_type = EVP_PKEY_EC;
         }
 
-        if (d2i != NULL) {
-            if (!raw) {
-                EVP_CIPHER_INFO cipher;
-
-                if (!PEM_get_EVP_CIPHER_INFO(header, &cipher))
-                    goto err;
-                if (!PEM_do_header(&cipher, data, &len, cb, u))
-                    goto err;
-                p = data;
-                if (ptype) {
-                    if (!d2i_PrivateKey(ptype, pp, &p, len)) {
-                        OPENSSL_PUT_ERROR(PEM, ERR_R_ASN1_LIB);
-                        goto err;
-                    }
-                } else if (d2i(pp, &p, len) == NULL) {
-                    OPENSSL_PUT_ERROR(PEM, ERR_R_ASN1_LIB);
+        /* If a private key has a header, assume it is encrypted. */
+        if (key_type != EVP_PKEY_NONE && strlen(header) > 10) {
+            if (info->x_pkey != NULL) {
+                if (!sk_X509_INFO_push(ret, info)) {
                     goto err;
                 }
-            } else {            /* encrypted RSA data */
-                if (!PEM_get_EVP_CIPHER_INFO(header, &xi->enc_cipher))
+                info = X509_INFO_new();
+                if (info == NULL) {
                     goto err;
-                xi->enc_data = (char *)data;
-                xi->enc_len = (int)len;
-                data = NULL;
+                }
             }
-        } else {
-            /* unknown */
+            /* Historically, raw entries pushed an empty key. */
+            info->x_pkey = X509_PKEY_new();
+            if (info->x_pkey == NULL ||
+                !PEM_get_EVP_CIPHER_INFO(header, &info->enc_cipher)) {
+                goto err;
+            }
+            info->enc_data = (char *)data;
+            info->enc_len = (int)len;
+            data = NULL;
+        } else if (parse_function != NULL) {
+            EVP_CIPHER_INFO cipher;
+            if (!PEM_get_EVP_CIPHER_INFO(header, &cipher) ||
+                !PEM_do_header(&cipher, data, &len, cb, u)) {
+                goto err;
+            }
+            enum parse_result_t result =
+                parse_function(info, data, len, key_type);
+            if (result == parse_new_entry) {
+                if (!sk_X509_INFO_push(ret, info)) {
+                    goto err;
+                }
+                info = X509_INFO_new();
+                if (info == NULL) {
+                    goto err;
+                }
+                result = parse_function(info, data, len, key_type);
+            }
+            if (result != parse_ok) {
+                OPENSSL_PUT_ERROR(PEM, ERR_R_ASN1_LIB);
+                goto err;
+            }
         }
-        if (name != NULL)
-            OPENSSL_free(name);
-        if (header != NULL)
-            OPENSSL_free(header);
-        if (data != NULL)
-            OPENSSL_free(data);
+        OPENSSL_free(name);
+        OPENSSL_free(header);
+        OPENSSL_free(data);
         name = NULL;
         header = NULL;
         data = NULL;
     }
 
-    /*
-     * if the last one hasn't been pushed yet and there is anything in it
-     * then add it to the stack ...
-     */
-    if ((xi->x509 != NULL) || (xi->crl != NULL) ||
-        (xi->x_pkey != NULL) || (xi->enc_data != NULL)) {
-        if (!sk_X509_INFO_push(ret, xi))
+    /* Push the last entry on the stack if not empty. */
+    if (info->x509 != NULL || info->crl != NULL ||
+        info->x_pkey != NULL || info->enc_data != NULL) {
+        if (!sk_X509_INFO_push(ret, info)) {
             goto err;
-        xi = NULL;
-    }
-    ok = 1;
- err:
-    if (xi != NULL)
-        X509_INFO_free(xi);
-    if (!ok) {
-        for (i = 0; i < sk_X509_INFO_num(ret); i++) {
-            xi = sk_X509_INFO_value(ret, i);
-            X509_INFO_free(xi);
         }
-        if (ret != sk)
+        info = NULL;
+    }
+
+    ok = 1;
+
+ err:
+    X509_INFO_free(info);
+    if (!ok) {
+        while (sk_X509_INFO_num(ret) > orig_num) {
+            X509_INFO_free(sk_X509_INFO_pop(ret));
+        }
+        if (ret != sk) {
             sk_X509_INFO_free(ret);
+        }
         ret = NULL;
     }
 
-    if (name != NULL)
-        OPENSSL_free(name);
-    if (header != NULL)
-        OPENSSL_free(header);
-    if (data != NULL)
-        OPENSSL_free(data);
-    return (ret);
+    OPENSSL_free(name);
+    OPENSSL_free(header);
+    OPENSSL_free(data);
+    return ret;
 }
 
 /* A TJH addition */
diff --git a/src/crypto/pem/pem_lib.c b/src/crypto/pem/pem_lib.c
index 5180e55..c682429 100644
--- a/src/crypto/pem/pem_lib.c
+++ b/src/crypto/pem/pem_lib.c
@@ -121,17 +121,14 @@
 void *PEM_ASN1_read(d2i_of_void *d2i, const char *name, FILE *fp, void **x,
                     pem_password_cb *cb, void *u)
 {
-    BIO *b;
-    void *ret;
-
-    if ((b = BIO_new(BIO_s_file())) == NULL) {
+    BIO *b = BIO_new_fp(fp, BIO_NOCLOSE);
+    if (b == NULL) {
         OPENSSL_PUT_ERROR(PEM, ERR_R_BUF_LIB);
-        return (0);
+        return NULL;
     }
-    BIO_set_fp(b, fp, BIO_NOCLOSE);
-    ret = PEM_ASN1_read_bio(d2i, name, b, x, cb, u);
+    void *ret = PEM_ASN1_read_bio(d2i, name, b, x, cb, u);
     BIO_free(b);
-    return (ret);
+    return ret;
 }
 #endif
 
@@ -220,8 +217,11 @@
 
     for (;;) {
         if (!PEM_read_bio(bp, &nm, &header, &data, &len)) {
-            if (ERR_GET_REASON(ERR_peek_error()) == PEM_R_NO_START_LINE)
+            uint32_t error = ERR_peek_error();
+            if (ERR_GET_LIB(error) == ERR_LIB_PEM &&
+                ERR_GET_REASON(error) == PEM_R_NO_START_LINE) {
                 ERR_add_error_data(2, "Expecting: ", name);
+            }
             return 0;
         }
         if (check_pem(nm, name))
@@ -257,17 +257,14 @@
                    void *x, const EVP_CIPHER *enc, unsigned char *kstr,
                    int klen, pem_password_cb *callback, void *u)
 {
-    BIO *b;
-    int ret;
-
-    if ((b = BIO_new(BIO_s_file())) == NULL) {
+    BIO *b = BIO_new_fp(fp, BIO_NOCLOSE);
+    if (b == NULL) {
         OPENSSL_PUT_ERROR(PEM, ERR_R_BUF_LIB);
-        return (0);
+        return 0;
     }
-    BIO_set_fp(b, fp, BIO_NOCLOSE);
-    ret = PEM_ASN1_write_bio(i2d, name, b, x, enc, kstr, klen, callback, u);
+    int ret = PEM_ASN1_write_bio(i2d, name, b, x, enc, kstr, klen, callback, u);
     BIO_free(b);
-    return (ret);
+    return ret;
 }
 #endif
 
@@ -514,15 +511,12 @@
 int PEM_write(FILE *fp, const char *name, const char *header,
               const unsigned char *data, long len)
 {
-    BIO *b;
-    int ret;
-
-    if ((b = BIO_new(BIO_s_file())) == NULL) {
+    BIO *b = BIO_new_fp(fp, BIO_NOCLOSE);
+    if (b == NULL) {
         OPENSSL_PUT_ERROR(PEM, ERR_R_BUF_LIB);
-        return (0);
+        return 0;
     }
-    BIO_set_fp(b, fp, BIO_NOCLOSE);
-    ret = PEM_write_bio(b, name, header, data, len);
+    int ret = PEM_write_bio(b, name, header, data, len);
     BIO_free(b);
     return (ret);
 }
@@ -588,15 +582,12 @@
 int PEM_read(FILE *fp, char **name, char **header, unsigned char **data,
              long *len)
 {
-    BIO *b;
-    int ret;
-
-    if ((b = BIO_new(BIO_s_file())) == NULL) {
+    BIO *b = BIO_new_fp(fp, BIO_NOCLOSE);
+    if (b == NULL) {
         OPENSSL_PUT_ERROR(PEM, ERR_R_BUF_LIB);
-        return (0);
+        return 0;
     }
-    BIO_set_fp(b, fp, BIO_NOCLOSE);
-    ret = PEM_read_bio(b, name, header, data, len);
+    int ret = PEM_read_bio(b, name, header, data, len);
     BIO_free(b);
     return (ret);
 }
diff --git a/src/crypto/pem/pem_pkey.c b/src/crypto/pem/pem_pkey.c
index 9fbaeef..725a84b 100644
--- a/src/crypto/pem/pem_pkey.c
+++ b/src/crypto/pem/pem_pkey.c
@@ -155,31 +155,26 @@
 EVP_PKEY *PEM_read_PrivateKey(FILE *fp, EVP_PKEY **x, pem_password_cb *cb,
                               void *u)
 {
-    BIO *b;
-    EVP_PKEY *ret;
-
-    if ((b = BIO_new(BIO_s_file())) == NULL) {
+    BIO *b = BIO_new_fp(fp, BIO_NOCLOSE);
+    if (b == NULL) {
         OPENSSL_PUT_ERROR(PEM, ERR_R_BUF_LIB);
-        return (0);
+        return NULL;
     }
-    BIO_set_fp(b, fp, BIO_NOCLOSE);
-    ret = PEM_read_bio_PrivateKey(b, x, cb, u);
+    EVP_PKEY *ret = PEM_read_bio_PrivateKey(b, x, cb, u);
     BIO_free(b);
-    return (ret);
+    return ret;
 }
 
 int PEM_write_PrivateKey(FILE *fp, EVP_PKEY *x, const EVP_CIPHER *enc,
                          unsigned char *kstr, int klen,
                          pem_password_cb *cb, void *u)
 {
-    BIO *b;
-    int ret;
-
-    if ((b = BIO_new_fp(fp, BIO_NOCLOSE)) == NULL) {
+    BIO *b = BIO_new_fp(fp, BIO_NOCLOSE);
+    if (b == NULL) {
         OPENSSL_PUT_ERROR(PEM, ERR_R_BUF_LIB);
         return 0;
     }
-    ret = PEM_write_bio_PrivateKey(b, x, enc, kstr, klen, cb, u);
+    int ret = PEM_write_bio_PrivateKey(b, x, enc, kstr, klen, cb, u);
     BIO_free(b);
     return ret;
 }
@@ -212,16 +207,13 @@
 #ifndef OPENSSL_NO_FP_API
 DH *PEM_read_DHparams(FILE *fp, DH **x, pem_password_cb *cb, void *u)
 {
-    BIO *b;
-    DH *ret;
-
-    if ((b = BIO_new(BIO_s_file())) == NULL) {
+    BIO *b = BIO_new_fp(fp, BIO_NOCLOSE);
+    if (b == NULL) {
         OPENSSL_PUT_ERROR(PEM, ERR_R_BUF_LIB);
-        return (0);
+        return NULL;
     }
-    BIO_set_fp(b, fp, BIO_NOCLOSE);
-    ret = PEM_read_bio_DHparams(b, x, cb, u);
+    DH *ret = PEM_read_bio_DHparams(b, x, cb, u);
     BIO_free(b);
-    return (ret);
+    return ret;
 }
 #endif
diff --git a/src/crypto/pkcs7/pkcs7_x509.c b/src/crypto/pkcs7/pkcs7_x509.c
index a2a6b46..d6ca44e 100644
--- a/src/crypto/pkcs7/pkcs7_x509.c
+++ b/src/crypto/pkcs7/pkcs7_x509.c
@@ -333,17 +333,7 @@
 }
 
 int i2d_PKCS7_bio(BIO *bio, const PKCS7 *p7) {
-  size_t written = 0;
-  while (written < p7->ber_len) {
-    size_t todo = p7->ber_len - written;
-    int len = todo > INT_MAX ? INT_MAX : (int)todo;
-    int ret = BIO_write(bio, p7->ber_bytes + written, len);
-    if (ret <= 0) {
-      return 0;
-    }
-    written += (size_t)ret;
-  }
-  return 1;
+  return BIO_write_all(bio, p7->ber_bytes, p7->ber_len);
 }
 
 void PKCS7_free(PKCS7 *p7) {
diff --git a/src/crypto/pkcs8/pkcs8_x509.c b/src/crypto/pkcs8/pkcs8_x509.c
index dc74d96..2c7841e 100644
--- a/src/crypto/pkcs8/pkcs8_x509.c
+++ b/src/crypto/pkcs8/pkcs8_x509.c
@@ -861,17 +861,7 @@
 }
 
 int i2d_PKCS12_bio(BIO *bio, const PKCS12 *p12) {
-  size_t written = 0;
-  while (written < p12->ber_len) {
-    size_t todo = p12->ber_len - written;
-    int len = todo > INT_MAX ? INT_MAX : (int)todo;
-    int ret = BIO_write(bio, p12->ber_bytes + written, len);
-    if (ret <= 0) {
-      return 0;
-    }
-    written += (size_t)ret;
-  }
-  return 1;
+  return BIO_write_all(bio, p12->ber_bytes, p12->ber_len);
 }
 
 int i2d_PKCS12_fp(FILE *fp, const PKCS12 *p12) {
diff --git a/src/crypto/pool/pool_test.cc b/src/crypto/pool/pool_test.cc
index 62fb17d..8f32fb6 100644
--- a/src/crypto/pool/pool_test.cc
+++ b/src/crypto/pool/pool_test.cc
@@ -18,7 +18,7 @@
 
 #include "../test/test_util.h"
 
-#if !defined(OPENSSL_NO_THREADS)
+#if defined(OPENSSL_THREADS)
 #include <chrono>
 #include <thread>
 #endif
@@ -61,7 +61,7 @@
   EXPECT_EQ(buf.get(), buf2.get()) << "CRYPTO_BUFFER_POOL did not dedup data.";
 }
 
-#if !defined(OPENSSL_NO_THREADS)
+#if defined(OPENSSL_THREADS)
 TEST(PoolTest, Threads) {
   bssl::UniquePtr<CRYPTO_BUFFER_POOL> pool(CRYPTO_BUFFER_POOL_new());
   ASSERT_TRUE(pool);
diff --git a/src/crypto/rand_extra/rand_test.cc b/src/crypto/rand_extra/rand_test.cc
index bd2eb18..d0a7e40 100644
--- a/src/crypto/rand_extra/rand_test.cc
+++ b/src/crypto/rand_extra/rand_test.cc
@@ -20,7 +20,7 @@
 
 #include "../test/test_util.h"
 
-#if !defined(OPENSSL_NO_THREADS)
+#if defined(OPENSSL_THREADS)
 #include <array>
 #include <thread>
 #include <vector>
@@ -50,7 +50,8 @@
   EXPECT_NE(Bytes(buf2), Bytes(kZeros));
 }
 
-#if !defined(OPENSSL_WINDOWS) && !defined(BORINGSSL_UNSAFE_DETERMINISTIC_MODE)
+#if !defined(OPENSSL_WINDOWS) && !defined(OPENSSL_IOS) && \
+    !defined(BORINGSSL_UNSAFE_DETERMINISTIC_MODE)
 static bool ForkAndRand(bssl::Span<uint8_t> out) {
   int pipefds[2];
   if (pipe(pipefds) < 0) {
@@ -144,9 +145,10 @@
   EXPECT_NE(Bytes(buf2), Bytes(kZeros));
   EXPECT_NE(Bytes(buf3), Bytes(kZeros));
 }
-#endif  // !OPENSSL_WINDOWS && !BORINGSSL_UNSAFE_DETERMINISTIC_MODE
+#endif  // !OPENSSL_WINDOWS && !OPENSSL_IOS &&
+        // !BORINGSSL_UNSAFE_DETERMINISTIC_MODE
 
-#if !defined(OPENSSL_NO_THREADS)
+#if defined(OPENSSL_THREADS)
 static void RunConcurrentRands(size_t num_threads) {
   static const uint8_t kZeros[256] = {0};
 
diff --git a/src/crypto/refcount_test.cc b/src/crypto/refcount_test.cc
index 6ce0746..700863f 100644
--- a/src/crypto/refcount_test.cc
+++ b/src/crypto/refcount_test.cc
@@ -16,7 +16,7 @@
 
 #include <gtest/gtest.h>
 
-#if !defined(OPENSSL_NO_THREADS)
+#if defined(OPENSSL_THREADS)
 #include <thread>
 #endif
 
@@ -43,7 +43,7 @@
   EXPECT_EQ(1u, count);
 }
 
-#if !defined(OPENSSL_NO_THREADS)
+#if defined(OPENSSL_THREADS)
 // This test is primarily intended to run under ThreadSanitizer.
 TEST(RefCountTest, Threads) {
   CRYPTO_refcount_t count = 0;
diff --git a/src/crypto/rsa_extra/rsa_test.cc b/src/crypto/rsa_extra/rsa_test.cc
index b0a0b7e..9bd47b1 100644
--- a/src/crypto/rsa_extra/rsa_test.cc
+++ b/src/crypto/rsa_extra/rsa_test.cc
@@ -72,7 +72,7 @@
 #include "../internal.h"
 #include "../test/test_util.h"
 
-#if !defined(OPENSSL_NO_THREADS)
+#if defined(OPENSSL_THREADS)
 #include <thread>
 #include <vector>
 #endif
@@ -1048,7 +1048,7 @@
 }
 #endif  // !BORINGSSL_SHARED_LIBRARY
 
-#if !defined(OPENSSL_NO_THREADS)
+#if defined(OPENSSL_THREADS)
 TEST(RSATest, Threads) {
   bssl::UniquePtr<RSA> rsa_template(
       RSA_private_key_from_bytes(kKey1, sizeof(kKey1) - 1));
diff --git a/src/crypto/stack/stack.c b/src/crypto/stack/stack.c
index 7aa3218..93b9d1b 100644
--- a/src/crypto/stack/stack.c
+++ b/src/crypto/stack/stack.c
@@ -133,19 +133,31 @@
   OPENSSL_free(sk);
 }
 
-void sk_pop_free(_STACK *sk, void (*func)(void *)) {
+void sk_pop_free_ex(_STACK *sk, void (*call_free_func)(stack_free_func, void *),
+                    stack_free_func free_func) {
   if (sk == NULL) {
     return;
   }
 
   for (size_t i = 0; i < sk->num; i++) {
     if (sk->data[i] != NULL) {
-      func(sk->data[i]);
+      call_free_func(free_func, sk->data[i]);
     }
   }
   sk_free(sk);
 }
 
+// Historically, |sk_pop_free| called the function as |stack_free_func|
+// directly. This is undefined in C. Some callers called |sk_pop_free| directly,
+// so we must maintain a compatibility version for now.
+static void call_free_func_legacy(stack_free_func func, void *ptr) {
+  func(ptr);
+}
+
+void sk_pop_free(_STACK *sk, stack_free_func free_func) {
+  sk_pop_free_ex(sk, call_free_func_legacy, free_func);
+}
+
 size_t sk_insert(_STACK *sk, void *p, size_t where) {
   if (sk == NULL) {
     return 0;
@@ -209,7 +221,7 @@
   return ret;
 }
 
-void *sk_delete_ptr(_STACK *sk, void *p) {
+void *sk_delete_ptr(_STACK *sk, const void *p) {
   if (sk == NULL) {
     return NULL;
   }
@@ -223,7 +235,9 @@
   return NULL;
 }
 
-int sk_find(const _STACK *sk, size_t *out_index, void *p) {
+int sk_find(const _STACK *sk, size_t *out_index, const void *p,
+            int (*call_cmp_func)(stack_cmp_func, const void **,
+                                 const void **)) {
   if (sk == NULL) {
     return 0;
   }
@@ -247,7 +261,8 @@
 
   if (!sk_is_sorted(sk)) {
     for (size_t i = 0; i < sk->num; i++) {
-      if (sk->comp((const void **)&p, (const void **)&sk->data[i]) == 0) {
+      const void *elem = sk->data[i];
+      if (call_cmp_func(sk->comp, &p, &elem) == 0) {
         if (out_index) {
           *out_index = i;
         }
@@ -262,15 +277,25 @@
   // elements. However, since we're passing an array of pointers to
   // qsort/bsearch, we can just cast the comparison function and everything
   // works.
+  //
+  // TODO(davidben): This is undefined behavior, but the call is in libc so,
+  // e.g., CFI does not notice. Unfortunately, |bsearch| is missing a void*
+  // parameter in its callback and |bsearch_s| is a mess of incompatibility.
   const void *const *r = bsearch(&p, sk->data, sk->num, sizeof(void *),
                                  (int (*)(const void *, const void *))sk->comp);
   if (r == NULL) {
     return 0;
   }
   size_t idx = ((void **)r) - sk->data;
-  // This function always returns the first result.
-  while (idx > 0 &&
-         sk->comp((const void **)&p, (const void **)&sk->data[idx - 1]) == 0) {
+  // This function always returns the first result. Note this logic is, in the
+  // worst case, O(N) rather than O(log(N)). If this ever becomes a problem,
+  // restore https://boringssl-review.googlesource.com/c/boringssl/+/32115/
+  // which integrates the preference into the binary search.
+  while (idx > 0) {
+    const void *elem = sk->data[idx - 1];
+    if (call_cmp_func(sk->comp, &p, &elem) != 0) {
+      break;
+    }
     idx--;
   }
   if (out_index) {
@@ -340,6 +365,11 @@
   }
 
   // See the comment in sk_find about this cast.
+  //
+  // TODO(davidben): This is undefined behavior, but the call is in libc so,
+  // e.g., CFI does not notice. Unfortunately, |qsort| is missing a void*
+  // parameter in its callback and |qsort_s| / |qsort_r| are a mess of
+  // incompatibility.
   comp_func = (int (*)(const void *, const void *))(sk->comp);
   qsort(sk->data, sk->num, sizeof(void *), comp_func);
   sk->sorted = 1;
@@ -363,8 +393,11 @@
   return old;
 }
 
-_STACK *sk_deep_copy(const _STACK *sk, void *(*copy_func)(void *),
-                     void (*free_func)(void *)) {
+_STACK *sk_deep_copy(const _STACK *sk,
+                     void *(*call_copy_func)(stack_copy_func, void *),
+                     stack_copy_func copy_func,
+                     void (*call_free_func)(stack_free_func, void *),
+                     stack_free_func free_func) {
   _STACK *ret = sk_dup(sk);
   if (ret == NULL) {
     return NULL;
@@ -374,11 +407,11 @@
     if (ret->data[i] == NULL) {
       continue;
     }
-    ret->data[i] = copy_func(ret->data[i]);
+    ret->data[i] = call_copy_func(copy_func, ret->data[i]);
     if (ret->data[i] == NULL) {
       for (size_t j = 0; j < i; j++) {
         if (ret->data[j] != NULL) {
-          free_func(ret->data[j]);
+          call_free_func(free_func, ret->data[j]);
         }
       }
       sk_free(ret);
diff --git a/src/crypto/stack/stack_test.cc b/src/crypto/stack/stack_test.cc
new file mode 100644
index 0000000..8b26971
--- /dev/null
+++ b/src/crypto/stack/stack_test.cc
@@ -0,0 +1,394 @@
+/* Copyright (c) 2018, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+#include <openssl/stack.h>
+
+#include <limits.h>
+
+#include <algorithm>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <openssl/mem.h>
+
+
+// Define a custom stack type for testing.
+using TEST_INT = int;
+
+static void TEST_INT_free(TEST_INT *x) { OPENSSL_free(x); }
+
+namespace bssl {
+BORINGSSL_MAKE_DELETER(TEST_INT, TEST_INT_free)
+}  // namespace bssl
+
+static bssl::UniquePtr<TEST_INT> TEST_INT_new(int x) {
+  bssl::UniquePtr<TEST_INT> ret(
+      static_cast<TEST_INT *>(OPENSSL_malloc(sizeof(TEST_INT))));
+  if (!ret) {
+    return nullptr;
+  }
+  *ret = x;
+  return ret;
+}
+
+DEFINE_STACK_OF(TEST_INT)
+
+struct ShallowStackDeleter {
+  void operator()(STACK_OF(TEST_INT) *sk) const { sk_TEST_INT_free(sk); }
+};
+
+using ShallowStack = std::unique_ptr<STACK_OF(TEST_INT), ShallowStackDeleter>;
+
+// kNull is treated as a nullptr expectation for purposes of ExpectStackEquals.
+// The tests in this file will never use it as a test value.
+static const int kNull = INT_MIN;
+
+static void ExpectStackEquals(const STACK_OF(TEST_INT) *sk,
+                              const std::vector<int> &vec) {
+  EXPECT_EQ(vec.size(), sk_TEST_INT_num(sk));
+  for (size_t i = 0; i < vec.size(); i++) {
+    SCOPED_TRACE(i);
+    const TEST_INT *obj = sk_TEST_INT_value(sk, i);
+    if (vec[i] == kNull) {
+      EXPECT_FALSE(obj);
+    } else {
+      EXPECT_TRUE(obj);
+      if (obj) {
+        EXPECT_EQ(vec[i], *obj);
+      }
+    }
+  }
+
+  // Reading out-of-bounds fails.
+  EXPECT_FALSE(sk_TEST_INT_value(sk, vec.size()));
+  EXPECT_FALSE(sk_TEST_INT_value(sk, vec.size() + 1));
+}
+
+TEST(StackTest, Basic) {
+  bssl::UniquePtr<STACK_OF(TEST_INT)> sk(sk_TEST_INT_new_null());
+  ASSERT_TRUE(sk);
+
+  // The stack starts out empty.
+  ExpectStackEquals(sk.get(), {});
+
+  // Removing elements from an empty stack does nothing.
+  EXPECT_FALSE(sk_TEST_INT_pop(sk.get()));
+  EXPECT_FALSE(sk_TEST_INT_shift(sk.get()));
+  EXPECT_FALSE(sk_TEST_INT_delete(sk.get(), 0));
+
+  // Push some elements.
+  for (int i = 0; i < 6; i++) {
+    auto value = TEST_INT_new(i);
+    ASSERT_TRUE(value);
+    ASSERT_TRUE(bssl::PushToStack(sk.get(), std::move(value)));
+  }
+
+  ExpectStackEquals(sk.get(), {0, 1, 2, 3, 4, 5});
+
+  // Items may be inserted in the middle.
+  auto value = TEST_INT_new(6);
+  ASSERT_TRUE(value);
+  // Hold on to the object for later.
+  TEST_INT *raw = value.get();
+  ASSERT_TRUE(sk_TEST_INT_insert(sk.get(), value.get(), 4));
+  value.release();  // sk_TEST_INT_insert takes ownership on success.
+
+  ExpectStackEquals(sk.get(), {0, 1, 2, 3, 6, 4, 5});
+
+  // Without a comparison function, find searches by pointer.
+  value = TEST_INT_new(6);
+  ASSERT_TRUE(value);
+  size_t index;
+  EXPECT_FALSE(sk_TEST_INT_find(sk.get(), &index, value.get()));
+  ASSERT_TRUE(sk_TEST_INT_find(sk.get(), &index, raw));
+  EXPECT_EQ(4u, index);
+
+  // sk_TEST_INT_insert can also insert values at the end.
+  value = TEST_INT_new(7);
+  ASSERT_TRUE(value);
+  ASSERT_TRUE(sk_TEST_INT_insert(sk.get(), value.get(), 7));
+  value.release();  // sk_TEST_INT_insert takes ownership on success.
+
+  ExpectStackEquals(sk.get(), {0, 1, 2, 3, 6, 4, 5, 7});
+
+  // Out-of-bounds indices are clamped.
+  value = TEST_INT_new(8);
+  ASSERT_TRUE(value);
+  ASSERT_TRUE(sk_TEST_INT_insert(sk.get(), value.get(), 999));
+  value.release();  // sk_TEST_INT_insert takes ownership on success.
+
+  ExpectStackEquals(sk.get(), {0, 1, 2, 3, 6, 4, 5, 7, 8});
+
+  // Test removing elements from various places.
+  bssl::UniquePtr<TEST_INT> removed(sk_TEST_INT_pop(sk.get()));
+  EXPECT_EQ(8, *removed);
+  ExpectStackEquals(sk.get(), {0, 1, 2, 3, 6, 4, 5, 7});
+
+  removed.reset(sk_TEST_INT_shift(sk.get()));
+  EXPECT_EQ(0, *removed);
+  ExpectStackEquals(sk.get(), {1, 2, 3, 6, 4, 5, 7});
+
+  removed.reset(sk_TEST_INT_delete(sk.get(), 2));
+  EXPECT_EQ(3, *removed);
+  ExpectStackEquals(sk.get(), {1, 2, 6, 4, 5, 7});
+
+  // Objects may also be deleted by pointer.
+  removed.reset(sk_TEST_INT_delete_ptr(sk.get(), raw));
+  EXPECT_EQ(raw, removed.get());
+  ExpectStackEquals(sk.get(), {1, 2, 4, 5, 7});
+
+  // Deleting is a no-op is the object is not found.
+  value = TEST_INT_new(100);
+  ASSERT_TRUE(value);
+  EXPECT_FALSE(sk_TEST_INT_delete_ptr(sk.get(), value.get()));
+
+  // Insert nullptr to test deep copy handling of it.
+  ASSERT_TRUE(sk_TEST_INT_insert(sk.get(), nullptr, 0));
+  ExpectStackEquals(sk.get(), {kNull, 1, 2, 4, 5, 7});
+
+  // Test both deep and shallow copies.
+  bssl::UniquePtr<STACK_OF(TEST_INT)> copy(sk_TEST_INT_deep_copy(
+      sk.get(),
+      [](TEST_INT *x) -> TEST_INT * {
+        return x == nullptr ? nullptr : TEST_INT_new(*x).release();
+      },
+      TEST_INT_free));
+  ASSERT_TRUE(copy);
+  ExpectStackEquals(copy.get(), {kNull, 1, 2, 4, 5, 7});
+
+  ShallowStack shallow(sk_TEST_INT_dup(sk.get()));
+  ASSERT_TRUE(shallow);
+  ASSERT_EQ(sk_TEST_INT_num(sk.get()), sk_TEST_INT_num(shallow.get()));
+  for (size_t i = 0; i < sk_TEST_INT_num(sk.get()); i++) {
+    EXPECT_EQ(sk_TEST_INT_value(sk.get(), i),
+              sk_TEST_INT_value(shallow.get(), i));
+  }
+
+  // Deep copies may fail. This should clean up temporaries.
+  EXPECT_FALSE(sk_TEST_INT_deep_copy(sk.get(),
+                                     [](TEST_INT *x) -> TEST_INT * {
+                                       return x == nullptr || *x == 4
+                                                  ? nullptr
+                                                  : TEST_INT_new(*x).release();
+                                     },
+                                     TEST_INT_free));
+
+  // sk_TEST_INT_zero clears a stack, but does not free the elements.
+  ShallowStack shallow2(sk_TEST_INT_dup(sk.get()));
+  ASSERT_TRUE(shallow2);
+  sk_TEST_INT_zero(shallow2.get());
+  ExpectStackEquals(shallow2.get(), {});
+}
+
+TEST(StackTest, BigStack) {
+  bssl::UniquePtr<STACK_OF(TEST_INT)> sk(sk_TEST_INT_new_null());
+  ASSERT_TRUE(sk);
+
+  std::vector<int> expected;
+  static const int kCount = 100000;
+  for (int i = 0; i < kCount; i++) {
+    auto value = TEST_INT_new(i);
+    ASSERT_TRUE(value);
+    ASSERT_TRUE(bssl::PushToStack(sk.get(), std::move(value)));
+    expected.push_back(i);
+  }
+  ExpectStackEquals(sk.get(), expected);
+}
+
+static uint64_t g_compare_count = 0;
+
+static int compare(const TEST_INT **a, const TEST_INT **b) {
+  g_compare_count++;
+  if (**a < **b) {
+    return -1;
+  }
+  if (**a > **b) {
+    return 1;
+  }
+  return 0;
+}
+
+static int compare_reverse(const TEST_INT **a, const TEST_INT **b) {
+  return -compare(a, b);
+}
+
+TEST(StackTest, Sorted) {
+  std::vector<int> vec_sorted = {0, 1, 2, 3, 4, 5, 6};
+  std::vector<int> vec = vec_sorted;
+  do {
+    bssl::UniquePtr<STACK_OF(TEST_INT)> sk(sk_TEST_INT_new(compare));
+    ASSERT_TRUE(sk);
+    for (int v : vec) {
+      auto value = TEST_INT_new(v);
+      ASSERT_TRUE(value);
+      ASSERT_TRUE(bssl::PushToStack(sk.get(), std::move(value)));
+    }
+
+    // The stack is not (known to be) sorted.
+    EXPECT_FALSE(sk_TEST_INT_is_sorted(sk.get()));
+
+    // With a comparison function, find matches by value.
+    auto ten = TEST_INT_new(10);
+    ASSERT_TRUE(ten);
+    size_t index;
+    EXPECT_FALSE(sk_TEST_INT_find(sk.get(), &index, ten.get()));
+
+    auto three = TEST_INT_new(3);
+    ASSERT_TRUE(three);
+    ASSERT_TRUE(sk_TEST_INT_find(sk.get(), &index, three.get()));
+    EXPECT_EQ(3, *sk_TEST_INT_value(sk.get(), index));
+
+    sk_TEST_INT_sort(sk.get());
+    EXPECT_TRUE(sk_TEST_INT_is_sorted(sk.get()));
+    ExpectStackEquals(sk.get(), vec_sorted);
+
+    // Sorting an already-sorted list is a no-op.
+    uint64_t old_compare_count = g_compare_count;
+    sk_TEST_INT_sort(sk.get());
+    EXPECT_EQ(old_compare_count, g_compare_count);
+    EXPECT_TRUE(sk_TEST_INT_is_sorted(sk.get()));
+    ExpectStackEquals(sk.get(), vec_sorted);
+
+    // When sorted, find uses binary search.
+    ASSERT_TRUE(ten);
+    EXPECT_FALSE(sk_TEST_INT_find(sk.get(), &index, ten.get()));
+
+    ASSERT_TRUE(three);
+    ASSERT_TRUE(sk_TEST_INT_find(sk.get(), &index, three.get()));
+    EXPECT_EQ(3u, index);
+
+    // Copies preserve comparison and sorted information.
+    bssl::UniquePtr<STACK_OF(TEST_INT)> copy(sk_TEST_INT_deep_copy(
+        sk.get(),
+        [](TEST_INT *x) -> TEST_INT * { return TEST_INT_new(*x).release(); },
+        TEST_INT_free));
+    ASSERT_TRUE(copy);
+    EXPECT_TRUE(sk_TEST_INT_is_sorted(copy.get()));
+    ASSERT_TRUE(sk_TEST_INT_find(copy.get(), &index, three.get()));
+    EXPECT_EQ(3u, index);
+
+    ShallowStack copy2(sk_TEST_INT_dup(sk.get()));
+    ASSERT_TRUE(copy2);
+    EXPECT_TRUE(sk_TEST_INT_is_sorted(copy2.get()));
+    ASSERT_TRUE(sk_TEST_INT_find(copy2.get(), &index, three.get()));
+    EXPECT_EQ(3u, index);
+
+    // Removing elements does not affect sortedness.
+    TEST_INT_free(sk_TEST_INT_delete(sk.get(), 0));
+    EXPECT_TRUE(sk_TEST_INT_is_sorted(sk.get()));
+
+    // Changing the comparison function invalidates sortedness.
+    sk_TEST_INT_set_cmp_func(sk.get(), compare_reverse);
+    EXPECT_FALSE(sk_TEST_INT_is_sorted(sk.get()));
+    ASSERT_TRUE(sk_TEST_INT_find(sk.get(), &index, three.get()));
+    EXPECT_EQ(2u, index);
+
+    sk_TEST_INT_sort(sk.get());
+    ExpectStackEquals(sk.get(), {6, 5, 4, 3, 2, 1});
+    ASSERT_TRUE(sk_TEST_INT_find(sk.get(), &index, three.get()));
+    EXPECT_EQ(3u, index);
+
+    // Inserting a new element invalidates sortedness.
+    auto tmp = TEST_INT_new(10);
+    ASSERT_TRUE(tmp);
+    ASSERT_TRUE(bssl::PushToStack(sk.get(), std::move(tmp)));
+    EXPECT_FALSE(sk_TEST_INT_is_sorted(sk.get()));
+    ASSERT_TRUE(sk_TEST_INT_find(sk.get(), &index, ten.get()));
+    EXPECT_EQ(6u, index);
+  } while (std::next_permutation(vec.begin(), vec.end()));
+}
+
+// sk_*_find should return the first matching element in all cases.
+TEST(StackTest, FindFirst) {
+  bssl::UniquePtr<STACK_OF(TEST_INT)> sk(sk_TEST_INT_new(compare));
+  auto value = TEST_INT_new(1);
+  ASSERT_TRUE(value);
+  ASSERT_TRUE(bssl::PushToStack(sk.get(), std::move(value)));
+  for (int i = 0; i < 10; i++) {
+    value = TEST_INT_new(2);
+    ASSERT_TRUE(value);
+    ASSERT_TRUE(bssl::PushToStack(sk.get(), std::move(value)));
+  }
+
+  const TEST_INT *two = sk_TEST_INT_value(sk.get(), 1);
+  // Pointer-based equality.
+  size_t index;
+  ASSERT_TRUE(sk_TEST_INT_find(sk.get(), &index, two));
+  EXPECT_EQ(1u, index);
+
+  // Comparator-based equality, unsorted.
+  sk_TEST_INT_set_cmp_func(sk.get(), compare);
+  EXPECT_FALSE(sk_TEST_INT_is_sorted(sk.get()));
+  ASSERT_TRUE(sk_TEST_INT_find(sk.get(), &index, two));
+  EXPECT_EQ(1u, index);
+
+  // Comparator-based equality, sorted.
+  sk_TEST_INT_sort(sk.get());
+  EXPECT_TRUE(sk_TEST_INT_is_sorted(sk.get()));
+  ASSERT_TRUE(sk_TEST_INT_find(sk.get(), &index, two));
+  EXPECT_EQ(1u, index);
+
+  // Comparator-based equality, sorted and at the front.
+  sk_TEST_INT_set_cmp_func(sk.get(), compare_reverse);
+  sk_TEST_INT_sort(sk.get());
+  EXPECT_TRUE(sk_TEST_INT_is_sorted(sk.get()));
+  ASSERT_TRUE(sk_TEST_INT_find(sk.get(), &index, two));
+  EXPECT_EQ(0u, index);
+}
+
+// Exhaustively test the binary search.
+TEST(StackTest, BinarySearch) {
+  static const size_t kCount = 100;
+  for (size_t i = 0; i < kCount; i++) {
+    SCOPED_TRACE(i);
+    for (size_t j = i; j <= kCount; j++) {
+      SCOPED_TRACE(j);
+      // Make a stack where [0, i) are below, [i, j) match, and [j, kCount) are
+      // above.
+      bssl::UniquePtr<STACK_OF(TEST_INT)> sk(sk_TEST_INT_new(compare));
+      ASSERT_TRUE(sk);
+      for (size_t k = 0; k < i; k++) {
+        auto value = TEST_INT_new(-1);
+        ASSERT_TRUE(value);
+        ASSERT_TRUE(bssl::PushToStack(sk.get(), std::move(value)));
+      }
+      for (size_t k = i; k < j; k++) {
+        auto value = TEST_INT_new(0);
+        ASSERT_TRUE(value);
+        ASSERT_TRUE(bssl::PushToStack(sk.get(), std::move(value)));
+      }
+      for (size_t k = j; k < kCount; k++) {
+        auto value = TEST_INT_new(1);
+        ASSERT_TRUE(value);
+        ASSERT_TRUE(bssl::PushToStack(sk.get(), std::move(value)));
+      }
+      sk_TEST_INT_sort(sk.get());
+
+      auto key = TEST_INT_new(0);
+      ASSERT_TRUE(key);
+
+      size_t idx;
+      int found = sk_TEST_INT_find(sk.get(), &idx, key.get());
+      if (i == j) {
+        EXPECT_FALSE(found);
+      } else {
+        ASSERT_TRUE(found);
+        EXPECT_EQ(i, idx);
+      }
+    }
+  }
+}
diff --git a/src/crypto/thread_none.c b/src/crypto/thread_none.c
index 718d960..4f07b9d 100644
--- a/src/crypto/thread_none.c
+++ b/src/crypto/thread_none.c
@@ -14,7 +14,7 @@
 
 #include "internal.h"
 
-#if defined(OPENSSL_NO_THREADS)
+#if !defined(OPENSSL_THREADS)
 
 void CRYPTO_MUTEX_init(CRYPTO_MUTEX *lock) {}
 
@@ -56,4 +56,4 @@
   return 1;
 }
 
-#endif  // OPENSSL_NO_THREADS
+#endif  // !OPENSSL_THREADS
diff --git a/src/crypto/thread_test.cc b/src/crypto/thread_test.cc
index f89b22e..f9fad9b 100644
--- a/src/crypto/thread_test.cc
+++ b/src/crypto/thread_test.cc
@@ -25,7 +25,7 @@
 #include "test/test_util.h"
 
 
-#if !defined(OPENSSL_NO_THREADS)
+#if defined(OPENSSL_THREADS)
 
 static unsigned g_once_init_called = 0;
 
@@ -130,4 +130,4 @@
   thread.join();
 }
 
-#endif  // !OPENSSL_NO_THREADS
+#endif  // OPENSSL_THREADS
diff --git a/src/crypto/x509/by_file.c b/src/crypto/x509/by_file.c
index 555cb85..dfff425 100644
--- a/src/crypto/x509/by_file.c
+++ b/src/crypto/x509/by_file.c
@@ -138,14 +138,15 @@
         for (;;) {
             x = PEM_read_bio_X509_AUX(in, NULL, NULL, NULL);
             if (x == NULL) {
-                if ((ERR_GET_REASON(ERR_peek_last_error()) ==
-                     PEM_R_NO_START_LINE) && (count > 0)) {
+                uint32_t error = ERR_peek_last_error();
+                if (ERR_GET_LIB(error) == ERR_LIB_PEM &&
+                    ERR_GET_REASON(error) == PEM_R_NO_START_LINE &&
+                    count > 0) {
                     ERR_clear_error();
                     break;
-                } else {
-                    OPENSSL_PUT_ERROR(X509, ERR_R_PEM_LIB);
-                    goto err;
                 }
+                OPENSSL_PUT_ERROR(X509, ERR_R_PEM_LIB);
+                goto err;
             }
             i = X509_STORE_add_cert(ctx->store_ctx, x);
             if (!i)
@@ -197,14 +198,15 @@
         for (;;) {
             x = PEM_read_bio_X509_CRL(in, NULL, NULL, NULL);
             if (x == NULL) {
-                if ((ERR_GET_REASON(ERR_peek_last_error()) ==
-                     PEM_R_NO_START_LINE) && (count > 0)) {
+                uint32_t error = ERR_peek_last_error();
+                if (ERR_GET_LIB(error) == ERR_LIB_PEM &&
+                    ERR_GET_REASON(error) == PEM_R_NO_START_LINE &&
+                    count > 0) {
                     ERR_clear_error();
                     break;
-                } else {
-                    OPENSSL_PUT_ERROR(X509, ERR_R_PEM_LIB);
-                    goto err;
                 }
+                OPENSSL_PUT_ERROR(X509, ERR_R_PEM_LIB);
+                goto err;
             }
             i = X509_STORE_add_crl(ctx->store_ctx, x);
             if (!i)
diff --git a/src/crypto/x509/t_crl.c b/src/crypto/x509/t_crl.c
index 6c347cb..dc9b87f 100644
--- a/src/crypto/x509/t_crl.c
+++ b/src/crypto/x509/t_crl.c
@@ -64,17 +64,14 @@
 #ifndef OPENSSL_NO_FP_API
 int X509_CRL_print_fp(FILE *fp, X509_CRL *x)
 {
-    BIO *b;
-    int ret;
-
-    if ((b = BIO_new(BIO_s_file())) == NULL) {
+    BIO *b = BIO_new_fp(fp, BIO_NOCLOSE);
+    if (b == NULL) {
         OPENSSL_PUT_ERROR(X509, ERR_R_BUF_LIB);
-        return (0);
+        return 0;
     }
-    BIO_set_fp(b, fp, BIO_NOCLOSE);
-    ret = X509_CRL_print(b, x);
+    int ret = X509_CRL_print(b, x);
     BIO_free(b);
-    return (ret);
+    return ret;
 }
 #endif
 
diff --git a/src/crypto/x509/t_req.c b/src/crypto/x509/t_req.c
index 39c836c..2fd36f8 100644
--- a/src/crypto/x509/t_req.c
+++ b/src/crypto/x509/t_req.c
@@ -65,13 +65,11 @@
 
 
 int X509_REQ_print_fp(FILE *fp, X509_REQ *x) {
-  BIO *bio = BIO_new(BIO_s_file());
+  BIO *bio = BIO_new_fp(fp, BIO_NOCLOSE);
   if (bio == NULL) {
     OPENSSL_PUT_ERROR(X509, ERR_R_BUF_LIB);
     return 0;
   }
-
-  BIO_set_fp(bio, fp, BIO_NOCLOSE);
   int ret = X509_REQ_print(bio, x);
   BIO_free(bio);
   return ret;
diff --git a/src/crypto/x509/t_x509.c b/src/crypto/x509/t_x509.c
index 3339523..e45a765 100644
--- a/src/crypto/x509/t_x509.c
+++ b/src/crypto/x509/t_x509.c
@@ -72,17 +72,14 @@
 int X509_print_ex_fp(FILE *fp, X509 *x, unsigned long nmflag,
                      unsigned long cflag)
 {
-    BIO *b;
-    int ret;
-
-    if ((b = BIO_new(BIO_s_file())) == NULL) {
+    BIO *b = BIO_new_fp(fp, BIO_NOCLOSE);
+    if (b == NULL) {
         OPENSSL_PUT_ERROR(X509, ERR_R_BUF_LIB);
-        return (0);
+        return 0;
     }
-    BIO_set_fp(b, fp, BIO_NOCLOSE);
-    ret = X509_print_ex(b, x, nmflag, cflag);
+    int ret = X509_print_ex(b, x, nmflag, cflag);
     BIO_free(b);
-    return (ret);
+    return ret;
 }
 
 int X509_print_fp(FILE *fp, X509 *x)
diff --git a/src/crypto/x509/x509_test.cc b/src/crypto/x509/x509_test.cc
index 9ed1b52..bf0b29a 100644
--- a/src/crypto/x509/x509_test.cc
+++ b/src/crypto/x509/x509_test.cc
@@ -1549,3 +1549,125 @@
   EXPECT_EQ(ERR_LIB_X509, ERR_GET_LIB(err));
   EXPECT_EQ(X509_R_SIGNATURE_ALGORITHM_MISMATCH, ERR_GET_REASON(err));
 }
+
+TEST(X509Test, PEMX509Info) {
+  std::string cert = kRootCAPEM;
+  auto cert_obj = CertFromPEM(kRootCAPEM);
+  ASSERT_TRUE(cert_obj);
+
+  std::string rsa = kRSAKey;
+  auto rsa_obj = PrivateKeyFromPEM(kRSAKey);
+  ASSERT_TRUE(rsa_obj);
+
+  std::string crl = kBasicCRL;
+  auto crl_obj = CRLFromPEM(kBasicCRL);
+  ASSERT_TRUE(crl_obj);
+
+  std::string unknown =
+      "-----BEGIN UNKNOWN-----\n"
+      "AAAA\n"
+      "-----END UNKNOWN-----\n";
+
+  std::string invalid =
+      "-----BEGIN CERTIFICATE-----\n"
+      "AAAA\n"
+      "-----END CERTIFICATE-----\n";
+
+  // Each X509_INFO contains at most one certificate, CRL, etc. The format
+  // creates a new X509_INFO when a repeated type is seen.
+  std::string pem =
+      // The first few entries have one of everything in different orders.
+      cert + rsa + crl +
+      rsa + crl + cert +
+      // Unknown types are ignored.
+      crl + unknown + cert + rsa +
+      // Seeing a new certificate starts a new entry, so now we have a bunch of
+      // certificate-only entries.
+      cert + cert + cert +
+      // The key folds into the certificate's entry.
+      cert + rsa +
+      // Doubled keys also start new entries.
+      rsa + rsa + rsa + rsa + crl +
+      // As do CRLs.
+      crl + crl;
+
+  const struct ExpectedInfo {
+    const X509 *cert;
+    const EVP_PKEY *key;
+    const X509_CRL *crl;
+  } kExpected[] = {
+    {cert_obj.get(), rsa_obj.get(), crl_obj.get()},
+    {cert_obj.get(), rsa_obj.get(), crl_obj.get()},
+    {cert_obj.get(), rsa_obj.get(), crl_obj.get()},
+    {cert_obj.get(), nullptr, nullptr},
+    {cert_obj.get(), nullptr, nullptr},
+    {cert_obj.get(), nullptr, nullptr},
+    {cert_obj.get(), rsa_obj.get(), nullptr},
+    {nullptr, rsa_obj.get(), nullptr},
+    {nullptr, rsa_obj.get(), nullptr},
+    {nullptr, rsa_obj.get(), nullptr},
+    {nullptr, rsa_obj.get(), crl_obj.get()},
+    {nullptr, nullptr, crl_obj.get()},
+    {nullptr, nullptr, crl_obj.get()},
+  };
+
+  auto check_info = [](const ExpectedInfo *expected, const X509_INFO *info) {
+    if (expected->cert != nullptr) {
+      EXPECT_EQ(0, X509_cmp(expected->cert, info->x509));
+    } else {
+      EXPECT_EQ(nullptr, info->x509);
+    }
+    if (expected->crl != nullptr) {
+      EXPECT_EQ(0, X509_CRL_cmp(expected->crl, info->crl));
+    } else {
+      EXPECT_EQ(nullptr, info->crl);
+    }
+    if (expected->key != nullptr) {
+      ASSERT_NE(nullptr, info->x_pkey);
+      // EVP_PKEY_cmp returns one if the keys are equal.
+      EXPECT_EQ(1, EVP_PKEY_cmp(expected->key, info->x_pkey->dec_pkey));
+    } else {
+      EXPECT_EQ(nullptr, info->x_pkey);
+    }
+  };
+
+  bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(pem.data(), pem.size()));
+  ASSERT_TRUE(bio);
+  bssl::UniquePtr<STACK_OF(X509_INFO)> infos(
+      PEM_X509_INFO_read_bio(bio.get(), nullptr, nullptr, nullptr));
+  ASSERT_TRUE(infos);
+  ASSERT_EQ(OPENSSL_ARRAY_SIZE(kExpected), sk_X509_INFO_num(infos.get()));
+  for (size_t i = 0; i < OPENSSL_ARRAY_SIZE(kExpected); i++) {
+    SCOPED_TRACE(i);
+    check_info(&kExpected[i], sk_X509_INFO_value(infos.get(), i));
+  }
+
+  // Passing an existing stack appends to it.
+  bio.reset(BIO_new_mem_buf(pem.data(), pem.size()));
+  ASSERT_TRUE(bio);
+  ASSERT_EQ(infos.get(),
+            PEM_X509_INFO_read_bio(bio.get(), infos.get(), nullptr, nullptr));
+  ASSERT_EQ(2 * OPENSSL_ARRAY_SIZE(kExpected), sk_X509_INFO_num(infos.get()));
+  for (size_t i = 0; i < OPENSSL_ARRAY_SIZE(kExpected); i++) {
+    SCOPED_TRACE(i);
+    check_info(&kExpected[i], sk_X509_INFO_value(infos.get(), i));
+    check_info(
+        &kExpected[i],
+        sk_X509_INFO_value(infos.get(), i + OPENSSL_ARRAY_SIZE(kExpected)));
+  }
+
+  // Gracefully handle errors in both the append and fresh cases.
+  std::string bad_pem = cert + cert + invalid;
+
+  bio.reset(BIO_new_mem_buf(bad_pem.data(), bad_pem.size()));
+  ASSERT_TRUE(bio);
+  bssl::UniquePtr<STACK_OF(X509_INFO)> infos2(
+      PEM_X509_INFO_read_bio(bio.get(), nullptr, nullptr, nullptr));
+  EXPECT_FALSE(infos2);
+
+  bio.reset(BIO_new_mem_buf(bad_pem.data(), bad_pem.size()));
+  ASSERT_TRUE(bio);
+  EXPECT_FALSE(
+      PEM_X509_INFO_read_bio(bio.get(), infos.get(), nullptr, nullptr));
+  EXPECT_EQ(2 * OPENSSL_ARRAY_SIZE(kExpected), sk_X509_INFO_num(infos.get()));
+}
diff --git a/src/crypto/x509/x_all.c b/src/crypto/x509/x_all.c
index 064c89c..a37d7bd 100644
--- a/src/crypto/x509/x_all.c
+++ b/src/crypto/x509/x_all.c
@@ -54,14 +54,18 @@
  * copied and put under another distribution licence
  * [including the GNU Public Licence.] */
 
+#include <openssl/x509.h>
+
+#include <limits.h>
+
 #include <openssl/asn1.h>
 #include <openssl/buf.h>
 #include <openssl/digest.h>
 #include <openssl/dsa.h>
 #include <openssl/evp.h>
+#include <openssl/mem.h>
 #include <openssl/rsa.h>
 #include <openssl/stack.h>
-#include <openssl/x509.h>
 
 int X509_verify(X509 *a, EVP_PKEY *r)
 {
@@ -201,154 +205,102 @@
 }
 
 #ifndef OPENSSL_NO_FP_API
-RSA *d2i_RSAPrivateKey_fp(FILE *fp, RSA **rsa)
-{
-    return ASN1_d2i_fp_of(RSA, RSA_new, d2i_RSAPrivateKey, fp, rsa);
-}
 
-int i2d_RSAPrivateKey_fp(FILE *fp, RSA *rsa)
-{
-    return ASN1_i2d_fp_of_const(RSA, i2d_RSAPrivateKey, fp, rsa);
-}
+#define IMPLEMENT_D2I_FP(type, name, bio_func) \
+  type *name(FILE *fp, type **obj) {           \
+    BIO *bio = BIO_new_fp(fp, BIO_NOCLOSE);    \
+    if (bio == NULL) {                         \
+      return NULL;                             \
+    }                                          \
+    type *ret = bio_func(bio, obj);            \
+    BIO_free(bio);                             \
+    return ret;                                \
+  }
 
-RSA *d2i_RSAPublicKey_fp(FILE *fp, RSA **rsa)
-{
-    return ASN1_d2i_fp_of(RSA, RSA_new, d2i_RSAPublicKey, fp, rsa);
-}
+#define IMPLEMENT_I2D_FP(type, name, bio_func) \
+  int name(FILE *fp, type *obj) {              \
+    BIO *bio = BIO_new_fp(fp, BIO_NOCLOSE);    \
+    if (bio == NULL) {                         \
+      return 0;                                \
+    }                                          \
+    int ret = bio_func(bio, obj);              \
+    BIO_free(bio);                             \
+    return ret;                                \
+  }
 
-RSA *d2i_RSA_PUBKEY_fp(FILE *fp, RSA **rsa)
-{
-    return ASN1_d2i_fp((void *(*)(void))
-                       RSA_new, (D2I_OF(void)) d2i_RSA_PUBKEY, fp,
-                       (void **)rsa);
-}
+IMPLEMENT_D2I_FP(RSA, d2i_RSAPrivateKey_fp, d2i_RSAPrivateKey_bio)
+IMPLEMENT_I2D_FP(RSA, i2d_RSAPrivateKey_fp, i2d_RSAPrivateKey_bio)
 
-int i2d_RSAPublicKey_fp(FILE *fp, RSA *rsa)
-{
-    return ASN1_i2d_fp_of_const(RSA, i2d_RSAPublicKey, fp, rsa);
-}
+IMPLEMENT_D2I_FP(RSA, d2i_RSAPublicKey_fp, d2i_RSAPublicKey_bio)
+IMPLEMENT_I2D_FP(RSA, i2d_RSAPublicKey_fp, i2d_RSAPublicKey_bio)
 
-int i2d_RSA_PUBKEY_fp(FILE *fp, RSA *rsa)
-{
-    return ASN1_i2d_fp((I2D_OF_const(void))i2d_RSA_PUBKEY, fp, rsa);
-}
+IMPLEMENT_D2I_FP(RSA, d2i_RSA_PUBKEY_fp, d2i_RSA_PUBKEY_bio)
+IMPLEMENT_I2D_FP(RSA, i2d_RSA_PUBKEY_fp, i2d_RSA_PUBKEY_bio)
 #endif
 
-RSA *d2i_RSAPrivateKey_bio(BIO *bp, RSA **rsa)
-{
-    return ASN1_d2i_bio_of(RSA, RSA_new, d2i_RSAPrivateKey, bp, rsa);
-}
+#define IMPLEMENT_D2I_BIO(type, name, d2i_func)         \
+  type *name(BIO *bio, type **obj) {                    \
+    uint8_t *data;                                      \
+    size_t len;                                         \
+    if (!BIO_read_asn1(bio, &data, &len, 100 * 1024)) { \
+      return NULL;                                      \
+    }                                                   \
+    const uint8_t *ptr = data;                          \
+    type *ret = d2i_func(obj, &ptr, (long)len);         \
+    OPENSSL_free(data);                                 \
+    return ret;                                         \
+  }
 
-int i2d_RSAPrivateKey_bio(BIO *bp, RSA *rsa)
-{
-    return ASN1_i2d_bio_of_const(RSA, i2d_RSAPrivateKey, bp, rsa);
-}
+#define IMPLEMENT_I2D_BIO(type, name, i2d_func) \
+  int name(BIO *bio, type *obj) {               \
+    uint8_t *data = NULL;                       \
+    int len = i2d_func(obj, &data);             \
+    if (len < 0) {                              \
+      return 0;                                 \
+    }                                           \
+    int ret = BIO_write_all(bio, data, len);    \
+    OPENSSL_free(data);                         \
+    return ret;                                 \
+  }
 
-RSA *d2i_RSAPublicKey_bio(BIO *bp, RSA **rsa)
-{
-    return ASN1_d2i_bio_of(RSA, RSA_new, d2i_RSAPublicKey, bp, rsa);
-}
+IMPLEMENT_D2I_BIO(RSA, d2i_RSAPrivateKey_bio, d2i_RSAPrivateKey)
+IMPLEMENT_I2D_BIO(RSA, i2d_RSAPrivateKey_bio, i2d_RSAPrivateKey)
 
-RSA *d2i_RSA_PUBKEY_bio(BIO *bp, RSA **rsa)
-{
-    return ASN1_d2i_bio_of(RSA, RSA_new, d2i_RSA_PUBKEY, bp, rsa);
-}
+IMPLEMENT_D2I_BIO(RSA, d2i_RSAPublicKey_bio, d2i_RSAPublicKey)
+IMPLEMENT_I2D_BIO(RSA, i2d_RSAPublicKey_bio, i2d_RSAPublicKey)
 
-int i2d_RSAPublicKey_bio(BIO *bp, RSA *rsa)
-{
-    return ASN1_i2d_bio_of_const(RSA, i2d_RSAPublicKey, bp, rsa);
-}
-
-int i2d_RSA_PUBKEY_bio(BIO *bp, RSA *rsa)
-{
-    return ASN1_i2d_bio_of_const(RSA, i2d_RSA_PUBKEY, bp, rsa);
-}
+IMPLEMENT_D2I_BIO(RSA, d2i_RSA_PUBKEY_bio, d2i_RSA_PUBKEY)
+IMPLEMENT_I2D_BIO(RSA, i2d_RSA_PUBKEY_bio, i2d_RSA_PUBKEY)
 
 #ifndef OPENSSL_NO_DSA
 # ifndef OPENSSL_NO_FP_API
-DSA *d2i_DSAPrivateKey_fp(FILE *fp, DSA **dsa)
-{
-    return ASN1_d2i_fp_of(DSA, DSA_new, d2i_DSAPrivateKey, fp, dsa);
-}
+IMPLEMENT_D2I_FP(DSA, d2i_DSAPrivateKey_fp, d2i_DSAPrivateKey_bio)
+IMPLEMENT_I2D_FP(DSA, i2d_DSAPrivateKey_fp, i2d_DSAPrivateKey_bio)
 
-int i2d_DSAPrivateKey_fp(FILE *fp, DSA *dsa)
-{
-    return ASN1_i2d_fp_of_const(DSA, i2d_DSAPrivateKey, fp, dsa);
-}
-
-DSA *d2i_DSA_PUBKEY_fp(FILE *fp, DSA **dsa)
-{
-    return ASN1_d2i_fp_of(DSA, DSA_new, d2i_DSA_PUBKEY, fp, dsa);
-}
-
-int i2d_DSA_PUBKEY_fp(FILE *fp, DSA *dsa)
-{
-    return ASN1_i2d_fp_of_const(DSA, i2d_DSA_PUBKEY, fp, dsa);
-}
+IMPLEMENT_D2I_FP(DSA, d2i_DSA_PUBKEY_fp, d2i_DSA_PUBKEY_bio)
+IMPLEMENT_I2D_FP(DSA, i2d_DSA_PUBKEY_fp, i2d_DSA_PUBKEY_bio)
 # endif
 
-DSA *d2i_DSAPrivateKey_bio(BIO *bp, DSA **dsa)
-{
-    return ASN1_d2i_bio_of(DSA, DSA_new, d2i_DSAPrivateKey, bp, dsa);
-}
+IMPLEMENT_D2I_BIO(DSA, d2i_DSAPrivateKey_bio, d2i_DSAPrivateKey)
+IMPLEMENT_I2D_BIO(DSA, i2d_DSAPrivateKey_bio, i2d_DSAPrivateKey)
 
-int i2d_DSAPrivateKey_bio(BIO *bp, DSA *dsa)
-{
-    return ASN1_i2d_bio_of_const(DSA, i2d_DSAPrivateKey, bp, dsa);
-}
-
-DSA *d2i_DSA_PUBKEY_bio(BIO *bp, DSA **dsa)
-{
-    return ASN1_d2i_bio_of(DSA, DSA_new, d2i_DSA_PUBKEY, bp, dsa);
-}
-
-int i2d_DSA_PUBKEY_bio(BIO *bp, DSA *dsa)
-{
-    return ASN1_i2d_bio_of_const(DSA, i2d_DSA_PUBKEY, bp, dsa);
-}
-
+IMPLEMENT_D2I_BIO(DSA, d2i_DSA_PUBKEY_bio, d2i_DSA_PUBKEY)
+IMPLEMENT_I2D_BIO(DSA, i2d_DSA_PUBKEY_bio, i2d_DSA_PUBKEY)
 #endif
 
 #ifndef OPENSSL_NO_FP_API
-EC_KEY *d2i_EC_PUBKEY_fp(FILE *fp, EC_KEY **eckey)
-{
-    return ASN1_d2i_fp_of(EC_KEY, EC_KEY_new, d2i_EC_PUBKEY, fp, eckey);
-}
+IMPLEMENT_D2I_FP(EC_KEY, d2i_ECPrivateKey_fp, d2i_ECPrivateKey_bio)
+IMPLEMENT_I2D_FP(EC_KEY, i2d_ECPrivateKey_fp, i2d_ECPrivateKey_bio)
 
-int i2d_EC_PUBKEY_fp(FILE *fp, EC_KEY *eckey)
-{
-    return ASN1_i2d_fp_of_const(EC_KEY, i2d_EC_PUBKEY, fp, eckey);
-}
-
-EC_KEY *d2i_ECPrivateKey_fp(FILE *fp, EC_KEY **eckey)
-{
-    return ASN1_d2i_fp_of(EC_KEY, EC_KEY_new, d2i_ECPrivateKey, fp, eckey);
-}
-
-int i2d_ECPrivateKey_fp(FILE *fp, EC_KEY *eckey)
-{
-    return ASN1_i2d_fp_of_const(EC_KEY, i2d_ECPrivateKey, fp, eckey);
-}
+IMPLEMENT_D2I_FP(EC_KEY, d2i_EC_PUBKEY_fp, d2i_EC_PUBKEY_bio)
+IMPLEMENT_I2D_FP(EC_KEY, i2d_EC_PUBKEY_fp, i2d_EC_PUBKEY_bio)
 #endif
-EC_KEY *d2i_EC_PUBKEY_bio(BIO *bp, EC_KEY **eckey)
-{
-    return ASN1_d2i_bio_of(EC_KEY, EC_KEY_new, d2i_EC_PUBKEY, bp, eckey);
-}
 
-int i2d_EC_PUBKEY_bio(BIO *bp, EC_KEY *ecdsa)
-{
-    return ASN1_i2d_bio_of_const(EC_KEY, i2d_EC_PUBKEY, bp, ecdsa);
-}
+IMPLEMENT_D2I_BIO(EC_KEY, d2i_ECPrivateKey_bio, d2i_ECPrivateKey)
+IMPLEMENT_I2D_BIO(EC_KEY, i2d_ECPrivateKey_bio, i2d_ECPrivateKey)
 
-EC_KEY *d2i_ECPrivateKey_bio(BIO *bp, EC_KEY **eckey)
-{
-    return ASN1_d2i_bio_of(EC_KEY, EC_KEY_new, d2i_ECPrivateKey, bp, eckey);
-}
-
-int i2d_ECPrivateKey_bio(BIO *bp, EC_KEY *eckey)
-{
-    return ASN1_i2d_bio_of_const(EC_KEY, i2d_ECPrivateKey, bp, eckey);
-}
+IMPLEMENT_D2I_BIO(EC_KEY, d2i_EC_PUBKEY_bio, d2i_EC_PUBKEY)
+IMPLEMENT_I2D_BIO(EC_KEY, i2d_EC_PUBKEY_bio, i2d_EC_PUBKEY)
 
 int X509_pubkey_digest(const X509 *data, const EVP_MD *type,
                        unsigned char *md, unsigned int *len)
@@ -389,40 +341,18 @@
 }
 
 #ifndef OPENSSL_NO_FP_API
-X509_SIG *d2i_PKCS8_fp(FILE *fp, X509_SIG **p8)
-{
-    return ASN1_d2i_fp_of(X509_SIG, X509_SIG_new, d2i_X509_SIG, fp, p8);
-}
-
-int i2d_PKCS8_fp(FILE *fp, X509_SIG *p8)
-{
-    return ASN1_i2d_fp_of(X509_SIG, i2d_X509_SIG, fp, p8);
-}
+IMPLEMENT_D2I_FP(X509_SIG, d2i_PKCS8_fp, d2i_PKCS8_bio)
+IMPLEMENT_I2D_FP(X509_SIG, i2d_PKCS8_fp, i2d_PKCS8_bio)
 #endif
 
-X509_SIG *d2i_PKCS8_bio(BIO *bp, X509_SIG **p8)
-{
-    return ASN1_d2i_bio_of(X509_SIG, X509_SIG_new, d2i_X509_SIG, bp, p8);
-}
-
-int i2d_PKCS8_bio(BIO *bp, X509_SIG *p8)
-{
-    return ASN1_i2d_bio_of(X509_SIG, i2d_X509_SIG, bp, p8);
-}
+IMPLEMENT_D2I_BIO(X509_SIG, d2i_PKCS8_bio, d2i_X509_SIG)
+IMPLEMENT_I2D_BIO(X509_SIG, i2d_PKCS8_bio, i2d_X509_SIG)
 
 #ifndef OPENSSL_NO_FP_API
-PKCS8_PRIV_KEY_INFO *d2i_PKCS8_PRIV_KEY_INFO_fp(FILE *fp,
-                                                PKCS8_PRIV_KEY_INFO **p8inf)
-{
-    return ASN1_d2i_fp_of(PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_new,
-                          d2i_PKCS8_PRIV_KEY_INFO, fp, p8inf);
-}
-
-int i2d_PKCS8_PRIV_KEY_INFO_fp(FILE *fp, PKCS8_PRIV_KEY_INFO *p8inf)
-{
-    return ASN1_i2d_fp_of(PKCS8_PRIV_KEY_INFO, i2d_PKCS8_PRIV_KEY_INFO, fp,
-                          p8inf);
-}
+IMPLEMENT_D2I_FP(PKCS8_PRIV_KEY_INFO, d2i_PKCS8_PRIV_KEY_INFO_fp,
+                 d2i_PKCS8_PRIV_KEY_INFO_bio)
+IMPLEMENT_I2D_FP(PKCS8_PRIV_KEY_INFO, i2d_PKCS8_PRIV_KEY_INFO_fp,
+                 i2d_PKCS8_PRIV_KEY_INFO_bio)
 
 int i2d_PKCS8PrivateKeyInfo_fp(FILE *fp, EVP_PKEY *key)
 {
@@ -436,38 +366,16 @@
     return ret;
 }
 
-int i2d_PrivateKey_fp(FILE *fp, EVP_PKEY *pkey)
-{
-    return ASN1_i2d_fp_of_const(EVP_PKEY, i2d_PrivateKey, fp, pkey);
-}
+IMPLEMENT_D2I_FP(EVP_PKEY, d2i_PrivateKey_fp, d2i_PrivateKey_bio)
+IMPLEMENT_I2D_FP(EVP_PKEY, i2d_PrivateKey_fp, i2d_PrivateKey_bio)
 
-EVP_PKEY *d2i_PrivateKey_fp(FILE *fp, EVP_PKEY **a)
-{
-    return ASN1_d2i_fp_of(EVP_PKEY, EVP_PKEY_new, d2i_AutoPrivateKey, fp, a);
-}
+IMPLEMENT_D2I_FP(EVP_PKEY, d2i_PUBKEY_fp, d2i_PUBKEY_bio)
+IMPLEMENT_I2D_FP(EVP_PKEY, i2d_PUBKEY_fp, i2d_PUBKEY_bio)
 
-int i2d_PUBKEY_fp(FILE *fp, EVP_PKEY *pkey)
-{
-    return ASN1_i2d_fp_of_const(EVP_PKEY, i2d_PUBKEY, fp, pkey);
-}
-
-EVP_PKEY *d2i_PUBKEY_fp(FILE *fp, EVP_PKEY **a)
-{
-    return ASN1_d2i_fp_of(EVP_PKEY, EVP_PKEY_new, d2i_PUBKEY, fp, a);
-}
-
-PKCS8_PRIV_KEY_INFO *d2i_PKCS8_PRIV_KEY_INFO_bio(BIO *bp,
-                                                 PKCS8_PRIV_KEY_INFO **p8inf)
-{
-    return ASN1_d2i_bio_of(PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_new,
-                           d2i_PKCS8_PRIV_KEY_INFO, bp, p8inf);
-}
-
-int i2d_PKCS8_PRIV_KEY_INFO_bio(BIO *bp, PKCS8_PRIV_KEY_INFO *p8inf)
-{
-    return ASN1_i2d_bio_of(PKCS8_PRIV_KEY_INFO, i2d_PKCS8_PRIV_KEY_INFO, bp,
-                           p8inf);
-}
+IMPLEMENT_D2I_BIO(PKCS8_PRIV_KEY_INFO, d2i_PKCS8_PRIV_KEY_INFO_bio,
+                  d2i_PKCS8_PRIV_KEY_INFO)
+IMPLEMENT_I2D_BIO(PKCS8_PRIV_KEY_INFO, i2d_PKCS8_PRIV_KEY_INFO_bio,
+                  i2d_PKCS8_PRIV_KEY_INFO)
 
 int i2d_PKCS8PrivateKeyInfo_bio(BIO *bp, EVP_PKEY *key)
 {
@@ -482,32 +390,11 @@
 }
 #endif
 
-int i2d_PrivateKey_bio(BIO *bp, EVP_PKEY *pkey)
-{
-    return ASN1_i2d_bio_of_const(EVP_PKEY, i2d_PrivateKey, bp, pkey);
-}
+IMPLEMENT_D2I_BIO(EVP_PKEY, d2i_PrivateKey_bio, d2i_AutoPrivateKey)
+IMPLEMENT_I2D_BIO(EVP_PKEY, i2d_PrivateKey_bio, i2d_PrivateKey)
 
-EVP_PKEY *d2i_PrivateKey_bio(BIO *bp, EVP_PKEY **a)
-{
-    return ASN1_d2i_bio_of(EVP_PKEY, EVP_PKEY_new, d2i_AutoPrivateKey, bp, a);
-}
+IMPLEMENT_D2I_BIO(EVP_PKEY, d2i_PUBKEY_bio, d2i_PUBKEY)
+IMPLEMENT_I2D_BIO(EVP_PKEY, i2d_PUBKEY_bio, i2d_PUBKEY)
 
-int i2d_PUBKEY_bio(BIO *bp, EVP_PKEY *pkey)
-{
-    return ASN1_i2d_bio_of_const(EVP_PKEY, i2d_PUBKEY, bp, pkey);
-}
-
-EVP_PKEY *d2i_PUBKEY_bio(BIO *bp, EVP_PKEY **a)
-{
-    return ASN1_d2i_bio_of(EVP_PKEY, EVP_PKEY_new, d2i_PUBKEY, bp, a);
-}
-
-DH *d2i_DHparams_bio(BIO *bp, DH **dh)
-{
-    return ASN1_d2i_bio_of(DH, DH_new, d2i_DHparams, bp, dh);
-}
-
-int i2d_DHparams_bio(BIO *bp, const DH *dh)
-{
-    return ASN1_i2d_bio_of_const(DH, i2d_DHparams, bp, dh);
-}
+IMPLEMENT_D2I_BIO(DH, d2i_DHparams_bio, d2i_DHparams)
+IMPLEMENT_I2D_BIO(const DH, i2d_DHparams_bio, i2d_DHparams)
diff --git a/src/crypto/x509v3/v3_genn.c b/src/crypto/x509v3/v3_genn.c
index 8c92687..552a524 100644
--- a/src/crypto/x509v3/v3_genn.c
+++ b/src/crypto/x509v3/v3_genn.c
@@ -100,12 +100,7 @@
 
 IMPLEMENT_ASN1_FUNCTIONS(GENERAL_NAMES)
 
-GENERAL_NAME *GENERAL_NAME_dup(GENERAL_NAME *a)
-{
-    return (GENERAL_NAME *)ASN1_dup((i2d_of_void *)i2d_GENERAL_NAME,
-                                    (d2i_of_void *)d2i_GENERAL_NAME,
-                                    (char *)a);
-}
+IMPLEMENT_ASN1_DUP_FUNCTION(GENERAL_NAME)
 
 /* Returns 0 if they are equal, != 0 otherwise. */
 int GENERAL_NAME_cmp(GENERAL_NAME *a, GENERAL_NAME *b)
diff --git a/src/decrepit/x509/x509_decrepit.c b/src/decrepit/x509/x509_decrepit.c
index 5714b40..28015f3 100644
--- a/src/decrepit/x509/x509_decrepit.c
+++ b/src/decrepit/x509/x509_decrepit.c
@@ -16,27 +16,7 @@
 #include <openssl/x509v3.h>
 
 
-X509_EXTENSION *X509V3_EXT_conf_nid(LHASH_OF(CONF_VALUE) *conf,
-                                    X509V3_CTX *ctx, int ext_nid, char *value) {
-  CONF *nconf = NULL;
-  LHASH_OF(CONF_VALUE) *orig_data = NULL;
-
-  if (conf != NULL) {
-    nconf = NCONF_new(NULL /* no method */);
-    if (nconf == NULL) {
-      return NULL;
-    }
-
-    orig_data = nconf->data;
-    nconf->data = conf;
-  }
-
-  X509_EXTENSION *ret = X509V3_EXT_nconf_nid(nconf, ctx, ext_nid, value);
-
-  if (nconf != NULL) {
-    nconf->data = orig_data;
-    NCONF_free(nconf);
-  }
-
-  return ret;
+X509_EXTENSION *X509V3_EXT_conf_nid(X509_MUST_BE_NULL *conf, X509V3_CTX *ctx,
+                                    int ext_nid, char *value) {
+  return X509V3_EXT_nconf_nid(NULL, ctx, ext_nid, value);
 }
diff --git a/src/decrepit/xts/xts.c b/src/decrepit/xts/xts.c
index e3189e5..bc2a1b2 100644
--- a/src/decrepit/xts/xts.c
+++ b/src/decrepit/xts/xts.c
@@ -57,7 +57,7 @@
 
 
 typedef struct xts128_context {
-  void *key1, *key2;
+  AES_KEY *key1, *key2;
   block128_f block1, block2;
 } XTS128_CONTEXT;
 
@@ -186,20 +186,20 @@
     // key_len is two AES keys
     if (enc) {
       AES_set_encrypt_key(key, ctx->key_len * 4, &xctx->ks1.ks);
-      xctx->xts.block1 = (block128_f) AES_encrypt;
+      xctx->xts.block1 = AES_encrypt;
     } else {
       AES_set_decrypt_key(key, ctx->key_len * 4, &xctx->ks1.ks);
-      xctx->xts.block1 = (block128_f) AES_decrypt;
+      xctx->xts.block1 = AES_decrypt;
     }
 
     AES_set_encrypt_key(key + ctx->key_len / 2,
                         ctx->key_len * 4, &xctx->ks2.ks);
-    xctx->xts.block2 = (block128_f) AES_encrypt;
-    xctx->xts.key1 = &xctx->ks1;
+    xctx->xts.block2 = AES_encrypt;
+    xctx->xts.key1 = &xctx->ks1.ks;
   }
 
   if (iv) {
-    xctx->xts.key2 = &xctx->ks2;
+    xctx->xts.key2 = &xctx->ks2.ks;
     OPENSSL_memcpy(ctx->iv, iv, 16);
   }
 
@@ -226,16 +226,16 @@
     EVP_CIPHER_CTX *out = ptr;
     EVP_AES_XTS_CTX *xctx_out = out->cipher_data;
     if (xctx->xts.key1) {
-      if (xctx->xts.key1 != &xctx->ks1) {
+      if (xctx->xts.key1 != &xctx->ks1.ks) {
         return 0;
       }
-      xctx_out->xts.key1 = &xctx_out->ks1;
+      xctx_out->xts.key1 = &xctx_out->ks1.ks;
     }
     if (xctx->xts.key2) {
-      if (xctx->xts.key2 != &xctx->ks2) {
+      if (xctx->xts.key2 != &xctx->ks2.ks) {
         return 0;
       }
-      xctx_out->xts.key2 = &xctx_out->ks2;
+      xctx_out->xts.key2 = &xctx_out->ks2.ks;
     }
     return 1;
   } else if (type != EVP_CTRL_INIT) {
diff --git a/src/include/openssl/asn1.h b/src/include/openssl/asn1.h
index 46e5f53..8b61eaa 100644
--- a/src/include/openssl/asn1.h
+++ b/src/include/openssl/asn1.h
@@ -298,19 +298,6 @@
 	OPENSSL_EXPORT int fname##_print_ctx(BIO *out, stname *x, int indent, \
 					 const ASN1_PCTX *pctx);
 
-#define D2I_OF(type) type *(*)(type **,const unsigned char **,long)
-#define I2D_OF(type) int (*)(type *,unsigned char **)
-#define I2D_OF_const(type) int (*)(const type *,unsigned char **)
-
-#define CHECKED_D2I_OF(type, d2i) \
-    ((d2i_of_void*) (1 ? d2i : ((D2I_OF(type))0)))
-#define CHECKED_I2D_OF(type, i2d) \
-    ((i2d_of_void*) (1 ? i2d : ((I2D_OF(type))0)))
-#define CHECKED_NEW_OF(type, xnew) \
-    ((void *(*)(void)) (1 ? xnew : ((type *(*)(void))0)))
-#define CHECKED_PPTR_OF(type, p) \
-    ((void**) (1 ? p : (type**)0))
-
 typedef void *d2i_of_void(void **, const unsigned char **, long);
 typedef int i2d_of_void(const void *, unsigned char **);
 
@@ -762,76 +749,17 @@
 OPENSSL_EXPORT int ASN1_put_eoc(unsigned char **pp);
 OPENSSL_EXPORT int ASN1_object_size(int constructed, int length, int tag);
 
-/* Used to implement other functions */
-OPENSSL_EXPORT void *ASN1_dup(i2d_of_void *i2d, d2i_of_void *d2i, void *x);
-
-#define ASN1_dup_of(type,i2d,d2i,x) \
-    ((type*)ASN1_dup(CHECKED_I2D_OF(type, i2d), \
-		     CHECKED_D2I_OF(type, d2i), \
-		     CHECKED_PTR_OF(type, x)))
-
-#define ASN1_dup_of_const(type,i2d,d2i,x) \
-    ((type*)ASN1_dup(CHECKED_I2D_OF(const type, i2d), \
-		     CHECKED_D2I_OF(type, d2i), \
-		     CHECKED_PTR_OF(const type, x)))
-
 OPENSSL_EXPORT void *ASN1_item_dup(const ASN1_ITEM *it, void *x);
 
-/* ASN1 alloc/free macros for when a type is only used internally */
-
-#define M_ASN1_new_of(type) (type *)ASN1_item_new(ASN1_ITEM_rptr(type))
-#define M_ASN1_free_of(x, type) \
-		ASN1_item_free(CHECKED_PTR_OF(type, x), ASN1_ITEM_rptr(type))
-
 #ifndef OPENSSL_NO_FP_API
-OPENSSL_EXPORT void *ASN1_d2i_fp(void *(*xnew)(void), d2i_of_void *d2i, FILE *in, void **x);
-
-#define ASN1_d2i_fp_of(type,xnew,d2i,in,x) \
-    ((type*)ASN1_d2i_fp(CHECKED_NEW_OF(type, xnew), \
-			CHECKED_D2I_OF(type, d2i), \
-			in, \
-			CHECKED_PPTR_OF(type, x)))
-
 OPENSSL_EXPORT void *ASN1_item_d2i_fp(const ASN1_ITEM *it, FILE *in, void *x);
-OPENSSL_EXPORT int ASN1_i2d_fp(i2d_of_void *i2d,FILE *out,void *x);
-
-#define ASN1_i2d_fp_of(type,i2d,out,x) \
-    (ASN1_i2d_fp(CHECKED_I2D_OF(type, i2d), \
-		 out, \
-		 CHECKED_PTR_OF(type, x)))
-
-#define ASN1_i2d_fp_of_const(type,i2d,out,x) \
-    (ASN1_i2d_fp(CHECKED_I2D_OF(const type, i2d), \
-		 out, \
-		 CHECKED_PTR_OF(const type, x)))
-
 OPENSSL_EXPORT int ASN1_item_i2d_fp(const ASN1_ITEM *it, FILE *out, void *x);
 OPENSSL_EXPORT int ASN1_STRING_print_ex_fp(FILE *fp, ASN1_STRING *str, unsigned long flags);
 #endif
 
 OPENSSL_EXPORT int ASN1_STRING_to_UTF8(unsigned char **out, ASN1_STRING *in);
 
-OPENSSL_EXPORT void *ASN1_d2i_bio(void *(*xnew)(void), d2i_of_void *d2i, BIO *in, void **x);
-
-#define ASN1_d2i_bio_of(type,xnew,d2i,in,x) \
-    ((type*)ASN1_d2i_bio( CHECKED_NEW_OF(type, xnew), \
-			  CHECKED_D2I_OF(type, d2i), \
-			  in, \
-			  CHECKED_PPTR_OF(type, x)))
-
 OPENSSL_EXPORT void *ASN1_item_d2i_bio(const ASN1_ITEM *it, BIO *in, void *x);
-OPENSSL_EXPORT int ASN1_i2d_bio(i2d_of_void *i2d,BIO *out, void *x);
-
-#define ASN1_i2d_bio_of(type,i2d,out,x) \
-    (ASN1_i2d_bio(CHECKED_I2D_OF(type, i2d), \
-		  out, \
-		  CHECKED_PTR_OF(type, x)))
-
-#define ASN1_i2d_bio_of_const(type,i2d,out,x) \
-    (ASN1_i2d_bio(CHECKED_I2D_OF(const type, i2d), \
-		  out, \
-		  CHECKED_PTR_OF(const type, x)))
-
 OPENSSL_EXPORT int ASN1_item_i2d_bio(const ASN1_ITEM *it, BIO *out, void *x);
 OPENSSL_EXPORT int ASN1_UTCTIME_print(BIO *fp, const ASN1_UTCTIME *a);
 OPENSSL_EXPORT int ASN1_GENERALIZEDTIME_print(BIO *fp, const ASN1_GENERALIZEDTIME *a);
diff --git a/src/include/openssl/base.h b/src/include/openssl/base.h
index d134993..5a4bb66 100644
--- a/src/include/openssl/base.h
+++ b/src/include/openssl/base.h
@@ -65,6 +65,10 @@
 #include <stdio.h>
 #endif
 
+#if defined(__APPLE__)
+#include <TargetConditionals.h>
+#endif
+
 // Include a BoringSSL-only header so consumers including this header without
 // setting up include paths do not accidentally pick up the system
 // opensslconf.h.
@@ -120,6 +124,9 @@
 
 #if defined(__APPLE__)
 #define OPENSSL_APPLE
+#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
+#define OPENSSL_IOS
+#endif
 #endif
 
 #if defined(_WIN32)
@@ -136,14 +143,36 @@
 
 #if defined(TRUSTY)
 #define OPENSSL_TRUSTY
-#define OPENSSL_NO_THREADS
+#define OPENSSL_NO_THREADS_CORRUPT_MEMORY_AND_LEAK_SECRETS_IF_THREADED
 #endif
 
 #if defined(__ANDROID_API__)
 #define OPENSSL_ANDROID
 #endif
 
-#if !defined(OPENSSL_NO_THREADS)
+// OPENSSL_NO_THREADS has been deprecated in favor of this much longer and
+// louder name, to better reflect exactly what that option did.
+//
+// TODO(davidben): Remove this block when callers have migrated.
+#if defined(OPENSSL_NO_THREADS) && \
+    !defined(OPENSSL_NO_THREADS_CORRUPT_MEMORY_AND_LEAK_SECRETS_IF_THREADED)
+#define OPENSSL_NO_THREADS_CORRUPT_MEMORY_AND_LEAK_SECRETS_IF_THREADED
+#endif
+
+// BoringSSL requires platform's locking APIs to make internal global state
+// thread-safe, including the PRNG. On some single-threaded embedded platforms,
+// locking APIs may not exist, so this dependency may be disabled with the
+// following build flag.
+//
+// IMPORTANT: Doing so means the consumer promises the library will never be
+// used in any multi-threaded context. It causes BoringSSL to be globally
+// thread-unsafe. Setting it inappropriately will subtly and unpredictably
+// corrupt memory and leak secret keys.
+//
+// Do not set this flag on any platform where threads are possible. BoringSSL
+// maintainers will not provide support for any consumers that do so. Changes
+// which break such unsupported configurations will not be reverted.
+#if !defined(OPENSSL_NO_THREADS_CORRUPT_MEMORY_AND_LEAK_SECRETS_IF_THREADED)
 #define OPENSSL_THREADS
 #endif
 
@@ -217,6 +246,35 @@
 #define OPENSSL_UNUSED
 #endif
 
+// C and C++ handle inline functions differently. In C++, an inline function is
+// defined in just the header file, potentially emitted in multiple compilation
+// units (in cases the compiler did not inline), but each copy must be identical
+// to satsify ODR. In C, a non-static inline must be manually emitted in exactly
+// one compilation unit with a separate extern inline declaration.
+//
+// In both languages, exported inline functions referencing file-local symbols
+// are problematic. C forbids this altogether (though GCC and Clang seem not to
+// enforce it). It works in C++, but ODR requires the definitions be identical,
+// including all names in the definitions resolving to the "same entity". In
+// practice, this is unlikely to be a problem, but an inline function that
+// returns a pointer to a file-local symbol
+// could compile oddly.
+//
+// Historically, we used static inline in headers. However, to satisfy ODR, use
+// plain inline in C++, to allow inline consumer functions to call our header
+// functions. Plain inline would also work better with C99 inline, but that is
+// not used much in practice, extern inline is tedious, and there are conflicts
+// with the old gnu89 model:
+// https://stackoverflow.com/questions/216510/extern-inline
+#if defined(__cplusplus)
+#define OPENSSL_INLINE inline
+#else
+// Add OPENSSL_UNUSED so that, should an inline function be emitted via macro
+// (e.g. a |STACK_OF(T)| implementation) in a source file without tripping
+// clang's -Wunused-function.
+#define OPENSSL_INLINE static inline OPENSSL_UNUSED
+#endif
+
 #if defined(BORINGSSL_UNSAFE_FUZZER_MODE) && \
     !defined(BORINGSSL_UNSAFE_DETERMINISTIC_MODE)
 #define BORINGSSL_UNSAFE_DETERMINISTIC_MODE
@@ -477,16 +535,16 @@
 template <typename T>
 using UniquePtr = std::unique_ptr<T, internal::Deleter<T>>;
 
-#define BORINGSSL_MAKE_UP_REF(type, up_ref_func)                    \
-  static inline UniquePtr<type> UpRef(type *v) {                    \
-    if (v != nullptr) {                                             \
-      up_ref_func(v);                                               \
-    }                                                               \
-    return UniquePtr<type>(v);                                      \
-  }                                                                 \
-                                                                    \
-  static inline UniquePtr<type> UpRef(const UniquePtr<type> &ptr) { \
-    return UpRef(ptr.get());                                        \
+#define BORINGSSL_MAKE_UP_REF(type, up_ref_func)             \
+  inline UniquePtr<type> UpRef(type *v) {                    \
+    if (v != nullptr) {                                      \
+      up_ref_func(v);                                        \
+    }                                                        \
+    return UniquePtr<type>(v);                               \
+  }                                                          \
+                                                             \
+  inline UniquePtr<type> UpRef(const UniquePtr<type> &ptr) { \
+    return UpRef(ptr.get());                                 \
   }
 
 BSSL_NAMESPACE_END
diff --git a/src/include/openssl/bio.h b/src/include/openssl/bio.h
index 70c2fbf..8e2db65 100644
--- a/src/include/openssl/bio.h
+++ b/src/include/openssl/bio.h
@@ -117,10 +117,14 @@
 // return a line for this call, remove the warning above.
 OPENSSL_EXPORT int BIO_gets(BIO *bio, char *buf, int size);
 
-// BIO_write writes |len| bytes from |data| to BIO. It returns the number of
+// BIO_write writes |len| bytes from |data| to |bio|. It returns the number of
 // bytes written or a negative number on error.
 OPENSSL_EXPORT int BIO_write(BIO *bio, const void *data, int len);
 
+// BIO_write_all writes |len| bytes from |data| to |bio|, looping as necessary.
+// It returns one if all bytes were successfully written and zero on error.
+OPENSSL_EXPORT int BIO_write_all(BIO *bio, const void *data, size_t len);
+
 // BIO_puts writes a NUL terminated string from |buf| to |bio|. It returns the
 // number of bytes written or a negative number on error.
 OPENSSL_EXPORT int BIO_puts(BIO *bio, const char *buf);
diff --git a/src/include/openssl/conf.h b/src/include/openssl/conf.h
index 07e34ee..7aa76e1 100644
--- a/src/include/openssl/conf.h
+++ b/src/include/openssl/conf.h
@@ -60,7 +60,6 @@
 #include <openssl/base.h>
 
 #include <openssl/stack.h>
-#include <openssl/lhash.h>
 
 #if defined(__cplusplus)
 extern "C" {
@@ -85,10 +84,6 @@
   char *value;
 };
 
-struct conf_st {
-  LHASH_OF(CONF_VALUE) *data;
-};
-
 DEFINE_STACK_OF(CONF_VALUE)
 
 
diff --git a/src/include/openssl/cpu.h b/src/include/openssl/cpu.h
index bb847f9..b2759fe 100644
--- a/src/include/openssl/cpu.h
+++ b/src/include/openssl/cpu.h
@@ -96,7 +96,7 @@
 #if defined(BORINGSSL_FIPS)
 const uint32_t *OPENSSL_ia32cap_get(void);
 #else
-static inline const uint32_t *OPENSSL_ia32cap_get(void) {
+OPENSSL_INLINE const uint32_t *OPENSSL_ia32cap_get(void) {
   return OPENSSL_ia32cap_P;
 }
 #endif
@@ -119,7 +119,7 @@
 
 // CRYPTO_is_NEON_capable returns true if the current CPU has a NEON unit. If
 // this is known statically then it returns one immediately.
-static inline int CRYPTO_is_NEON_capable(void) {
+OPENSSL_INLINE int CRYPTO_is_NEON_capable(void) {
   // Only statically skip the runtime lookup on aarch64. On arm, one CPU is
   // known to have a broken NEON unit which is known to fail with on some
   // hand-written NEON assembly. For now, continue to apply the workaround even
@@ -152,7 +152,7 @@
 
 #else
 
-static inline int CRYPTO_is_NEON_capable(void) {
+OPENSSL_INLINE int CRYPTO_is_NEON_capable(void) {
 #if defined(OPENSSL_STATIC_ARMCAP_NEON) || defined(__ARM_NEON__)
   return 1;
 #else
@@ -160,7 +160,7 @@
 #endif
 }
 
-static inline int CRYPTO_is_ARMv8_AES_capable(void) {
+OPENSSL_INLINE int CRYPTO_is_ARMv8_AES_capable(void) {
 #if defined(OPENSSL_STATIC_ARMCAP_AES) || defined(__ARM_FEATURE_CRYPTO)
   return 1;
 #else
@@ -168,7 +168,7 @@
 #endif
 }
 
-static inline int CRYPTO_is_ARMv8_PMULL_capable(void) {
+OPENSSL_INLINE int CRYPTO_is_ARMv8_PMULL_capable(void) {
 #if defined(OPENSSL_STATIC_ARMCAP_PMULL) || defined(__ARM_FEATURE_CRYPTO)
   return 1;
 #else
diff --git a/src/include/openssl/pem.h b/src/include/openssl/pem.h
index a43ca0d..9c0ff93 100644
--- a/src/include/openssl/pem.h
+++ b/src/include/openssl/pem.h
@@ -123,73 +123,109 @@
 
 #else
 
-#define IMPLEMENT_PEM_read_fp(name, type, str, asn1) \
-OPENSSL_EXPORT type *PEM_read_##name(FILE *fp, type **x, pem_password_cb *cb, void *u)\
-{ \
-return (type *)PEM_ASN1_read((d2i_of_void *)d2i_##asn1, str,fp,(void **)x,cb,u); \
-} 
+#define IMPLEMENT_PEM_read_fp(name, type, str, asn1)                         \
+  static void *pem_read_##name##_d2i(void **x, const unsigned char **inp,    \
+                                     long len) {                             \
+    return d2i_##asn1((type **)x, inp, len);                                 \
+  }                                                                          \
+  OPENSSL_EXPORT type *PEM_read_##name(FILE *fp, type **x,                   \
+                                       pem_password_cb *cb, void *u) {       \
+    return (type *)PEM_ASN1_read(pem_read_##name##_d2i, str, fp, (void **)x, \
+                                 cb, u);                                     \
+  }
 
-#define IMPLEMENT_PEM_write_fp(name, type, str, asn1) \
-OPENSSL_EXPORT int PEM_write_##name(FILE *fp, type *x) \
-{ \
-return PEM_ASN1_write((i2d_of_void *)i2d_##asn1,str,fp,x,NULL,NULL,0,NULL,NULL); \
-}
+#define IMPLEMENT_PEM_write_fp(name, type, str, asn1)                        \
+  static int pem_write_##name##_i2d(const void *x, unsigned char **outp) {   \
+    return i2d_##asn1((type *)x, outp);                                      \
+  }                                                                          \
+  OPENSSL_EXPORT int PEM_write_##name(FILE *fp, type *x) {                   \
+    return PEM_ASN1_write(pem_write_##name##_i2d, str, fp, x, NULL, NULL, 0, \
+                          NULL, NULL);                                       \
+  }
 
-#define IMPLEMENT_PEM_write_fp_const(name, type, str, asn1) \
-OPENSSL_EXPORT int PEM_write_##name(FILE *fp, const type *x) \
-{ \
-return PEM_ASN1_write((i2d_of_void *)i2d_##asn1,str,fp,(void *)x,NULL,NULL,0,NULL,NULL); \
-}
+#define IMPLEMENT_PEM_write_fp_const(name, type, str, asn1)                 \
+  static int pem_write_##name##_i2d(const void *x, unsigned char **outp) {  \
+    return i2d_##asn1((const type *)x, outp);                               \
+  }                                                                         \
+  OPENSSL_EXPORT int PEM_write_##name(FILE *fp, const type *x) {            \
+    return PEM_ASN1_write(pem_write_##name##_i2d, str, fp, (void *)x, NULL, \
+                          NULL, 0, NULL, NULL);                             \
+  }
 
-#define IMPLEMENT_PEM_write_cb_fp(name, type, str, asn1) \
-OPENSSL_EXPORT int PEM_write_##name(FILE *fp, type *x, const EVP_CIPHER *enc, \
-	     unsigned char *kstr, int klen, pem_password_cb *cb, \
-		  void *u) \
-	{ \
-	return PEM_ASN1_write((i2d_of_void *)i2d_##asn1,str,fp,x,enc,kstr,klen,cb,u); \
-	}
+#define IMPLEMENT_PEM_write_cb_fp(name, type, str, asn1)                       \
+  static int pem_write_##name##_i2d(const void *x, unsigned char **outp) {     \
+    return i2d_##asn1((type *)x, outp);                                        \
+  }                                                                            \
+  OPENSSL_EXPORT int PEM_write_##name(                                         \
+      FILE *fp, type *x, const EVP_CIPHER *enc, unsigned char *kstr, int klen, \
+      pem_password_cb *cb, void *u) {                                          \
+    return PEM_ASN1_write(pem_write_##name##_i2d, str, fp, x, enc, kstr, klen, \
+                          cb, u);                                              \
+  }
 
-#define IMPLEMENT_PEM_write_cb_fp_const(name, type, str, asn1) \
-OPENSSL_EXPORT int PEM_write_##name(FILE *fp, type *x, const EVP_CIPHER *enc, \
-	     unsigned char *kstr, int klen, pem_password_cb *cb, \
-		  void *u) \
-	{ \
-	return PEM_ASN1_write((i2d_of_void *)i2d_##asn1,str,fp,x,enc,kstr,klen,cb,u); \
-	}
+#define IMPLEMENT_PEM_write_cb_fp_const(name, type, str, asn1)                 \
+  static int pem_write_##name##_i2d(const void *x, unsigned char **outp) {     \
+    return i2d_##asn1((const type *)x, outp);                                  \
+  }                                                                            \
+  OPENSSL_EXPORT int PEM_write_##name(                                         \
+      FILE *fp, type *x, const EVP_CIPHER *enc, unsigned char *kstr, int klen, \
+      pem_password_cb *cb, void *u) {                                          \
+    return PEM_ASN1_write(pem_write_##name##_i2d, str, fp, x, enc, kstr, klen, \
+                          cb, u);                                              \
+  }
 
 #endif
 
-#define IMPLEMENT_PEM_read_bio(name, type, str, asn1) \
-OPENSSL_EXPORT type *PEM_read_bio_##name(BIO *bp, type **x, pem_password_cb *cb, void *u)\
-{ \
-return (type *)PEM_ASN1_read_bio((d2i_of_void *)d2i_##asn1, str,bp,(void **)x,cb,u); \
-}
+#define IMPLEMENT_PEM_read_bio(name, type, str, asn1)                         \
+  static void *pem_read_bio_##name##_d2i(void **x, const unsigned char **inp, \
+                                         long len) {                          \
+    return d2i_##asn1((type **)x, inp, len);                                  \
+  }                                                                           \
+  OPENSSL_EXPORT type *PEM_read_bio_##name(BIO *bp, type **x,                 \
+                                           pem_password_cb *cb, void *u) {    \
+    return (type *)PEM_ASN1_read_bio(pem_read_bio_##name##_d2i, str, bp,      \
+                                     (void **)x, cb, u);                      \
+  }
 
-#define IMPLEMENT_PEM_write_bio(name, type, str, asn1) \
-OPENSSL_EXPORT int PEM_write_bio_##name(BIO *bp, type *x) \
-{ \
-return PEM_ASN1_write_bio((i2d_of_void *)i2d_##asn1,str,bp,x,NULL,NULL,0,NULL,NULL); \
-}
+#define IMPLEMENT_PEM_write_bio(name, type, str, asn1)                         \
+  static int pem_write_bio_##name##_i2d(const void *x, unsigned char **outp) { \
+    return i2d_##asn1((type *)x, outp);                                        \
+  }                                                                            \
+  OPENSSL_EXPORT int PEM_write_bio_##name(BIO *bp, type *x) {                  \
+    return PEM_ASN1_write_bio(pem_write_bio_##name##_i2d, str, bp, x, NULL,    \
+                              NULL, 0, NULL, NULL);                            \
+  }
 
-#define IMPLEMENT_PEM_write_bio_const(name, type, str, asn1) \
-OPENSSL_EXPORT int PEM_write_bio_##name(BIO *bp, const type *x) \
-{ \
-return PEM_ASN1_write_bio((i2d_of_void *)i2d_##asn1,str,bp,(void *)x,NULL,NULL,0,NULL,NULL); \
-}
+#define IMPLEMENT_PEM_write_bio_const(name, type, str, asn1)                   \
+  static int pem_write_bio_##name##_i2d(const void *x, unsigned char **outp) { \
+    return i2d_##asn1((const type *)x, outp);                                  \
+  }                                                                            \
+  OPENSSL_EXPORT int PEM_write_bio_##name(BIO *bp, const type *x) {            \
+    return PEM_ASN1_write_bio(pem_write_bio_##name##_i2d, str, bp, (void *)x,  \
+                              NULL, NULL, 0, NULL, NULL);                      \
+  }
 
-#define IMPLEMENT_PEM_write_cb_bio(name, type, str, asn1) \
-OPENSSL_EXPORT int PEM_write_bio_##name(BIO *bp, type *x, const EVP_CIPHER *enc, \
-	     unsigned char *kstr, int klen, pem_password_cb *cb, void *u) \
-	{ \
-	return PEM_ASN1_write_bio((i2d_of_void *)i2d_##asn1,str,bp,x,enc,kstr,klen,cb,u); \
-	}
+#define IMPLEMENT_PEM_write_cb_bio(name, type, str, asn1)                      \
+  static int pem_write_bio_##name##_i2d(const void *x, unsigned char **outp) { \
+    return i2d_##asn1((type *)x, outp);                                        \
+  }                                                                            \
+  OPENSSL_EXPORT int PEM_write_bio_##name(                                     \
+      BIO *bp, type *x, const EVP_CIPHER *enc, unsigned char *kstr, int klen,  \
+      pem_password_cb *cb, void *u) {                                          \
+    return PEM_ASN1_write_bio(pem_write_bio_##name##_i2d, str, bp, x, enc,     \
+                              kstr, klen, cb, u);                              \
+  }
 
-#define IMPLEMENT_PEM_write_cb_bio_const(name, type, str, asn1) \
-OPENSSL_EXPORT int PEM_write_bio_##name(BIO *bp, type *x, const EVP_CIPHER *enc, \
-	     unsigned char *kstr, int klen, pem_password_cb *cb, void *u) \
-	{ \
-	return PEM_ASN1_write_bio((i2d_of_void *)i2d_##asn1,str,bp,(void *)x,enc,kstr,klen,cb,u); \
-	}
+#define IMPLEMENT_PEM_write_cb_bio_const(name, type, str, asn1)                \
+  static int pem_write_bio_##name##_i2d(const void *x, unsigned char **outp) { \
+    return i2d_##asn1((const type *)x, outp);                                  \
+  }                                                                            \
+  OPENSSL_EXPORT int PEM_write_bio_##name(                                     \
+      BIO *bp, type *x, const EVP_CIPHER *enc, unsigned char *kstr, int klen,  \
+      pem_password_cb *cb, void *u) {                                          \
+    return PEM_ASN1_write_bio(pem_write_bio_##name##_i2d, str, bp, (void *)x,  \
+                              enc, kstr, klen, cb, u);                         \
+  }
 
 #define IMPLEMENT_PEM_write(name, type, str, asn1) \
 	IMPLEMENT_PEM_write_bio(name, type, str, asn1) \
diff --git a/src/include/openssl/ssl.h b/src/include/openssl/ssl.h
index 0d5a444..c0d44ce 100644
--- a/src/include/openssl/ssl.h
+++ b/src/include/openssl/ssl.h
@@ -3004,7 +3004,7 @@
 OPENSSL_EXPORT const char *SSL_get_psk_identity(const SSL *ssl);
 
 
-// QUIC Transport Parameters.
+// QUIC transport parameters.
 //
 // draft-ietf-quic-tls defines a new TLS extension quic_transport_parameters
 // used by QUIC for each endpoint to unilaterally declare its supported
@@ -3667,6 +3667,10 @@
 OPENSSL_EXPORT void SSL_CTX_set_ignore_tls13_downgrade(SSL_CTX *ctx,
                                                        int ignore);
 
+// SSL_set_ignore_tls13_downgrade configures whether |ssl| ignores the downgrade
+// signal in the server's random value.
+OPENSSL_EXPORT void SSL_set_ignore_tls13_downgrade(SSL *ssl, int ignore);
+
 // SSL_is_tls13_downgrade returns one if the TLS 1.3 anti-downgrade
 // mechanism would have aborted |ssl|'s handshake and zero otherwise.
 OPENSSL_EXPORT int SSL_is_tls13_downgrade(const SSL *ssl);
diff --git a/src/include/openssl/stack.h b/src/include/openssl/stack.h
index 15b6adf..c1bf520 100644
--- a/src/include/openssl/stack.h
+++ b/src/include/openssl/stack.h
@@ -86,10 +86,23 @@
 // STACK_OF(FOO), the macros would be sk_FOO_new, sk_FOO_pop etc.
 
 
+// stack_free_func is a function that frees an element in a stack. Note its
+// actual type is void (*)(T *) for some T. Low-level |sk_*| functions will be
+// passed a type-specific wrapper to call it correctly.
+typedef void (*stack_free_func)(void *ptr);
+
+// stack_copy_func is a function that copies an element in a stack. Note its
+// actual type is T *(*)(T *) for some T. Low-level |sk_*| functions will be
+// passed a type-specific wrapper to call it correctly.
+typedef void *(*stack_copy_func)(void *ptr);
+
 // stack_cmp_func is a comparison function that returns a value < 0, 0 or > 0
 // if |*a| is less than, equal to or greater than |*b|, respectively.  Note the
 // extra indirection - the function is given a pointer to a pointer to the
 // element. This differs from the usual qsort/bsearch comparison function.
+//
+// Note its actual type is int (*)(const T **, const T **). Low-level |sk_*|
+// functions will be passed a type-specific wrapper to call it correctly.
 typedef int (*stack_cmp_func)(const void **a, const void **b);
 
 // stack_st contains an array of pointers. It is not designed to be used
@@ -140,12 +153,17 @@
 OPENSSL_EXPORT void *sk_set(_STACK *sk, size_t i, void *p);
 
 // sk_free frees the given stack and array of pointers, but does nothing to
-// free the individual elements. Also see |sk_pop_free|.
+// free the individual elements. Also see |sk_pop_free_ex|.
 OPENSSL_EXPORT void sk_free(_STACK *sk);
 
-// sk_pop_free calls |free_func| on each element in the stack and then frees
-// the stack itself.
-OPENSSL_EXPORT void sk_pop_free(_STACK *sk, void (*free_func)(void *));
+// sk_pop_free_ex calls |free_func| on each element in the stack and then frees
+// the stack itself. Note this corresponds to |sk_FOO_pop_free|. It is named
+// |sk_pop_free_ex| as a workaround for existing code calling an older version
+// of |sk_pop_free|.
+OPENSSL_EXPORT void sk_pop_free_ex(_STACK *sk,
+                                   void (*call_free_func)(stack_free_func,
+                                                          void *),
+                                   stack_free_func free_func);
 
 // sk_insert inserts |p| into the stack at index |where|, moving existing
 // elements if needed. It returns the length of the new stack, or zero on
@@ -160,7 +178,7 @@
 // sk_delete_ptr removes, at most, one instance of |p| from the stack based on
 // pointer equality. If an instance of |p| is found then |p| is returned,
 // otherwise it returns NULL.
-OPENSSL_EXPORT void *sk_delete_ptr(_STACK *sk, void *p);
+OPENSSL_EXPORT void *sk_delete_ptr(_STACK *sk, const void *p);
 
 // sk_find returns the first value in the stack equal to |p|. If a comparison
 // function has been set on the stack, equality is defined by it, otherwise
@@ -173,7 +191,9 @@
 // Note this differs from OpenSSL. The type signature is slightly different, and
 // OpenSSL's sk_find will implicitly sort |sk| if it has a comparison function
 // defined.
-OPENSSL_EXPORT int sk_find(const _STACK *sk, size_t *out_index, void *p);
+OPENSSL_EXPORT int sk_find(const _STACK *sk, size_t *out_index, const void *p,
+                           int (*call_cmp_func)(stack_cmp_func, const void **,
+                                                const void **));
 
 // sk_shift removes and returns the first element in the stack, or returns NULL
 // if the stack is empty.
@@ -207,9 +227,20 @@
 // sk_deep_copy performs a copy of |sk| and of each of the non-NULL elements in
 // |sk| by using |copy_func|. If an error occurs, |free_func| is used to free
 // any copies already made and NULL is returned.
-OPENSSL_EXPORT _STACK *sk_deep_copy(const _STACK *sk,
-                                    void *(*copy_func)(void *),
-                                    void (*free_func)(void *));
+OPENSSL_EXPORT _STACK *sk_deep_copy(
+    const _STACK *sk, void *(*call_copy_func)(stack_copy_func, void *),
+    stack_copy_func copy_func, void (*call_free_func)(stack_free_func, void *),
+    stack_free_func free_func);
+
+
+// Deprecated functions.
+
+// sk_pop_free behaves like |sk_pop_free_ex| but performs an invalid function
+// pointer cast. It exists because some existing callers called |sk_pop_free|
+// directly.
+//
+// TODO(davidben): Migrate callers to bssl::UniquePtr and remove this.
+OPENSSL_EXPORT void sk_pop_free(_STACK *sk, stack_free_func free_func);
 
 
 // Defining stack types.
@@ -245,113 +276,126 @@
 #define BORINGSSL_DEFINE_STACK_TRAITS(name, type, is_const)
 #endif
 
-// Stack functions must be tagged unused to support file-local stack types.
-// Clang's -Wunused-function only allows unused static inline functions if they
-// are defined in a header.
-
 #define BORINGSSL_DEFINE_STACK_OF_IMPL(name, ptrtype, constptrtype)            \
   DECLARE_STACK_OF(name)                                                       \
                                                                                \
+  typedef void (*stack_##name##_free_func)(ptrtype);                           \
+  typedef ptrtype (*stack_##name##_copy_func)(ptrtype);                        \
   typedef int (*stack_##name##_cmp_func)(constptrtype *a, constptrtype *b);    \
                                                                                \
-  static inline OPENSSL_UNUSED STACK_OF(name) *                                \
+  OPENSSL_INLINE void sk_##name##_call_free_func(stack_free_func free_func,    \
+                                                 void *ptr) {                  \
+    ((stack_##name##_free_func)free_func)((ptrtype)ptr);                       \
+  }                                                                            \
+                                                                               \
+  OPENSSL_INLINE void *sk_##name##_call_copy_func(stack_copy_func copy_func,   \
+                                                  void *ptr) {                 \
+    return (void *)((stack_##name##_copy_func)copy_func)((ptrtype)ptr);        \
+  }                                                                            \
+                                                                               \
+  OPENSSL_INLINE int sk_##name##_call_cmp_func(                                \
+      stack_cmp_func cmp_func, const void **a, const void **b) {               \
+    constptrtype a_ptr = (constptrtype)*a;                                     \
+    constptrtype b_ptr = (constptrtype)*b;                                     \
+    return ((stack_##name##_cmp_func)cmp_func)(&a_ptr, &b_ptr);                \
+  }                                                                            \
+                                                                               \
+  OPENSSL_INLINE STACK_OF(name) *                                              \
       sk_##name##_new(stack_##name##_cmp_func comp) {                          \
     return (STACK_OF(name) *)sk_new((stack_cmp_func)comp);                     \
   }                                                                            \
                                                                                \
-  static inline OPENSSL_UNUSED STACK_OF(name) *sk_##name##_new_null(void) {    \
+  OPENSSL_INLINE STACK_OF(name) *sk_##name##_new_null(void) {                  \
     return (STACK_OF(name) *)sk_new_null();                                    \
   }                                                                            \
                                                                                \
-  static inline OPENSSL_UNUSED size_t sk_##name##_num(                         \
-      const STACK_OF(name) *sk) {                                              \
+  OPENSSL_INLINE size_t sk_##name##_num(const STACK_OF(name) *sk) {            \
     return sk_num((const _STACK *)sk);                                         \
   }                                                                            \
                                                                                \
-  static inline OPENSSL_UNUSED void sk_##name##_zero(STACK_OF(name) *sk) {     \
+  OPENSSL_INLINE void sk_##name##_zero(STACK_OF(name) *sk) {                   \
     sk_zero((_STACK *)sk);                                                     \
   }                                                                            \
                                                                                \
-  static inline OPENSSL_UNUSED ptrtype sk_##name##_value(                      \
-      const STACK_OF(name) *sk, size_t i) {                                    \
+  OPENSSL_INLINE ptrtype sk_##name##_value(const STACK_OF(name) *sk,           \
+                                           size_t i) {                         \
     return (ptrtype)sk_value((const _STACK *)sk, i);                           \
   }                                                                            \
                                                                                \
-  static inline OPENSSL_UNUSED ptrtype sk_##name##_set(STACK_OF(name) *sk,     \
-                                                       size_t i, ptrtype p) {  \
+  OPENSSL_INLINE ptrtype sk_##name##_set(STACK_OF(name) *sk, size_t i,         \
+                                         ptrtype p) {                          \
     return (ptrtype)sk_set((_STACK *)sk, i, (void *)p);                        \
   }                                                                            \
                                                                                \
-  static inline OPENSSL_UNUSED void sk_##name##_free(STACK_OF(name) *sk) {     \
+  OPENSSL_INLINE void sk_##name##_free(STACK_OF(name) * sk) {                  \
     sk_free((_STACK *)sk);                                                     \
   }                                                                            \
                                                                                \
-  static inline OPENSSL_UNUSED void sk_##name##_pop_free(                      \
-      STACK_OF(name) *sk, void (*free_func)(ptrtype p)) {                      \
-    sk_pop_free((_STACK *)sk, (void (*)(void *))free_func);                    \
+  OPENSSL_INLINE void sk_##name##_pop_free(                                    \
+      STACK_OF(name) * sk, stack_##name##_free_func free_func) {               \
+    sk_pop_free_ex((_STACK *)sk, sk_##name##_call_free_func,                   \
+                   (stack_free_func)free_func);                                \
   }                                                                            \
                                                                                \
-  static inline OPENSSL_UNUSED size_t sk_##name##_insert(                      \
-      STACK_OF(name) *sk, ptrtype p, size_t where) {                           \
+  OPENSSL_INLINE size_t sk_##name##_insert(STACK_OF(name) *sk, ptrtype p,      \
+                                           size_t where) {                     \
     return sk_insert((_STACK *)sk, (void *)p, where);                          \
   }                                                                            \
                                                                                \
-  static inline OPENSSL_UNUSED ptrtype sk_##name##_delete(STACK_OF(name) *sk,  \
-                                                          size_t where) {      \
+  OPENSSL_INLINE ptrtype sk_##name##_delete(STACK_OF(name) *sk,                \
+                                            size_t where) {                    \
     return (ptrtype)sk_delete((_STACK *)sk, where);                            \
   }                                                                            \
                                                                                \
-  static inline OPENSSL_UNUSED ptrtype sk_##name##_delete_ptr(                 \
-      STACK_OF(name) *sk, ptrtype p) {                                         \
-    return (ptrtype)sk_delete_ptr((_STACK *)sk, (void *)p);                    \
+  OPENSSL_INLINE ptrtype sk_##name##_delete_ptr(STACK_OF(name) *sk,            \
+                                                constptrtype p) {              \
+    return (ptrtype)sk_delete_ptr((_STACK *)sk, (const void *)p);              \
   }                                                                            \
                                                                                \
-  static inline OPENSSL_UNUSED int sk_##name##_find(                           \
-      const STACK_OF(name) *sk, size_t *out_index, ptrtype p) {                \
-    return sk_find((const _STACK *)sk, out_index, (void *)p);                  \
+  OPENSSL_INLINE int sk_##name##_find(const STACK_OF(name) *sk,                \
+                                      size_t * out_index, constptrtype p) {    \
+    return sk_find((const _STACK *)sk, out_index, (const void *)p,             \
+                   sk_##name##_call_cmp_func);                                 \
   }                                                                            \
                                                                                \
-  static inline OPENSSL_UNUSED ptrtype sk_##name##_shift(STACK_OF(name) *sk) { \
+  OPENSSL_INLINE ptrtype sk_##name##_shift(STACK_OF(name) *sk) {               \
     return (ptrtype)sk_shift((_STACK *)sk);                                    \
   }                                                                            \
                                                                                \
-  static inline OPENSSL_UNUSED size_t sk_##name##_push(STACK_OF(name) *sk,     \
-                                                       ptrtype p) {            \
+  OPENSSL_INLINE size_t sk_##name##_push(STACK_OF(name) *sk, ptrtype p) {      \
     return sk_push((_STACK *)sk, (void *)p);                                   \
   }                                                                            \
                                                                                \
-  static inline OPENSSL_UNUSED ptrtype sk_##name##_pop(STACK_OF(name) *sk) {   \
+  OPENSSL_INLINE ptrtype sk_##name##_pop(STACK_OF(name) *sk) {                 \
     return (ptrtype)sk_pop((_STACK *)sk);                                      \
   }                                                                            \
                                                                                \
-  static inline OPENSSL_UNUSED STACK_OF(name) *                                \
-      sk_##name##_dup(const STACK_OF(name) *sk) {                              \
+  OPENSSL_INLINE STACK_OF(name) * sk_##name##_dup(const STACK_OF(name) *sk) {  \
     return (STACK_OF(name) *)sk_dup((const _STACK *)sk);                       \
   }                                                                            \
                                                                                \
-  static inline OPENSSL_UNUSED void sk_##name##_sort(STACK_OF(name) *sk) {     \
+  OPENSSL_INLINE void sk_##name##_sort(STACK_OF(name) *sk) {                   \
     sk_sort((_STACK *)sk);                                                     \
   }                                                                            \
                                                                                \
-  static inline OPENSSL_UNUSED int sk_##name##_is_sorted(                      \
-      const STACK_OF(name) *sk) {                                              \
+  OPENSSL_INLINE int sk_##name##_is_sorted(const STACK_OF(name) *sk) {         \
     return sk_is_sorted((const _STACK *)sk);                                   \
   }                                                                            \
                                                                                \
-  static inline OPENSSL_UNUSED stack_##name##_cmp_func                         \
-      sk_##name##_set_cmp_func(STACK_OF(name) *sk,                             \
-                               stack_##name##_cmp_func comp) {                 \
+  OPENSSL_INLINE stack_##name##_cmp_func sk_##name##_set_cmp_func(             \
+      STACK_OF(name) *sk, stack_##name##_cmp_func comp) {                      \
     return (stack_##name##_cmp_func)sk_set_cmp_func((_STACK *)sk,              \
                                                     (stack_cmp_func)comp);     \
   }                                                                            \
                                                                                \
-  static inline OPENSSL_UNUSED STACK_OF(name) *                                \
+  OPENSSL_INLINE STACK_OF(name) *                                              \
       sk_##name##_deep_copy(const STACK_OF(name) *sk,                          \
                             ptrtype(*copy_func)(ptrtype),                      \
                             void (*free_func)(ptrtype)) {                      \
-    return (STACK_OF(name) *)sk_deep_copy((const _STACK *)sk,                  \
-                                          (void *(*)(void *))copy_func,        \
-                                          (void (*)(void *))free_func);        \
+    return (STACK_OF(name) *)sk_deep_copy(                                     \
+        (const _STACK *)sk, sk_##name##_call_copy_func,                        \
+        (stack_copy_func)copy_func, sk_##name##_call_free_func,                \
+        (stack_free_func)free_func);                                           \
   }
 
 // DEFINE_NAMED_STACK_OF defines |STACK_OF(name)| to be a stack whose elements
@@ -410,10 +454,14 @@
 struct DeleterImpl<
     Stack, typename std::enable_if<!StackTraits<Stack>::kIsConst>::type> {
   static void Free(Stack *sk) {
-    sk_pop_free(
-        reinterpret_cast<_STACK *>(sk),
-        reinterpret_cast<void (*)(void *)>(
-            DeleterImpl<typename StackTraits<Stack>::Type>::Free));
+    // sk_FOO_pop_free is defined by macros and bound by name, so we cannot
+    // access it from C++ here.
+    using Type = typename StackTraits<Stack>::Type;
+    sk_pop_free_ex(reinterpret_cast<_STACK *>(sk),
+                   [](stack_free_func /* unused */, void *ptr) {
+                     DeleterImpl<Type>::Free(reinterpret_cast<Type *>(ptr));
+                   },
+                   nullptr);
   }
 };
 
@@ -462,7 +510,7 @@
 // PushToStack pushes |elem| to |sk|. It returns true on success and false on
 // allocation failure.
 template <typename Stack>
-static inline
+inline
     typename std::enable_if<!internal::StackTraits<Stack>::kIsConst, bool>::type
     PushToStack(Stack *sk,
                 UniquePtr<typename internal::StackTraits<Stack>::Type> elem) {
@@ -478,12 +526,12 @@
 
 // Define begin() and end() for stack types so C++ range for loops work.
 template <typename Stack>
-static inline bssl::internal::StackIterator<Stack> begin(const Stack *sk) {
+inline bssl::internal::StackIterator<Stack> begin(const Stack *sk) {
   return bssl::internal::StackIterator<Stack>(sk, 0);
 }
 
 template <typename Stack>
-static inline bssl::internal::StackIterator<Stack> end(const Stack *sk) {
+inline bssl::internal::StackIterator<Stack> end(const Stack *sk) {
   return bssl::internal::StackIterator<Stack>(
       sk, sk_num(reinterpret_cast<const _STACK *>(sk)));
 }
diff --git a/src/include/openssl/thread.h b/src/include/openssl/thread.h
index 98073b0..91706fe 100644
--- a/src/include/openssl/thread.h
+++ b/src/include/openssl/thread.h
@@ -66,7 +66,7 @@
 #endif
 
 
-#if defined(OPENSSL_NO_THREADS)
+#if !defined(OPENSSL_THREADS)
 typedef struct crypto_mutex_st {
   char padding;  // Empty structs have different sizes in C and C++.
 } CRYPTO_MUTEX;
diff --git a/src/include/openssl/type_check.h b/src/include/openssl/type_check.h
index da78d70..e5d7047 100644
--- a/src/include/openssl/type_check.h
+++ b/src/include/openssl/type_check.h
@@ -72,10 +72,6 @@
 // CHECKED_CAST casts |p| from type |from| to type |to|.
 #define CHECKED_CAST(to, from, p) ((to) (1 ? (p) : (from)0))
 
-// CHECKED_PTR_OF casts a given pointer to void* and statically checks that it
-// was a pointer to |type|.
-#define CHECKED_PTR_OF(type, p) CHECKED_CAST(void*, type*, (p))
-
 #if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
 #define OPENSSL_COMPILE_ASSERT(cond, msg) _Static_assert(cond, #msg)
 #else
diff --git a/src/include/openssl/x509v3.h b/src/include/openssl/x509v3.h
index 53e20a0..7d1c306 100644
--- a/src/include/openssl/x509v3.h
+++ b/src/include/openssl/x509v3.h
@@ -57,7 +57,6 @@
 
 #include <openssl/bio.h>
 #include <openssl/conf.h>
-#include <openssl/lhash.h>
 #include <openssl/x509.h>
 
 #ifdef __cplusplus
@@ -612,7 +611,8 @@
 				  X509V3_CTX *ctx, CONF_VALUE *cnf, int is_nc);
 OPENSSL_EXPORT void X509V3_conf_free(CONF_VALUE *val);
 
-OPENSSL_EXPORT X509_EXTENSION *X509V3_EXT_conf_nid(LHASH_OF(CONF_VALUE) *conf, X509V3_CTX *ctx, int ext_nid, char *value);
+typedef struct x509_must_be_null_st X509_MUST_BE_NULL;
+OPENSSL_EXPORT X509_EXTENSION *X509V3_EXT_conf_nid(X509_MUST_BE_NULL *conf, X509V3_CTX *ctx, int ext_nid, char *value);
 OPENSSL_EXPORT X509_EXTENSION *X509V3_EXT_nconf_nid(CONF *conf, X509V3_CTX *ctx, int ext_nid, char *value);
 OPENSSL_EXPORT X509_EXTENSION *X509V3_EXT_nconf(CONF *conf, X509V3_CTX *ctx, char *name, char *value);
 OPENSSL_EXPORT int X509V3_EXT_add_nconf_sk(CONF *conf, X509V3_CTX *ctx, char *section, STACK_OF(X509_EXTENSION) **sk);
@@ -620,9 +620,6 @@
 OPENSSL_EXPORT int X509V3_EXT_REQ_add_nconf(CONF *conf, X509V3_CTX *ctx, char *section, X509_REQ *req);
 OPENSSL_EXPORT int X509V3_EXT_CRL_add_nconf(CONF *conf, X509V3_CTX *ctx, char *section, X509_CRL *crl);
 
-OPENSSL_EXPORT int X509V3_EXT_CRL_add_conf(LHASH_OF(CONF_VALUE) *conf, X509V3_CTX *ctx,
-			    char *section, X509_CRL *crl);
-
 OPENSSL_EXPORT int X509V3_add_value_bool_nf(char *name, int asn1_bool,
 			     STACK_OF(CONF_VALUE) **extlist);
 OPENSSL_EXPORT int X509V3_get_value_bool(CONF_VALUE *value, int *asn1_bool);
diff --git a/src/ssl/handshake_client.cc b/src/ssl/handshake_client.cc
index ae96bcf..e46b39f 100644
--- a/src/ssl/handshake_client.cc
+++ b/src/ssl/handshake_client.cc
@@ -600,7 +600,7 @@
             .subspan(SSL3_RANDOM_SIZE - sizeof(kTLS13DowngradeRandom));
     if (suffix == kTLS12DowngradeRandom || suffix == kTLS13DowngradeRandom) {
       ssl->s3->tls13_downgrade = true;
-      if (!ssl->ctx->ignore_tls13_downgrade) {
+      if (!hs->config->ignore_tls13_downgrade) {
         OPENSSL_PUT_ERROR(SSL, SSL_R_TLS13_DOWNGRADE);
         ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
         return ssl_hs_error;
diff --git a/src/ssl/internal.h b/src/ssl/internal.h
index 0535b8d..561b5d9 100644
--- a/src/ssl/internal.h
+++ b/src/ssl/internal.h
@@ -2456,6 +2456,10 @@
   // shed_handshake_config indicates that the handshake config (this object!)
   // should be freed after the handshake completes.
   bool shed_handshake_config : 1;
+
+  // ignore_tls13_downgrade is whether the connection should continue when the
+  // server random signals a downgrade.
+  bool ignore_tls13_downgrade:1;
 };
 
 // From RFC 8446, used in determining PSK modes.
diff --git a/src/ssl/ssl_lib.cc b/src/ssl/ssl_lib.cc
index 13b9cac..9c16de4 100644
--- a/src/ssl/ssl_lib.cc
+++ b/src/ssl/ssl_lib.cc
@@ -693,6 +693,7 @@
       ctx->signed_cert_timestamps_enabled;
   ssl->config->ocsp_stapling_enabled = ctx->ocsp_stapling_enabled;
   ssl->config->handoff = ctx->handoff;
+  ssl->config->ignore_tls13_downgrade = ctx->ignore_tls13_downgrade;
 
   if (!ssl->method->ssl_new(ssl.get()) ||
       !ssl->ctx->x509_method->ssl_new(ssl->s3->hs.get())) {
@@ -709,7 +710,8 @@
       channel_id_enabled(false),
       retain_only_sha256_of_client_certs(false),
       handoff(false),
-      shed_handshake_config(false) {
+      shed_handshake_config(false),
+      ignore_tls13_downgrade(false) {
   assert(ssl);
 }
 
@@ -2642,6 +2644,13 @@
   ctx->ignore_tls13_downgrade = !!ignore;
 }
 
+void SSL_set_ignore_tls13_downgrade(SSL *ssl, int ignore) {
+  if (!ssl->config) {
+    return;
+  }
+  ssl->config->ignore_tls13_downgrade = !!ignore;
+}
+
 void SSL_set_shed_handshake_config(SSL *ssl, int enable) {
   if (!ssl->config) {
     return;
diff --git a/src/ssl/ssl_test.cc b/src/ssl/ssl_test.cc
index 894bb14..61a47d3 100644
--- a/src/ssl/ssl_test.cc
+++ b/src/ssl/ssl_test.cc
@@ -48,7 +48,7 @@
 #include <sys/time.h>
 #endif
 
-#if !defined(OPENSSL_NO_THREADS)
+#if defined(OPENSSL_THREADS)
 #include <thread>
 #endif
 
@@ -4298,7 +4298,7 @@
 
 // These tests test multi-threaded behavior. They are intended to run with
 // ThreadSanitizer.
-#if !defined(OPENSSL_NO_THREADS)
+#if defined(OPENSSL_THREADS)
 TEST_P(SSLVersionTest, SessionCacheThreads) {
   SSL_CTX_set_options(server_ctx_.get(), SSL_OP_NO_TICKET);
   SSL_CTX_set_session_cache_mode(client_ctx_.get(), SSL_SESS_CACHE_BOTH);
diff --git a/src/ssl/ssl_x509.cc b/src/ssl/ssl_x509.cc
index 9fa800f..ec203b2 100644
--- a/src/ssl/ssl_x509.cc
+++ b/src/ssl/ssl_x509.cc
@@ -999,17 +999,25 @@
   return 1;
 }
 
-static SSL_SESSION *ssl_session_new_with_crypto_x509(void) {
-  return ssl_session_new(&ssl_crypto_x509_method).release();
-}
-
 SSL_SESSION *d2i_SSL_SESSION_bio(BIO *bio, SSL_SESSION **out) {
-  return ASN1_d2i_bio_of(SSL_SESSION, ssl_session_new_with_crypto_x509,
-                         d2i_SSL_SESSION, bio, out);
+  uint8_t *data;
+  size_t len;
+  if (!BIO_read_asn1(bio, &data, &len, 1024 * 1024)) {
+    return 0;
+  }
+  bssl::UniquePtr<uint8_t> free_data(data);
+  const uint8_t *ptr = data;
+  return d2i_SSL_SESSION(out, &ptr, static_cast<long>(len));
 }
 
 int i2d_SSL_SESSION_bio(BIO *bio, const SSL_SESSION *session) {
-  return ASN1_i2d_bio_of(SSL_SESSION, i2d_SSL_SESSION, bio, session);
+  uint8_t *data;
+  size_t len;
+  if (!SSL_SESSION_to_bytes(session, &data, &len)) {
+    return 0;
+  }
+  bssl::UniquePtr<uint8_t> free_data(data);
+  return BIO_write_all(bio, data, len);
 }
 
 IMPLEMENT_PEM_rw(SSL_SESSION, SSL_SESSION, PEM_STRING_SSL_SESSION, SSL_SESSION)
diff --git a/src/ssl/test/runner/runner.go b/src/ssl/test/runner/runner.go
index 4bcf603..9631e6e 100644
--- a/src/ssl/test/runner/runner.go
+++ b/src/ssl/test/runner/runner.go
@@ -5891,59 +5891,65 @@
 	})
 
 	// Test TLS 1.3's downgrade signal.
-	testCases = append(testCases, testCase{
-		name: "Downgrade-TLS12-Client",
-		config: Config{
-			Bugs: ProtocolBugs{
-				NegotiateVersion: VersionTLS12,
-			},
-		},
-		tls13Variant:       TLS13RFC,
-		expectedVersion:    VersionTLS12,
-		shouldFail:         true,
-		expectedError:      ":TLS13_DOWNGRADE:",
-		expectedLocalError: "remote error: illegal parameter",
-	})
-	testCases = append(testCases, testCase{
-		testType: serverTest,
-		name:     "Downgrade-TLS12-Server",
-		config: Config{
-			Bugs: ProtocolBugs{
-				SendSupportedVersions: []uint16{VersionTLS12},
-			},
-		},
-		tls13Variant:       TLS13RFC,
-		expectedVersion:    VersionTLS12,
-		shouldFail:         true,
-		expectedLocalError: "tls: downgrade from TLS 1.3 detected",
-	})
+	var downgradeTests = []struct {
+		name            string
+		version         uint16
+		clientShimError string
+	}{
+		{"TLS12", VersionTLS12, "tls: downgrade from TLS 1.3 detected"},
+		{"TLS11", VersionTLS11, "tls: downgrade from TLS 1.2 detected"},
+		// TLS 1.0 does not have a dedicated value.
+		{"TLS10", VersionTLS10, "tls: downgrade from TLS 1.2 detected"},
+	}
 
-	testCases = append(testCases, testCase{
-		name: "Downgrade-TLS11-Client",
-		config: Config{
-			Bugs: ProtocolBugs{
-				NegotiateVersion: VersionTLS11,
+	for _, test := range downgradeTests {
+		// The client should enforce the downgrade sentinel.
+		testCases = append(testCases, testCase{
+			name: "Downgrade-" + test.name + "-Client",
+			config: Config{
+				Bugs: ProtocolBugs{
+					NegotiateVersion: test.version,
+				},
 			},
-		},
-		tls13Variant:       TLS13RFC,
-		expectedVersion:    VersionTLS11,
-		shouldFail:         true,
-		expectedError:      ":TLS13_DOWNGRADE:",
-		expectedLocalError: "remote error: illegal parameter",
-	})
-	testCases = append(testCases, testCase{
-		testType: serverTest,
-		name:     "Downgrade-TLS11-Server",
-		config: Config{
-			Bugs: ProtocolBugs{
-				SendSupportedVersions: []uint16{VersionTLS11},
+			tls13Variant:       TLS13RFC,
+			expectedVersion:    test.version,
+			shouldFail:         true,
+			expectedError:      ":TLS13_DOWNGRADE:",
+			expectedLocalError: "remote error: illegal parameter",
+		})
+
+		// The client should ignore the downgrade sentinel if
+		// configured.
+		testCases = append(testCases, testCase{
+			name: "Downgrade-" + test.name + "-Client-Ignore",
+			config: Config{
+				Bugs: ProtocolBugs{
+					NegotiateVersion: test.version,
+				},
 			},
-		},
-		tls13Variant:       TLS13RFC,
-		expectedVersion:    VersionTLS11,
-		shouldFail:         true,
-		expectedLocalError: "tls: downgrade from TLS 1.2 detected",
-	})
+			tls13Variant:    TLS13RFC,
+			expectedVersion: test.version,
+			flags: []string{
+				"-ignore-tls13-downgrade",
+				"-expect-tls13-downgrade",
+			},
+		})
+
+		// The server should emit the downgrade signal.
+		testCases = append(testCases, testCase{
+			testType: serverTest,
+			name:     "Downgrade-" + test.name + "-Server",
+			config: Config{
+				Bugs: ProtocolBugs{
+					SendSupportedVersions: []uint16{test.version},
+				},
+			},
+			tls13Variant:       TLS13RFC,
+			expectedVersion:    test.version,
+			shouldFail:         true,
+			expectedLocalError: test.clientShimError,
+		})
+	}
 
 	// Test that the draft TLS 1.3 variants don't trigger the downgrade logic.
 	testCases = append(testCases, testCase{
diff --git a/src/util/fipstools/fipscommon/ar.go b/src/util/ar/ar.go
similarity index 79%
rename from src/util/fipstools/fipscommon/ar.go
rename to src/util/ar/ar.go
index 85b378d..f5dee62 100644
--- a/src/util/fipstools/fipscommon/ar.go
+++ b/src/util/ar/ar.go
@@ -14,11 +14,12 @@
 
 // ar.go contains functions for parsing .a archive files.
 
-package fipscommon
+package ar
 
 import (
 	"bytes"
 	"errors"
+	"fmt"
 	"io"
 	"strconv"
 	"strings"
@@ -113,6 +114,33 @@
 			name = strings.TrimRight(name, "/")
 		}
 
+		// Post-processing for BSD:
+		// https://en.wikipedia.org/wiki/Ar_(Unix)#BSD_variant
+		//
+		// If the name is of the form #1/XXX, XXX identifies the length of the
+		// name, and the name itself is stored as a prefix of the data, possibly
+		// null-padded.
+
+		var namelen uint
+		n, err := fmt.Sscanf(name, "#1/%d", &namelen)
+		if err == nil && n == 1 && len(contents) >= int(namelen) {
+			name = string(contents[:namelen])
+			contents = contents[namelen:]
+
+			// Names can be null padded; find the first null (if any). Note that
+			// this also handles the case of a null followed by non-null
+			// characters. It's not clear whether those can ever show up in
+			// practice, but we might as well handle them in case they can show
+			// up.
+			var null int
+			for ; null < len(name); null++ {
+				if name[null] == 0 {
+					break
+				}
+			}
+			name = name[:null]
+		}
+
 		ret[name] = contents
 	}
 
diff --git a/src/util/fipstools/delocate/delocate.go b/src/util/fipstools/delocate/delocate.go
index a8c4fd0..4734285 100644
--- a/src/util/fipstools/delocate/delocate.go
+++ b/src/util/fipstools/delocate/delocate.go
@@ -26,6 +26,7 @@
 	"strconv"
 	"strings"
 
+	"boringssl.googlesource.com/boringssl/util/ar"
 	"boringssl.googlesource.com/boringssl/util/fipstools/fipscommon"
 )
 
@@ -1425,7 +1426,7 @@
 			}
 			defer arFile.Close()
 
-			ar, err := fipscommon.ParseAR(arFile)
+			ar, err := ar.ParseAR(arFile)
 			if err != nil {
 				return err
 			}
diff --git a/src/util/fipstools/inject-hash/inject-hash.go b/src/util/fipstools/inject_hash/inject_hash.go
similarity index 96%
rename from src/util/fipstools/inject-hash/inject-hash.go
rename to src/util/fipstools/inject_hash/inject_hash.go
index 14418a3..29307c0 100644
--- a/src/util/fipstools/inject-hash/inject-hash.go
+++ b/src/util/fipstools/inject_hash/inject_hash.go
@@ -12,7 +12,7 @@
 // OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
 
-// inject-hash parses an archive containing a file object file. It finds a FIPS
+// inject_hash parses an archive containing a file object file. It finds a FIPS
 // module inside that object, calculates its hash and replaces the default hash
 // value in the object with the calculated value.
 package main
@@ -29,6 +29,7 @@
 	"io/ioutil"
 	"os"
 
+	"boringssl.googlesource.com/boringssl/util/ar"
 	"boringssl.googlesource.com/boringssl/util/fipstools/fipscommon"
 )
 
@@ -45,7 +46,7 @@
 		}
 		defer arFile.Close()
 
-		ar, err := fipscommon.ParseAR(arFile)
+		ar, err := ar.ParseAR(arFile)
 		if err != nil {
 			return err
 		}
diff --git a/src/util/read_symbols.go b/src/util/read_symbols.go
new file mode 100644
index 0000000..5e3a177
--- /dev/null
+++ b/src/util/read_symbols.go
@@ -0,0 +1,170 @@
+// Copyright (c) 2018, Google Inc.
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+// read_symbols.go scans one or more .a files and, for each object contained in
+// the .a files, reads the list of symbols in that object file.
+package main
+
+import (
+	"bytes"
+	"debug/elf"
+	"debug/macho"
+	"flag"
+	"fmt"
+	"os"
+	"runtime"
+	"sort"
+	"strings"
+
+	"boringssl.googlesource.com/boringssl/util/ar"
+)
+
+const (
+	ObjFileFormatELF   = "elf"
+	ObjFileFormatMachO = "macho"
+)
+
+var outFlag = flag.String("out", "-", "File to write output symbols")
+var objFileFormat = flag.String("obj-file-format", defaultObjFileFormat(runtime.GOOS), "Object file format to expect (options are elf, macho)")
+
+func defaultObjFileFormat(goos string) string {
+	switch goos {
+	case "linux":
+		return ObjFileFormatELF
+	case "darwin":
+		return ObjFileFormatMachO
+	default:
+		// By returning a value here rather than panicking, the user can still
+		// cross-compile from an unsupported platform to a supported platform by
+		// overriding this default with a flag. If the user doesn't provide the
+		// flag, we will panic during flag parsing.
+		return "unsupported"
+	}
+}
+
+func main() {
+	flag.Parse()
+	if flag.NArg() < 1 {
+		fmt.Fprintf(os.Stderr, "Usage: %s [-out OUT] [-obj-file-format FORMAT] ARCHIVE_FILE [ARCHIVE_FILE [...]]\n", os.Args[0])
+		os.Exit(1)
+	}
+	archiveFiles := flag.Args()
+
+	out := os.Stdout
+	if *outFlag != "-" {
+		var err error
+		out, err = os.Create(*outFlag)
+		nilOrPanic(err, "failed to open output file")
+		defer out.Close()
+	}
+
+	var symbols []string
+	// Only add first instance of any symbol; keep track of them in this map.
+	added := make(map[string]bool)
+	for _, archive := range archiveFiles {
+		f, err := os.Open(archive)
+		nilOrPanic(err, "failed to open archive file %s", archive)
+		objectFiles, err := ar.ParseAR(f)
+		nilOrPanic(err, "failed to read archive file %s", archive)
+
+		for name, contents := range objectFiles {
+			if !strings.HasSuffix(name, ".o") {
+				continue
+			}
+			for _, s := range listSymbols(name, contents) {
+				if !added[s] {
+					added[s] = true
+					symbols = append(symbols, s)
+				}
+			}
+		}
+	}
+	sort.Strings(symbols)
+	for _, s := range symbols {
+		// Filter out C++ mangled names.
+		prefix := "_Z"
+		if runtime.GOOS == "darwin" {
+			prefix = "__Z"
+		}
+		if !strings.HasPrefix(s, prefix) {
+			fmt.Fprintln(out, s)
+		}
+	}
+}
+
+// listSymbols lists the exported symbols from an object file.
+func listSymbols(name string, contents []byte) []string {
+	switch *objFileFormat {
+	case ObjFileFormatELF:
+		return listSymbolsELF(name, contents)
+	case ObjFileFormatMachO:
+		return listSymbolsMachO(name, contents)
+	default:
+		panic(fmt.Errorf("unsupported object file format %v", *objFileFormat))
+	}
+}
+
+func listSymbolsELF(name string, contents []byte) []string {
+	f, err := elf.NewFile(bytes.NewReader(contents))
+	nilOrPanic(err, "failed to parse ELF file %s", name)
+	syms, err := f.Symbols()
+	nilOrPanic(err, "failed to read symbol names from ELF file %s", name)
+
+	var names []string
+	for _, sym := range syms {
+		// Only include exported, defined symbols
+		if elf.ST_BIND(sym.Info) != elf.STB_LOCAL && sym.Section != elf.SHN_UNDEF {
+			names = append(names, sym.Name)
+		}
+	}
+	return names
+}
+
+func listSymbolsMachO(name string, contents []byte) []string {
+	f, err := macho.NewFile(bytes.NewReader(contents))
+	nilOrPanic(err, "failed to parse Mach-O file %s", name)
+	if f.Symtab == nil {
+		return nil
+	}
+	var names []string
+	for _, sym := range f.Symtab.Syms {
+		// Source: https://opensource.apple.com/source/xnu/xnu-3789.51.2/EXTERNAL_HEADERS/mach-o/nlist.h.auto.html
+		const (
+			N_PEXT uint8 = 0x10 // Private external symbol bit
+			N_EXT  uint8 = 0x01 // External symbol bit, set for external symbols
+			N_TYPE uint8 = 0x0e // mask for the type bits
+
+			N_UNDF uint8 = 0x0 // undefined, n_sect == NO_SECT
+			N_ABS  uint8 = 0x2 // absolute, n_sect == NO_SECT
+			N_SECT uint8 = 0xe // defined in section number n_sect
+			N_PBUD uint8 = 0xc // prebound undefined (defined in a dylib)
+			N_INDR uint8 = 0xa // indirect
+		)
+
+		// Only include exported, defined symbols.
+		if sym.Type&N_EXT != 0 && sym.Type&N_TYPE != N_UNDF {
+			if len(sym.Name) == 0 || sym.Name[0] != '_' {
+				panic(fmt.Errorf("unexpected symbol without underscore prefix: %v", sym.Name))
+			}
+			names = append(names, sym.Name[1:])
+		}
+	}
+	return names
+}
+
+func nilOrPanic(err error, f string, args ...interface{}) {
+	if err != nil {
+		panic(fmt.Errorf(f+": %v", append(args, err)...))
+	}
+}