]> git.ipfire.org Git - thirdparty/apache/httpd.git/commitdiff
Merge r1933586, r1916058, r1916068, r1933631, r1929891 from trunk:
authorJoe Orton <jorton@apache.org>
Thu, 4 Jun 2026 08:50:44 +0000 (08:50 +0000)
committerJoe Orton <jorton@apache.org>
Thu, 4 Jun 2026 08:50:44 +0000 (08:50 +0000)
Fix OpenSSL 4.0 compatibility and test that in CI.

CI: Update to test OpenSSL 4.0.0 explicitly.
CI: No longer disable deprecated-declaration warnings for OpenSSL 3.4 -Werror build.

* modules/ssl/ssl_engine_kernel.c (ssl_hook_UserCheck): Change name to
  const X509_NAME *.
  (ssl_callback_proxy_cert): Change ca_name, issuer, and ca_issuer to
  const X509_NAME *.

* modules/ssl/ssl_engine_log.c (ssl_log_cert_error): Change cert
  parameter to const X509 *. Use X509_get0_serialNumber,
  X509_get0_notBefore, and X509_get0_notAfter instead of non-const
  variants.
  (ssl_log_xerror, ssl_log_cxerror, ssl_log_rxerror): Change cert
  parameter to const X509 *.

* modules/ssl/ssl_engine_vars.c (ssl_var_lookup_ssl_cert_dn): Change
  xsname parameter to const X509_NAME *.
  (ssl_var_lookup_ssl_cert_dn_oneline): Change xsname parameter to
  const X509_NAME *.
  (ssl_var_lookup_ssl_cert): Change xsname to const X509_NAME *.
  (ssl_var_lookup_ssl_cert_rfc4523_cea): Change issuer to const
  X509_NAME *.

* modules/ssl/ssl_private.h (ssl_log_xerror, ssl_log_cxerror,
  ssl_log_rxerror): Update declarations to use const X509 *.

* modules/ssl/ssl_util_ssl.c (modssl_X509_NAME_to_string): Change dn
  parameter to const X509_NAME *.
  (getIDs): Change subj to const X509_NAME *.

* modules/ssl/ssl_util_ssl.h (modssl_X509_NAME_to_string): Update
  declaration to use const X509_NAME *.

* support/ab.c (ssl_print_cert_info): Change dn to const X509_NAME *.

mod_ssl: use ASN1_STRING accessor API in dump_extn_value:

* modules/ssl/ssl_engine_vars.c (dump_extn_value): Use
  ASN1_STRING_get0_data() and ASN1_STRING_length() rather than
  directly dereferencing the ASN1_OCTET_STRING structure, which is
  opaque in OpenSSL 4.0.
* modules/ssl/ssl_private.h: Add compat macros for
  ASN1_STRING_get0_data and ASN1_STRING_length for pre-1.1 API.

mod_ssl: constify ASN1_TIME pointers, use X509_get0_not{Before,After}:

* modules/ssl/ssl_engine_vars.c (ssl_var_lookup_ssl_cert_valid,
  ssl_var_lookup_ssl_cert_remain): Constify ASN1_TIME * parameter.
  (ssl_var_lookup_ssl_cert): Use X509_get0_notBefore() and
  X509_get0_notAfter() which return const pointers.
  (ssl_var_lookup_ssl_cert_remain): Use ASN1_TIME_check() directly
  rather than INVALID_ASN1_TIME macro which dereferences the
  ASN1_TIME structure.
  (dump_extn_value): Constify ASN1_OCTET_STRING * parameter.
* modules/ssl/ssl_private.h: Add compat macros for
  X509_get0_before and X509_get0_after for pre-1.1 API.

mod_ssl: constify X509_NAME_ENTRY and X509_EXTENSION pointers:

* modules/ssl/ssl_engine_vars.c (ssl_var_lookup_ssl_cert_dn,
  extract_dn): Constify X509_NAME_ENTRY * variables, constify
  X509_NAME * parameter of extract_dn, drop unnecessary casts
  on X509_NAME_ENTRY_get_object() calls.
  (ssl_ext_list): Use MODSSL_X509_EXT_CONST for X509_EXTENSION *
  since X509_EXTENSION accessors are only constified in OpenSSL 4.
* modules/ssl/ssl_util_ssl.c, modules/ssl/ssl_util_ssl.h
  (modssl_X509_NAME_ENTRY_to_string): Constify X509_NAME_ENTRY *
  parameter.
* modules/ssl/ssl_private.h: Add MODSSL_X509_EXT_CONST, defined
  as const for OpenSSL 4+ and empty otherwise.

* modules/ssl/ssl_util_ssl.c (asn1_string_convert): Constify
  ASN1_STRING * argument.
* modules/ssl/ssl_engine_ocsp.c (extract_responder_uri): Use
  modssl_ASN1_STRING_convert instead of directly accessing ASN1_STRING
  data pointer.

* modules/ssl/ssl_util_ssl.c (modssl_ASN1_STRING_convert): Rename from
  asn1_string_convert and export function.
  (asn1_string_to_utf8): Update to use modssl_ASN1_STRING_convert.
  (modssl_X509_NAME_ENTRY_to_string): Update to use
  modssl_ASN1_STRING_convert.

* modules/ssl/ssl_util_ssl.h (modssl_ASN1_STRING_convert): Declare new
  function.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
CI: Add OpenSSL 3.1 builds, including a no-engine build.
(attempt to use 3.2 failed, unsure why)
- add OpenSSL build binaries to $PATH

CI: add OpenSSL 3.2, test OpenSSL 3.x using Apache::Test
trunk to pick up r1916067.

CI: The OpenSSL no-engine config option is redundant as of 4.0, remove.

CI: Try to fix ab failures during OpenSSL ech job, set RPATH via LDFLAGS

CI: For OpenSSL branch builds, always build a fresh version of the
OpenSSL branch and cache the commit hash to allow checking for freshness.
Also clone with --depth=1 to save time+bandwidth.

Reviewed by: jorton, covener (skimmed+CI results), rpluem
Github: closes #642

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1934973 13f79535-47bb-0310-9956-ffa450edef68

12 files changed:
.github/workflows/linux.yml
CHANGES
modules/ssl/ssl_engine_kernel.c
modules/ssl/ssl_engine_log.c
modules/ssl/ssl_engine_ocsp.c
modules/ssl/ssl_engine_vars.c
modules/ssl/ssl_private.h
modules/ssl/ssl_util_ssl.c
modules/ssl/ssl_util_ssl.h
support/ab.c
test/travis_before_linux.sh
test/travis_run_linux.sh

index 6988f36443209ebafb6c29c5db498a2918af2f00..e603cc8bdc842db54b3050ed2ecc3b195d504473 100644 (file)
@@ -260,6 +260,58 @@ jobs:
           #     APR_VERSION=1.7.3
           #     APU_VERSION=1.6.3
           # APU_CONFIG="--with-crypto --with-ldap"
+          # -------------------------------------------------------------------------
+          - name: OpenSSL 3.0 LTS
+            config: --enable-mods-shared=most --enable-maintainer-mode --disable-md --disable-http2 --disable-ldap --disable-crypto
+            env: |
+              TEST_OPENSSL3=3.0.18
+              APR_VERSION=1.7.6
+              APU_VERSION=1.6.3
+              APU_CONFIG="--without-crypto"
+            pkgs: subversion
+          # -------------------------------------------------------------------------
+          - name: OpenSSL 3.4 -Werror
+            config: --enable-mods-shared=most --enable-maintainer-mode --disable-md --disable-http2 --disable-ldap --disable-crypto
+            notest-cflags: -Werror -O2
+            env: |
+              TEST_OPENSSL3=3.4.4
+              APR_VERSION=1.7.6
+              APU_VERSION=1.6.3
+              APU_CONFIG="--without-crypto"
+            pkgs: subversion
+          # -------------------------------------------------------------------------
+          - name: OpenSSL 3.4 no-engine
+            config: --enable-mods-shared=most --enable-maintainer-mode --disable-md --disable-http2 --disable-ldap --disable-crypto
+            env: |
+              TEST_OPENSSL3=3.4.4
+              OPENSSL_CONFIG=no-engine
+              APR_VERSION=1.7.6
+              APU_VERSION=1.6.3
+              APU_CONFIG="--without-crypto"
+            pkgs: subversion
+          # -------------------------------------------------------------------------
+          - name: OpenSSL 3.5 no-engine -Werror
+            config: --enable-mods-shared=most --enable-maintainer-mode --disable-md --disable-http2 --disable-ldap --disable-crypto
+            notest-cflags: -Werror -O2
+            env: |
+              TEST_OPENSSL3=3.5.5
+              OPENSSL_CONFIG=no-engine
+              APR_VERSION=1.7.6
+              APU_VERSION=1.6.3
+              APU_CONFIG="--without-crypto"
+            pkgs: subversion
+          # -------------------------------------------------------------------------
+          - name: OpenSSL 4.0
+            config: --enable-mods-shared=most --enable-maintainer-mode --disable-md --disable-http2 --disable-ldap --disable-crypto
+            notest-cflags: -Werror -O2
+            env: |
+              TEST_OPENSSL3=4.0.0
+              OPENSSL_CONFIG=
+              APR_VERSION=1.7.6
+              APU_VERSION=1.6.3
+              APU_CONFIG="--without-crypto"
+            pkgs: subversion
+          # -------------------------------------------------------------------------
     runs-on: ${{ matrix.os == '' && 'ubuntu-latest' || matrix.os }}
     timeout-minutes: 30
     env:
diff --git a/CHANGES b/CHANGES
index 4d21cf5b9fe2b84a19f80990602167af5d8042d2..216e050c202d9d7ea2c2df974af01c03f723d8a8 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,8 @@
                                                          -*- coding: utf-8 -*-
 Changes with Apache 2.4.68
 
+  *) mod_ssl, ab: Add support for OpenSSL 4.0.  [Joe Orton]
+
   *) mod_ssl: Add SerialNumber as a recognized attribute type for SSL
      distinguished name variables.  [Michael Osipov <michaelo apache.org>,
      Benjamin Demarteau <benjamin.demarteau liege.be>]
index bcc90f2e567f13c8ffde608a6f549f15f207deac..88ff5d19023348af38c6374d9361aab9ef946a4e 100644 (file)
@@ -1256,7 +1256,7 @@ int ssl_hook_UserCheck(request_rec *r)
     }
 
     if (!sslconn->client_dn) {
-        X509_NAME *name = X509_get_subject_name(sslconn->client_cert);
+        const X509_NAME *name = X509_get_subject_name(sslconn->client_cert);
         char *cp = X509_NAME_oneline(name, NULL, 0);
         sslconn->client_dn = apr_pstrdup(r->connection->pool, cp);
         OPENSSL_free(cp);
@@ -1780,7 +1780,7 @@ int ssl_callback_proxy_cert(SSL *ssl, X509 **x509, EVP_PKEY **pkey)
     server_rec *s = mySrvFromConn(c);
     SSLSrvConfigRec *sc = mySrvConfig(s);
     SSLDirConfigRec *dc = myDirConfigFromConn(c);
-    X509_NAME *ca_name, *issuer, *ca_issuer;
+    const X509_NAME *ca_name, *issuer, *ca_issuer;
     X509_INFO *info;
     X509 *ca_cert;
     STACK_OF(X509_NAME) *ca_list;
index 3b3ceacf0a5300a227797de48fca446b89269b44..341cc0d3e68dcea19bad5505558dce61afd33639 100644 (file)
@@ -126,7 +126,7 @@ void ssl_log_ssl_error(const char *file, int line, int level, server_rec *s)
 static void ssl_log_cert_error(const char *file, int line, int level,
                                apr_status_t rv, const server_rec *s,
                                const conn_rec *c, const request_rec *r,
-                               apr_pool_t *p, X509 *cert, const char *format,
+                               apr_pool_t *p, const X509 *cert, const char *format,
                                va_list ap)
 {
     char buf[HUGE_STRING_LEN];
@@ -167,14 +167,14 @@ static void ssl_log_cert_error(const char *file, int line, int level,
             }
 
             BIO_puts(bio, " / serial: ");
-            if (i2a_ASN1_INTEGER(bio, X509_get_serialNumber(cert)) == -1)
+            if (i2a_ASN1_INTEGER(bio, X509_get0_serialNumber(cert)) == -1)
                 BIO_puts(bio, "(ERROR)");
 
             BIO_puts(bio, " / notbefore: ");
-            ASN1_TIME_print(bio, X509_get_notBefore(cert));
+            ASN1_TIME_print(bio, X509_get0_notBefore(cert));
 
             BIO_puts(bio, " / notafter: ");
-            ASN1_TIME_print(bio, X509_get_notAfter(cert));
+            ASN1_TIME_print(bio, X509_get0_notAfter(cert));
 
             BIO_puts(bio, "]");
 
@@ -209,7 +209,7 @@ static void ssl_log_cert_error(const char *file, int line, int level,
  * in the other cases we use the connection and request pool, respectively).
  */
 void ssl_log_xerror(const char *file, int line, int level, apr_status_t rv,
-                    apr_pool_t *ptemp, server_rec *s, X509 *cert,
+                    apr_pool_t *ptemp, server_rec *s, const X509 *cert,
                     const char *fmt, ...)
 {
     if (APLOG_IS_LEVEL(s,level)) {
@@ -222,7 +222,7 @@ void ssl_log_xerror(const char *file, int line, int level, apr_status_t rv,
 }
 
 void ssl_log_cxerror(const char *file, int line, int level, apr_status_t rv,
-                     conn_rec *c, X509 *cert, const char *fmt, ...)
+                     conn_rec *c, const X509 *cert, const char *fmt, ...)
 {
     if (APLOG_IS_LEVEL(mySrvFromConn(c),level)) {
        va_list ap;
@@ -234,7 +234,7 @@ void ssl_log_cxerror(const char *file, int line, int level, apr_status_t rv,
 }
 
 void ssl_log_rxerror(const char *file, int line, int level, apr_status_t rv,
-                     request_rec *r, X509 *cert, const char *fmt, ...)
+                     request_rec *r, const X509 *cert, const char *fmt, ...)
 {
     if (APLOG_R_IS_LEVEL(r,level)) {
        va_list ap;
index 5e045125585309243a70e8fdccae5e537065990f..539ed103eae38647fb86f7e5b7b27c7502dd270c 100644 (file)
@@ -38,8 +38,8 @@ static const char *extract_responder_uri(X509 *cert, apr_pool_t *pool)
         /* Name found in extension, and is a URI: */
         if (OBJ_obj2nid(value->method) == NID_ad_OCSP
             && value->location->type == GEN_URI) {
-            result = apr_pstrdup(pool,
-                                 (char *)value->location->d.uniformResourceIdentifier->data);
+            const ASN1_STRING *uri = value->location->d.uniformResourceIdentifier;
+            result = modssl_ASN1_STRING_convert(pool, uri, 0);
         }
     }
 
index 85f075978df43e65664385d143dfead272ccbe38..4307815eec871ea2a528fd46c182617c0ffc5e45 100644 (file)
 
 static char *ssl_var_lookup_ssl(apr_pool_t *p, SSLConnRec *sslconn, request_rec *r, char *var);
 static char *ssl_var_lookup_ssl_cert(apr_pool_t *p, request_rec *r, X509 *xs, char *var);
-static char *ssl_var_lookup_ssl_cert_dn(apr_pool_t *p, X509_NAME *xsname, const char *var);
+static char *ssl_var_lookup_ssl_cert_dn(apr_pool_t *p, const X509_NAME *xsname, const char *var);
 static char *ssl_var_lookup_ssl_cert_san(apr_pool_t *p, X509 *xs, char *var);
-static char *ssl_var_lookup_ssl_cert_valid(apr_pool_t *p, ASN1_TIME *tm);
-static char *ssl_var_lookup_ssl_cert_remain(apr_pool_t *p, ASN1_TIME *tm);
+static char *ssl_var_lookup_ssl_cert_valid(apr_pool_t *p, const ASN1_TIME *tm);
+static char *ssl_var_lookup_ssl_cert_remain(apr_pool_t *p, const ASN1_TIME *tm);
 static char *ssl_var_lookup_ssl_cert_serial(apr_pool_t *p, X509 *xs);
 static char *ssl_var_lookup_ssl_cert_chain(apr_pool_t *p, STACK_OF(X509) *sk, char *var);
 static char *ssl_var_lookup_ssl_cert_rfc4523_cea(apr_pool_t *p, SSL *ssl);
@@ -444,7 +444,7 @@ static char *ssl_var_lookup_ssl(apr_pool_t *p, SSLConnRec *sslconn,
 }
 
 static char *ssl_var_lookup_ssl_cert_dn_oneline(apr_pool_t *p, request_rec *r,
-                                                X509_NAME *xsname)
+                                                const X509_NAME *xsname)
 {
     char *result = NULL;
     SSLDirConfigRec *dc;
@@ -476,7 +476,7 @@ static char *ssl_var_lookup_ssl_cert(apr_pool_t *p, request_rec *r, X509 *xs,
 {
     char *result;
     BOOL resdup;
-    X509_NAME *xsname;
+    const X509_NAME *xsname;
     int nid;
 
     result = NULL;
@@ -490,13 +490,13 @@ static char *ssl_var_lookup_ssl_cert(apr_pool_t *p, request_rec *r, X509 *xs,
         result = ssl_var_lookup_ssl_cert_serial(p, xs);
     }
     else if (strcEQ(var, "V_START")) {
-        result = ssl_var_lookup_ssl_cert_valid(p, X509_get_notBefore(xs));
+        result = ssl_var_lookup_ssl_cert_valid(p, X509_get0_notBefore(xs));
     }
     else if (strcEQ(var, "V_END")) {
-        result = ssl_var_lookup_ssl_cert_valid(p, X509_get_notAfter(xs));
+        result = ssl_var_lookup_ssl_cert_valid(p, X509_get0_notAfter(xs));
     }
     else if (strcEQ(var, "V_REMAIN")) {
-        result = ssl_var_lookup_ssl_cert_remain(p, X509_get_notAfter(xs));
+        result = ssl_var_lookup_ssl_cert_remain(p, X509_get0_notAfter(xs));
         resdup = FALSE;
     }
     else if (*var && strcEQ(var+1, "_DN")) {
@@ -584,12 +584,12 @@ static const struct {
     { NULL,    0,                          0 }
 };
 
-static char *ssl_var_lookup_ssl_cert_dn(apr_pool_t *p, X509_NAME *xsname,
+static char *ssl_var_lookup_ssl_cert_dn(apr_pool_t *p, const X509_NAME *xsname,
                                         const char *var)
 {
     const char *ptr;
     char *result;
-    X509_NAME_ENTRY *xsne;
+    const X509_NAME_ENTRY *xsne;
     int i, j, n, idx = 0, raw = 0;
     apr_size_t varlen;
 
@@ -616,7 +616,7 @@ static char *ssl_var_lookup_ssl_cert_dn(apr_pool_t *p, X509_NAME *xsname,
             for (j = 0; j < X509_NAME_entry_count(xsname); j++) {
                 xsne = X509_NAME_get_entry(xsname, j);
 
-                n =OBJ_obj2nid((ASN1_OBJECT *)X509_NAME_ENTRY_get_object(xsne));
+                n = OBJ_obj2nid(X509_NAME_ENTRY_get_object(xsne));
 
                 if (n == ssl_var_lookup_ssl_cert_dn_rec[i].nid && idx-- == 0) {
                     result = modssl_X509_NAME_ENTRY_to_string(p, xsne, raw);
@@ -673,7 +673,7 @@ static char *ssl_var_lookup_ssl_cert_san(apr_pool_t *p, X509 *xs, char *var)
         return NULL;
 }
 
-static char *ssl_var_lookup_ssl_cert_valid(apr_pool_t *p, ASN1_TIME *tm)
+static char *ssl_var_lookup_ssl_cert_valid(apr_pool_t *p, const ASN1_TIME *tm)
 {
     BIO* bio;
 
@@ -688,8 +688,15 @@ static char *ssl_var_lookup_ssl_cert_valid(apr_pool_t *p, ASN1_TIME *tm)
 
 /* Return a string giving the number of days remaining until 'tm', or
  * "0" if this can't be determined. */
-static char *ssl_var_lookup_ssl_cert_remain(apr_pool_t *p, ASN1_TIME *tm)
+static char *ssl_var_lookup_ssl_cert_remain(apr_pool_t *p, const ASN1_TIME *tm)
 {
+#if OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined(LIBRESSL_VERSION_NUMBER)
+    int diff;
+
+    if (ASN1_TIME_check(tm) != 1 || ASN1_TIME_diff(&diff, NULL, NULL, tm) != 1) {
+        return "0";
+    }
+#else
     apr_time_t then, now = apr_time_now();
     apr_time_exp_t exp = {0};
     long diff;
@@ -724,6 +731,7 @@ static char *ssl_var_lookup_ssl_cert_remain(apr_pool_t *p, ASN1_TIME *tm)
     }
 
     diff = (long)((apr_time_sec(then) - apr_time_sec(now)) / (60*60*24));
+#endif
 
     return diff > 0 ? apr_ltoa(p, diff) : apr_pstrdup(p, "0");
 }
@@ -773,7 +781,7 @@ static char *ssl_var_lookup_ssl_cert_rfc4523_cea(apr_pool_t *p, SSL *ssl)
 
     serialNumber = X509_get_serialNumber(xs);
     if (serialNumber) {
-        X509_NAME *issuer = X509_get_issuer_name(xs);
+        const X509_NAME *issuer = X509_get_issuer_name(xs);
         if (issuer) {
             BIGNUM *bn = ASN1_INTEGER_to_BN(serialNumber, NULL);
             char *decimal = BN_bn2dec(bn);
@@ -897,9 +905,9 @@ static char *ssl_var_lookup_ssl_version(apr_pool_t *p, char *var)
 /* Add each RDN in 'xn' to the table 't' where the NID is present in
  * 'nids', using key prefix 'pfx'.  */
 static void extract_dn(apr_table_t *t, apr_hash_t *nids, const char *pfx,
-                       X509_NAME *xn, apr_pool_t *p)
+                       const X509_NAME *xn, apr_pool_t *p)
 {
-    X509_NAME_ENTRY *xsne;
+    const X509_NAME_ENTRY *xsne;
     apr_hash_t *count;
     int i, nid;
 
@@ -914,7 +922,7 @@ static void extract_dn(apr_table_t *t, apr_hash_t *nids, const char *pfx,
 
          /* Retrieve the nid, and check whether this is one of the nids
           * which are to be extracted. */
-         nid = OBJ_obj2nid((ASN1_OBJECT *)X509_NAME_ENTRY_get_object(xsne));
+         nid = OBJ_obj2nid(X509_NAME_ENTRY_get_object(xsne));
 
          tag = apr_hash_get(nids, &nid, sizeof nid);
          if (tag) {
@@ -1027,15 +1035,19 @@ void modssl_var_extract_san_entries(apr_table_t *t, SSL *ssl, apr_pool_t *p)
  * parse the extension type as a primitive string.  This will fail for
  * any structured extension type per the docs.  Returns non-zero on
  * success and writes the string to the given bio. */
-static int dump_extn_value(BIO *bio, ASN1_OCTET_STRING *str)
+static int dump_extn_value(BIO *bio, const ASN1_OCTET_STRING *str)
 {
-    const unsigned char *pp = str->data;
+    const unsigned char *pp = ASN1_STRING_get0_data(str);
     ASN1_STRING *ret = ASN1_STRING_new();
     int rv = 0;
 
+    if (!ret) {
+        return rv;
+    }
+
     /* This allows UTF8String, IA5String, VisibleString, or BMPString;
      * conversion to UTF-8 is forced. */
-    if (d2i_DISPLAYTEXT(&ret, &pp, str->length)) {
+    if (d2i_DISPLAYTEXT(&ret, &pp, ASN1_STRING_length(str))) {
         ASN1_STRING_print_ex(bio, ret, ASN1_STRFLGS_UTF8_CONVERT);
         rv = 1;
     }
@@ -1082,7 +1094,7 @@ apr_array_header_t *ssl_ext_list(apr_pool_t *p, conn_rec *c, int peer,
      */
     array = apr_array_make(p, count, sizeof(char *));
     for (j = 0; j < count; j++) {
-        X509_EXTENSION *ext = X509_get_ext(xs, j);
+        MODSSL_X509_EXT_CONST X509_EXTENSION *ext = X509_get_ext(xs, j);
 
         if (OBJ_cmp(X509_EXTENSION_get_object(ext), oid) == 0) {
             BIO *bio = BIO_new(BIO_s_mem());
index 1ec02f31aef363d302b51c9fbacbd73f7b5c4eed..b2f5dfd385f27cba9da14d146d2b2723e7278ddf 100644 (file)
 #define MODSSL_SSL_METHOD_CONST
 #endif
 
+#if OPENSSL_VERSION_NUMBER >= 0x40000000L
+#define MODSSL_X509_EXT_CONST const
+#else
+#define MODSSL_X509_EXT_CONST
+#endif
+
 #if defined(LIBRESSL_VERSION_NUMBER)
 /* Missing from LibreSSL */
 #if LIBRESSL_VERSION_NUMBER < 0x2060000f
 #define BIO_get_shutdown(x)        (x->shutdown)
 #define BIO_set_shutdown(x,v)      (x->shutdown=v)
 #define DH_bits(x)                 (BN_num_bits(x->p))
+#define X509_up_ref(x)             (CRYPTO_add(&(x)->references, +1, CRYPTO_LOCK_X509))
+#define EVP_PKEY_up_ref(pk)        (CRYPTO_add(&(pk)->references, +1, CRYPTO_LOCK_EVP_PKEY))
+#define ASN1_STRING_get0_data(x)   ((x)->data)
+#define ASN1_STRING_length(x)      ((int)(x)->length)
+#define X509_get0_before(x)        X509_get_before(x)
+#define X509_get0_after(x)         X509_get_after(x)
 #else
 void init_bio_methods(void);
 void free_bio_methods(void);
@@ -1164,16 +1176,16 @@ void         ssl_log_ssl_error(const char *, int, int, server_rec *);
  * counterparts. */
 void ssl_log_xerror(const char *file, int line, int level,
                     apr_status_t rv, apr_pool_t *p, server_rec *s,
-                    X509 *cert, const char *format, ...)
+                    const X509 *cert, const char *format, ...)
     __attribute__((format(printf,8,9)));
 
 void ssl_log_cxerror(const char *file, int line, int level,
-                     apr_status_t rv, conn_rec *c, X509 *cert,
+                     apr_status_t rv, conn_rec *c, const X509 *cert,
                      const char *format, ...)
     __attribute__((format(printf,7,8)));
 
 void ssl_log_rxerror(const char *file, int line, int level,
-                     apr_status_t rv, request_rec *r, X509 *cert,
+                     apr_status_t rv, request_rec *r, const X509 *cert,
                      const char *format, ...)
     __attribute__((format(printf,7,8)));
 
index 8bd9c8a2d236f20993cae42845b7e8b18ad57f77..85d571207c3fa33b2ce7ae9b6620804784f1c6f3 100644 (file)
@@ -202,7 +202,7 @@ char *modssl_bio_free_read(apr_pool_t *p, BIO *bio)
 /* Convert ASN.1 string to a pool-allocated char * string, escaping
  * control characters.  If raw is zero, convert to UTF-8, otherwise
  * unchanged from the character set. */
-static char *asn1_string_convert(apr_pool_t *p, ASN1_STRING *asn1str, int raw)
+char *modssl_ASN1_STRING_convert(apr_pool_t *p, const ASN1_STRING *asn1str, int raw)
 {
     BIO *bio;
     int flags = ASN1_STRFLGS_ESC_CTRL;
@@ -217,13 +217,13 @@ static char *asn1_string_convert(apr_pool_t *p, ASN1_STRING *asn1str, int raw)
     return modssl_bio_free_read(p, bio);
 }
 
-#define asn1_string_to_utf8(p, a) asn1_string_convert(p, a, 0)
+#define asn1_string_to_utf8(p, a) modssl_ASN1_STRING_convert(p, a, 0)
 
 /* convert a NAME_ENTRY to UTF8 string */
-char *modssl_X509_NAME_ENTRY_to_string(apr_pool_t *p, X509_NAME_ENTRY *xsne,
+char *modssl_X509_NAME_ENTRY_to_string(apr_pool_t *p, const X509_NAME_ENTRY *xsne,
                                        int raw)
 {
-    char *result = asn1_string_convert(p, X509_NAME_ENTRY_get_data(xsne), raw);
+    char *result = modssl_ASN1_STRING_convert(p, X509_NAME_ENTRY_get_data(xsne), raw);
     ap_xlate_proto_from_ascii(result, len);
     return result;
 }
@@ -232,7 +232,7 @@ char *modssl_X509_NAME_ENTRY_to_string(apr_pool_t *p, X509_NAME_ENTRY *xsne,
  * convert an X509_NAME to an RFC 2253 formatted string, optionally truncated
  * to maxlen characters (specify a maxlen of 0 for no length limit)
  */
-char *modssl_X509_NAME_to_string(apr_pool_t *p, X509_NAME *dn, int maxlen)
+char *modssl_X509_NAME_to_string(apr_pool_t *p, const X509_NAME *dn, int maxlen)
 {
     char *result = NULL;
     BIO *bio;
@@ -362,7 +362,7 @@ BOOL modssl_X509_getSAN(apr_pool_t *p, X509 *x509, int type, const char *onf,
 /* return an array of (RFC 6125 coined) DNS-IDs and CN-IDs in a certificate */
 static BOOL getIDs(apr_pool_t *p, X509 *x509, apr_array_header_t **ids)
 {
-    X509_NAME *subj;
+    const X509_NAME *subj;
     int i = -1;
 
     /* First, the DNS-IDs (dNSName entries in the subjectAltName extension) */
index 443c1b7ee73344257b9c685dd757b41b90291cda..f5ed3c23aac917c8bd68256fe007976104004b23 100644 (file)
@@ -71,13 +71,19 @@ EVP_PKEY   *modssl_read_privatekey(const char *filename, pem_password_cb *cb, vo
 
 int         modssl_smart_shutdown(SSL *ssl);
 BOOL        modssl_X509_getBC(X509 *, int *, int *);
-char       *modssl_X509_NAME_ENTRY_to_string(apr_pool_t *p, X509_NAME_ENTRY *xsne,
+char       *modssl_X509_NAME_ENTRY_to_string(apr_pool_t *p, const X509_NAME_ENTRY *xsne,
                                              int raw);
-char       *modssl_X509_NAME_to_string(apr_pool_t *, X509_NAME *, int);
+char       *modssl_X509_NAME_to_string(apr_pool_t *, const X509_NAME *, int);
 BOOL        modssl_X509_getSAN(apr_pool_t *, X509 *, int, const char *, int, apr_array_header_t **);
 BOOL        modssl_X509_match_name(apr_pool_t *, X509 *, const char *, BOOL, server_rec *);
 char       *modssl_SSL_SESSION_id2sz(IDCONST unsigned char *, int, char *, int);
 
+/* Convert ASN.1 string to a pool-allocated char * string, escaping
+ * control characters.  If raw is zero, convert to UTF-8, otherwise
+ * unchanged from the character set. */
+char *modssl_ASN1_STRING_convert(apr_pool_t *p, const ASN1_STRING *asn1str,
+                                 int raw);
+
 /* Reads the remaining data in BIO, if not empty, and copies it into a
  * pool-allocated string.  If empty, returns NULL.  BIO_free(bio) is
  * called for both cases. */
index 9d74aa1976a8c4ad01f75e331ebb648cb7704e16..824bf808aa9ce92b6e4b562330c5b40274b156ae 100644 (file)
@@ -675,7 +675,7 @@ static int ssl_print_connection_info(BIO *bio, SSL *ssl)
 
 static void ssl_print_cert_info(BIO *bio, X509 *cert)
 {
-    X509_NAME *dn;
+    const X509_NAME *dn;
     EVP_PKEY *pk;
     char buf[1024];
 
index ab073f07fb10806f9e009584baf52b6c7f20c42c..5296d08bf9f195e3a537afbc01532e86e40f78a1 100755 (executable)
@@ -120,6 +120,13 @@ if ! test -v SKIP_TESTING -o -v NO_TEST_FRAMEWORK; then
 
     # Make a shallow clone of httpd-tests git repo.
     git clone -q --depth=1 https://github.com/apache/httpd-tests.git test/perl-framework
+
+    # For OpenSSL 3.2+ testing, Apache::Test r1916067 is required, so
+    # use a checkout of trunk until there is an updated CPAN release
+    # with that revision.
+    if test -v TEST_OPENSSL3; then
+       svn co -q https://svn.apache.org/repos/asf/perl/Apache-Test/trunk test/perl-framework/Apache-Test
+    fi
 fi
 
 # For LDAP testing, run slapd listening on port 8389 and populate the
@@ -138,22 +145,46 @@ if test -v TEST_SSL; then
     popd
 fi
 
+# Build the requested version of OpenSSL if it's not already installed
+# in the cached ~/root
 if test -v TEST_OPENSSL3; then
-    # Build the requested version of OpenSSL if it's not already
-    # installed in the cached ~/root
+    # For a branch, rebuild if the remote branch has updated.
+    if test -v TEST_OPENSSL3_BRANCH -a -f $HOME/root/openssl-is-${TEST_OPENSSL3}; then
+        latest=`git ls-remote https://github.com/openssl/openssl refs/heads/${TEST_OPENSSL3_BRANCH} | cut -f1`
+        : Got branch latest commit ${latest}
+        if grep -q ^${latest} $HOME/root/openssl-is-${TEST_OPENSSL3}; then
+            : Cached repos already at ${latest}
+        else
+            : Forcing rebuild
+            rm -f $HOME/root/openssl-is-${TEST_OPENSSL3}
+        fi
+    fi
+
     if ! test -f $HOME/root/openssl-is-${TEST_OPENSSL3}; then
         # Remove any previous install.
         rm -rf $HOME/root/openssl3
 
         mkdir -p build/openssl
         pushd build/openssl
-           curl "https://www.openssl.org/source/openssl-${TEST_OPENSSL3}.tar.gz" |
-              tar -xzf -
+           if test -v TEST_OPENSSL3_BRANCH; then
+               git clone --depth=1 -b $TEST_OPENSSL3_BRANCH -q https://github.com/openssl/openssl openssl-${TEST_OPENSSL3}
+           else
+               curl -L "https://github.com/openssl/openssl/releases/download/openssl-${TEST_OPENSSL3}/openssl-${TEST_OPENSSL3}.tar.gz" |
+                   tar -xzf -
+           fi
            cd openssl-${TEST_OPENSSL3}
-           ./Configure --prefix=$HOME/root/openssl3 shared no-tests
+           # Build with RPATH so ./bin/openssl doesn't require $LD_LIBRARY_PATH
+           ./Configure --prefix=$HOME/root/openssl3 \
+                       shared no-tests ${OPENSSL_CONFIG} \
+                       '-Wl,-rpath=$(LIBRPATH)'
            make $MFLAGS
            make install_sw
-           touch $HOME/root/openssl-is-${TEST_OPENSSL3}
+           if test -d .git; then
+               : Caching git commit hash:
+               git rev-parse HEAD | tee $HOME/root/openssl-is-${TEST_OPENSSL3}
+           else
+               touch $HOME/root/openssl-is-${TEST_OPENSSL3}
+           fi
        popd
     fi
 
index 67d9d005054db899a7cd41f630765799edde8ac0..45e4a6b16beaa37fc6e417b209cdceee11af365d 100755 (executable)
@@ -61,7 +61,10 @@ fi
 
 if test -v TEST_OPENSSL3; then
     CONFIG="$CONFIG --with-ssl=$HOME/root/openssl3"
-    export LD_LIBRARY_PATH=$HOME/root/openssl3/lib:$HOME/root/openssl3/lib64
+    export PATH=$HOME/root/openssl3/bin:$PATH
+    # Force everything built to hard-code an RPATH
+    export LDFLAGS="-Wl,-rpath,$HOME/root/openssl3/lib -Wl,-rpath,$HOME/root/openssl3/lib64"
+    openssl version
 fi
 
 srcdir=$PWD
@@ -74,9 +77,19 @@ fi
 $srcdir/configure --prefix=$PREFIX $CONFIG
 make $MFLAGS
 
+if test -v TEST_OPENSSL3; then
+   # Clear the library/run paths so that anything else run during
+   # testing is not forced to use the custom OpenSSL build; e.g. perl,
+   # php-fpm, ...
+   unset LD_LIBRARY_PATH
+   unset LD_RUN_PATH
+fi
+
 if test -v TEST_INSTALL; then
    make install
    pushd $PREFIX
+     # Basic sanity tests of the installed server.
+     ./bin/apachectl -V
      test `./bin/apxs -q PREFIX` = $PREFIX
      test `$PWD/bin/apxs -q PREFIX` = $PREFIX
      ./bin/apxs -g -n foobar
@@ -84,174 +97,196 @@ if test -v TEST_INSTALL; then
    popd
 fi
 
-if ! test -v SKIP_TESTING; then
-    set +e
-    RV=0
+if test -v SKIP_TESTING; then
+    # Check that httpd was built successfully, nothing more.
+    ./httpd -V
+    exit 0
+fi
 
-    if test -v TEST_MALLOC; then
-        # Enable enhanced glibc malloc debugging, see mallopt(3)
-        export MALLOC_PERTURB_=65 MALLOC_CHECK_=3
-        export LIBC_FATAL_STDERR_=1
-    fi
+###############################################################
+### Everything below is only run if SKIP_TESTING was not set ##
+###############################################################
 
-    if test -v TEST_UBSAN; then
-        export UBSAN_OPTIONS="log_path=$PWD/ubsan.log"
-    fi
+: Running tests...
 
-    if test -v TEST_ASAN; then
-        export ASAN_OPTIONS="log_path=$PWD/asan.log:detect_leaks=0"
-    fi
+set +e
+RV=0
 
-    # Try to keep all potential coredumps from all processes
-    sudo sysctl -w kernel.core_uses_pid=1 2>/dev/null || true
-    # Systemd based systems might process core dumps via systemd-coredump.
-    # But we want to have local unprocessed files.
-    sudo sysctl -w kernel.core_pattern=core || true
-    ulimit -c unlimited 2>/dev/null || true
+if test -v TEST_MALLOC; then
+    # Enable enhanced glibc malloc debugging, see mallopt(3)
+    export MALLOC_PERTURB_=65 MALLOC_CHECK_=3
+    export LIBC_FATAL_STDERR_=1
+fi
 
-    if test -v WITH_TEST_SUITE; then
-        make check TESTS="${TESTS}" TEST_CONFIG="${TEST_ARGS}"
-        RV=$?
-    else
-        test -v TEST_INSTALL || make install
-        pushd test/perl-framework
-            perl Makefile.PL -apxs $PREFIX/bin/apxs
-            make test APACHE_TEST_EXTRA_ARGS="${TEST_ARGS} ${TESTS}" | tee test.log
-            RV=${PIPESTATUS[0]}
-            # re-run failing tests with -v, avoiding set -e
-            if [ $RV -ne 0 ]; then
-                #mv t/logs/error_log t/logs/error_log_save
-                FAILERS=""
-                while read FAILER; do
-                    FAILERS="$FAILERS $FAILER"
-                done < <(awk '/Failed:/{print $1}' test.log)
-                if [ -n "$FAILERS" ]; then
-                    t/TEST -v $FAILERS || true
-                fi
-                # set -e would have killed us after the original t/TEST
-                rm -f test.log
-                #mv t/logs/error_log_save t/logs/error_log
-                false
-            fi
-        popd
-    fi
+if test -v TEST_UBSAN; then
+    export UBSAN_OPTIONS="log_path=$PWD/ubsan.log"
+fi
 
-    # Skip further testing if a core dump was created during the test
-    # suite run above.
-    if test $RV -eq 0 && test -n "`ls test/perl-framework/t/core{,.*} 2>/dev/null`"; then
-        RV=4
-    fi
+if test -v TEST_ASAN; then
+    export ASAN_OPTIONS="log_path=$PWD/asan.log:detect_leaks=0"
+fi
 
-    if test -v TEST_SSL -a $RV -eq 0; then
-        pushd test/perl-framework
-            # Test loading encrypted private keys
-            ./t/TEST -defines "TEST_SSL_DES3_KEY TEST_SSL_PASSPHRASE_EXEC" t/ssl
-            RV=$?
+if test -v PHP_FPM; then
+    # Sanity test the executable exists.
+    $PHP_FPM --version
+fi
 
-            # Log the OpenSSL version.
-            grep 'mod_ssl.*compiled against' t/logs/error_log | tail -n 1
-            
-            # Test various session cache backends
-            for cache in shmcb redis:localhost:6379 memcache:localhost:11211; do
-                test $RV -eq 0 || break
-
-                SSL_SESSCACHE=$cache ./t/TEST -sslproto TLSv1.2 -defines TEST_SSL_SESSCACHE -start
-                ./t/TEST t/ssl
-                RV=$?
-                ./t/TEST -stop
-                SRV=$?
-                if test $RV -eq 0 -a $SRV -ne 0; then
-                    RV=$SRV
-                fi
-            done
-        popd
-    fi
+# Try to keep all potential coredumps from all processes
+sudo sysctl -w kernel.core_uses_pid=1 2>/dev/null || true
+# Systemd based systems might process core dumps via systemd-coredump.
+# But we want to have local unprocessed files.
+sudo sysctl -w kernel.core_pattern=core || true
+ulimit -c unlimited 2>/dev/null || true
 
-    if test -v LITMUS -a $RV -eq 0; then
-        pushd test/perl-framework
-           mkdir -p t/htdocs/modules/dav
-           ./t/TEST -start
-           # litmus uses $TESTS, so unset it.
-           unset TESTS
-           litmus http://localhost:8529/modules/dav/
-           RV=$?
-           ./t/TEST -stop
-        popd
-    fi
+if test -v WITH_TEST_SUITE; then
+    make check TESTS="${TESTS}" TEST_CONFIG="${TEST_ARGS}"
+    RV=$?
+else
+    test -v TEST_INSTALL || make install
+    pushd test/perl-framework
+        perl Makefile.PL -apxs $PREFIX/bin/apxs
+        make test APACHE_TEST_EXTRA_ARGS="${TEST_ARGS} ${TESTS}" | tee test.log
+        RV=${PIPESTATUS[0]}
+        # re-run failing tests with -v, avoiding set -e
+        if [ $RV -ne 0 ]; then
+            #mv t/logs/error_log t/logs/error_log_save
+            FAILERS=""
+            while read FAILER; do
+                FAILERS="$FAILERS $FAILER"
+            done < <(awk '/Failed:/{print $1}' test.log)
+            if [ -n "$FAILERS" ]; then
+                t/TEST -v $FAILERS || true
+            fi
+            # set -e would have killed us after the original t/TEST
+            rm -f test.log
+            #mv t/logs/error_log_save t/logs/error_log
+            false
+        fi
+    popd
+fi
 
-    if test $RV -ne 0 && test -f test/perl-framework/t/logs/error_log; then
-        grep -v ':\(debug\|trace[12345678]\)\]' test/perl-framework/t/logs/error_log
-    fi
+# Skip further testing if a core dump was created during the test
+# suite run above.
+if test $RV -eq 0 && test -n "`ls test/perl-framework/t/core{,.*} 2>/dev/null`"; then
+    RV=4
+fi
 
-    if test -v TEST_CORE -a $RV -eq 0; then
-        # Run HTTP/2 tests.
-        MPM=event py.test-3 test/modules/core
-        RV=$?
-    fi
+if test \( -v TEST_SSL -o -v TEST_OPENSSL3 \) \
+        -a -f test/perl-framework/t/logs/error_log; then
+    : -- Check OpenSSL version used by mod_ssl at compile- and run-time --
+    grep 'mod_ssl.*compiled against' test/perl-framework/t/logs/error_log | tail -n1 | grep --color=always 'OpenSSL/[^ ]*'
+    grep 'resuming normal operations' test/perl-framework/t/logs/error_log | tail -n1 | grep --color=always 'OpenSSL/[^ ]*'
+fi
 
-    if test -v TEST_H2 -a $RV -eq 0; then
-        # Build the test clients
-        (cd test/clients && make)
-        # Run HTTP/2 tests.
-        MPM=event py.test-3 test/modules/http2
+if test -v TEST_SSL -a $RV -eq 0; then
+    pushd test/perl-framework
+        # Test loading encrypted private keys
+        ./t/TEST -defines "TEST_SSL_DES3_KEY TEST_SSL_PASSPHRASE_EXEC" t/ssl
         RV=$?
-        if test $RV -eq 0; then
-          MPM=worker py.test-3 test/modules/http2
-          RV=$?
-        fi
-    fi
 
-    if test -v TEST_MD -a $RV -eq 0; then
-        # Run ACME tests.
-        # need the go based pebble as ACME test server
-        # which is a package on debian sid, but not on focal
-        export GOPATH=${PREFIX}/gocode
-        mkdir -p "${GOPATH}"
-        export PATH="${GOROOT}/bin:${GOPATH}/bin:${PATH}"
-        go get -u github.com/letsencrypt/pebble/...
-        (cd $GOPATH/src/github.com/letsencrypt/pebble && go install ./...)
-
-        py.test-3 test/modules/md
-        RV=$?
-    fi
+        # Log the OpenSSL version.
+        grep 'mod_ssl.*compiled against' t/logs/error_log | tail -n 1
+        
+        # Test various session cache backends
+        for cache in shmcb redis:localhost:6379 memcache:localhost:11211; do
+            test $RV -eq 0 || break
 
-    # Catch cases where abort()s get logged to stderr by libraries but
-    # only cause child processes to terminate e.g. during shutdown,
-    # which may not otherwise trigger test failures.
+            SSL_SESSCACHE=$cache ./t/TEST -sslproto TLSv1.2 -defines TEST_SSL_SESSCACHE -start
+            ./t/TEST t/ssl
+            RV=$?
+            ./t/TEST -stop
+            SRV=$?
+            if test $RV -eq 0 -a $SRV -ne 0; then
+                RV=$SRV
+            fi
+        done
+    popd
+fi
 
-    # "glibc detected": printed with LIBC_FATAL_STDERR_/MALLOC_CHECK_
-    # glibc will abort when malloc errors are detected.  This will get
-    # caught by the segfault grep as well.
+if test -v LITMUS -a $RV -eq 0; then
+    pushd test/perl-framework
+       mkdir -p t/htdocs/modules/dav
+       ./t/TEST -start
+       # litmus uses $TESTS, so unset it.
+       unset TESTS
+       litmus http://localhost:8529/modules/dav/
+       RV=$?
+       ./t/TEST -stop
+    popd
+fi
 
-    # "pool concurrency check": printed by APR built with
-    # --enable-thread-debug when an APR pool concurrency check aborts
+if test $RV -ne 0 && test -f test/perl-framework/t/logs/error_log; then
+    grep -v ':\(debug\|trace[12345678]\)\]' test/perl-framework/t/logs/error_log
+fi
 
-    for phrase in 'Segmentation fault' 'glibc detected' 'pool concurrency check:' 'Assertion.*failed'; do
-        # Ignore IO/debug logs
-        if grep -v ':\(debug\|trace[12345678]\)\]' test/perl-framework/t/logs/error_log | grep -q "$phrase"; then
-            grep --color=always -C5 "$phrase" test/perl-framework/t/logs/error_log
-            RV=2
-        fi
-    done
+if test -v TEST_CORE -a $RV -eq 0; then
+    # Run HTTP/2 tests.
+    MPM=event py.test-3 test/modules/core
+    RV=$?
+fi
 
-    if test -v TEST_UBSAN && test -n "`ls ubsan.log.* 2>/dev/null`"; then
-        cat ubsan.log.*
-        RV=3
+if test -v TEST_H2 -a $RV -eq 0; then
+    # Build the test clients
+    (cd test/clients && make)
+    # Run HTTP/2 tests.
+    MPM=event py.test-3 test/modules/http2
+    RV=$?
+    if test $RV -eq 0; then
+      MPM=worker py.test-3 test/modules/http2
+      RV=$?
     fi
+fi
 
-    if test -v TEST_ASAN && test -n "`ls asan.log.* 2>/dev/null`"; then
-        cat asan.log.*
+if test -v TEST_MD -a $RV -eq 0; then
+    # Run ACME tests.
+    # need the go based pebble as ACME test server
+    # which is a package on debian sid, but not on focal
+    export GOPATH=${PREFIX}/gocode
+    mkdir -p "${GOPATH}"
+    export PATH="${GOROOT}/bin:${GOPATH}/bin:${PATH}"
+    go get -u github.com/letsencrypt/pebble/...
+    (cd $GOPATH/src/github.com/letsencrypt/pebble && go install ./...)
+
+    py.test-3 test/modules/md
+    RV=$?
+fi
 
-        # ASan can report memory leaks, fail on errors only
-        if grep -q "ERROR: AddressSanitizer:" `ls asan.log.*`; then
-            RV=4
-        fi
+# Catch cases where abort()s get logged to stderr by libraries but
+# only cause child processes to terminate e.g. during shutdown,
+# which may not otherwise trigger test failures.
+
+# "glibc detected": printed with LIBC_FATAL_STDERR_/MALLOC_CHECK_
+# glibc will abort when malloc errors are detected.  This will get
+# caught by the segfault grep as well.
+
+# "pool concurrency check": printed by APR built with
+# --enable-thread-debug when an APR pool concurrency check aborts
+
+for phrase in 'Segmentation fault' 'glibc detected' 'pool concurrency check:' 'Assertion.*failed'; do
+    # Ignore IO/debug logs
+    if grep -v ':\(debug\|trace[12345678]\)\]' test/perl-framework/t/logs/error_log | grep -q "$phrase"; then
+        grep --color=always -C5 "$phrase" test/perl-framework/t/logs/error_log
+        RV=2
     fi
+done
 
-    for core in `ls test/perl-framework/t/core{,.*} test/gen/apache/core{,.*} 2>/dev/null`; do
-        gdb -ex 'thread apply all backtrace full' -batch ./httpd "$core"
-        RV=5
-    done
+if test -v TEST_UBSAN && test -n "`ls ubsan.log.* 2>/dev/null`"; then
+    cat ubsan.log.*
+    RV=3
+fi
+
+if test -v TEST_ASAN && test -n "`ls asan.log.* 2>/dev/null`"; then
+    cat asan.log.*
 
-    exit $RV
+    # ASan can report memory leaks, fail on errors only
+    if grep -q "ERROR: AddressSanitizer:" `ls asan.log.*`; then
+        RV=4
+    fi
 fi
+
+for core in `ls test/perl-framework/t/core{,.*} test/gen/apache/core{,.*} 2>/dev/null`; do
+    gdb -ex 'thread apply all backtrace full' -batch ./httpd "$core"
+    RV=5
+done
+
+exit $RV