[openssl] master update

Richard Levitte levitte at openssl.org
Sun Jul 5 19:17:15 UTC 2020


The branch master has been updated
       via  1b726e9b91a032298dc96ad117b23e18e1583246 (commit)
       via  fa7a807435edbcdcddf809398c59a60650315981 (commit)
       via  17b7f8968481aa99c622080ac73879f42fb8c4ae (commit)
       via  71f2994b151f5de0c7bc14592c84795ff98256c1 (commit)
       via  163b2bcd8b2e5cd149dfc8dce1ca096805559379 (commit)
      from  dd76b90ef6cf9bd344c9a6cd0de536a734d1b6a3 (commit)


- Log -----------------------------------------------------------------
commit 1b726e9b91a032298dc96ad117b23e18e1583246
Author: Richard Levitte <levitte at openssl.org>
Date:   Wed Jul 1 23:13:49 2020 +0200

    TEST: update 02-test_errstr.t to have better tests
    
    We now check that if libcrypto hasn't loaded the string for some particular
    system error, it gives us "reason(nnn)" instead, where 'nnn' is the system
    error number in decimal.
    
    We go through all possible error macros that perl serves us, not only the
    POSIX ones.
    
    Reviewed-by: David von Oheimb <david.von.oheimb at siemens.com>
    (Merged from https://github.com/openssl/openssl/pull/12343)

commit fa7a807435edbcdcddf809398c59a60650315981
Author: Richard Levitte <levitte at openssl.org>
Date:   Wed Jul 1 22:17:01 2020 +0200

    SSL: fix misuse of ERR_LIB_SYS
    
    Reviewed-by: David von Oheimb <david.von.oheimb at siemens.com>
    (Merged from https://github.com/openssl/openssl/pull/12343)

commit 17b7f8968481aa99c622080ac73879f42fb8c4ae
Author: Richard Levitte <levitte at openssl.org>
Date:   Mon Jun 29 12:43:40 2020 +0200

    TEST: fix test/errtest.c
    
    test/errtest.c used the system error code 1 for EPERM.  However, EPERM
    may be coded differently on different systems, so we switch to using
    EPERM instead.  However, because we know that the ERR sub-system
    truncates system error codes that occupy more than 24 bits, we check
    that the reason code in the recorded error matches our EPERM, and skip
    the test if not.
    
    To be safe (even though the error string for that code is well defined
    in POSIX), we also use strerror() to retrieve the string for that
    error code instead of using a hard coded value.
    
    Fixes #12276
    Fixes #12217
    Fixes #12354
    
    Reviewed-by: David von Oheimb <david.von.oheimb at siemens.com>
    (Merged from https://github.com/openssl/openssl/pull/12343)

commit 71f2994b151f5de0c7bc14592c84795ff98256c1
Author: Richard Levitte <levitte at openssl.org>
Date:   Mon Jun 29 12:18:24 2020 +0200

    ERR: special case system errors
    
    Because system errors can be any positive number that fits in an 'int'
    according to POSIX, we can't reasonably expect them to be in the 1..127
    range, even though that's the most usual.
    
    Instead of packing them into the OpenSSL error code structure, we
    recognise them as a special case and mark them as such by storing them
    in our error queue with the highest bit set.  We make OpenSSL specific
    error records have their highest bit cleared, and in doing so, we
    shift down the library section of the code by one bit.  This still
    leaves a very large section for the reason codes.
    
    Of course, we must adapt the error code and reason string extraction
    and printing functions accordingly.
    
    With this, we also thrown away the pre-loaded array of system error
    strings, and extract them from the system when needed instead, i.e.
    when we create error strings.
    
    Reviewed-by: David von Oheimb <david.von.oheimb at siemens.com>
    (Merged from https://github.com/openssl/openssl/pull/12343)

commit 163b2bcd8b2e5cd149dfc8dce1ca096805559379
Author: Richard Levitte <levitte at openssl.org>
Date:   Mon Jun 29 12:08:27 2020 +0200

    ERR: refactor global error codes
    
    Some ERR_R_ codes overlapped other ERR_R_ codes:
    
    - ERR_R_BUF_LIB vs ERR_R_PASSED_INVALID_ARGUMENT
    - ERR_R_DSA_LIB vs ERR_R_INTERRUPTED_OR_CANCELLED
    
    Looking back at history, this was originally not an issue, because
    the ERR_R_ codes that weren't ERR_LIB_ aliases had bit 2**6 set.
    However, new codes without that bit came in, and we got the overlap
    that is mentioned above.
    
    To get rid of the overlap, we repartition the codes as follows:
    
    - ERR_R_{name}_LIB that are aliases for ERR_LIB_{name} are confined to
      the range 1..63.
    - Other ERR_R_ codes are confined to 64..99
    
    We also expand the reason codes to 24 bits of data, where the 4 top
    bits are for reason code flags.  We also allocate a "fatal" flag
    ERR_RFLAG_FATAL.  The reason code ERR_R_FATAL stops acting as a flag,
    but is coded in such a way that it still serves as one for code that
    happens to use it as such.
    
    Reviewed-by: David von Oheimb <david.von.oheimb at siemens.com>
    (Merged from https://github.com/openssl/openssl/pull/12343)

-----------------------------------------------------------------------

Summary of changes:
 crypto/err/err.c              | 104 ++++++----------------------
 crypto/err/err_local.h        |   5 +-
 crypto/err/err_prn.c          |  26 ++++++-
 include/openssl/err.h         | 135 +++++++++++++++++++++++++++++-------
 ssl/ssl_lib.c                 |   3 +-
 test/errtest.c                |  47 ++++++++++---
 test/recipes/02-test_errstr.t | 157 +++++++++++++++++++++---------------------
 7 files changed, 279 insertions(+), 198 deletions(-)

diff --git a/crypto/err/err.c b/crypto/err/err.c
index 9a37c42625..26cf2b0b9d 100644
--- a/crypto/err/err.c
+++ b/crypto/err/err.c
@@ -173,84 +173,6 @@ static ERR_STRING_DATA *int_err_get_item(const ERR_STRING_DATA *d)
     return p;
 }
 
-#ifndef OPENSSL_NO_ERR
-/* 2019-05-21: Russian and Ukrainian locales on Linux require more than 6,5 kB */
-# define SPACE_SYS_STR_REASONS 8 * 1024
-# define NUM_SYS_STR_REASONS 127
-
-static ERR_STRING_DATA SYS_str_reasons[NUM_SYS_STR_REASONS + 1];
-/*
- * SYS_str_reasons is filled with copies of strerror() results at
- * initialization. 'errno' values up to 127 should cover all usual errors,
- * others will be displayed numerically by ERR_error_string. It is crucial
- * that we have something for each reason code that occurs in
- * ERR_str_reasons, or bogus reason strings will be returned for SYSerr(),
- * which always gets an errno value and never one of those 'standard' reason
- * codes.
- */
-
-static void build_SYS_str_reasons(void)
-{
-    /* OPENSSL_malloc cannot be used here, use static storage instead */
-    static char strerror_pool[SPACE_SYS_STR_REASONS];
-    char *cur = strerror_pool;
-    size_t cnt = 0;
-    static int init = 1;
-    int i;
-    int saveerrno = get_last_sys_error();
-
-    CRYPTO_THREAD_write_lock(err_string_lock);
-    if (!init) {
-        CRYPTO_THREAD_unlock(err_string_lock);
-        return;
-    }
-
-    for (i = 1; i <= NUM_SYS_STR_REASONS; i++) {
-        ERR_STRING_DATA *str = &SYS_str_reasons[i - 1];
-
-        str->error = ERR_PACK(ERR_LIB_SYS, 0, i);
-        /*
-         * If we have used up all the space in strerror_pool,
-         * there's no point in calling openssl_strerror_r()
-         */
-        if (str->string == NULL && cnt < sizeof(strerror_pool)) {
-            if (openssl_strerror_r(i, cur, sizeof(strerror_pool) - cnt)) {
-                size_t l = strlen(cur);
-
-                str->string = cur;
-                cnt += l;
-                cur += l;
-
-                /*
-                 * VMS has an unusual quirk of adding spaces at the end of
-                 * some (most? all?) messages. Lets trim them off.
-                 */
-                while (cur > strerror_pool && ossl_isspace(cur[-1])) {
-                    cur--;
-                    cnt--;
-                }
-                *cur++ = '\0';
-                cnt++;
-            }
-        }
-        if (str->string == NULL)
-            str->string = "unknown";
-    }
-
-    /*
-     * Now we still have SYS_str_reasons[NUM_SYS_STR_REASONS] = {0, NULL}, as
-     * required by ERR_load_strings.
-     */
-
-    init = 0;
-
-    CRYPTO_THREAD_unlock(err_string_lock);
-    /* openssl_strerror_r could change errno, but we want to preserve it */
-    set_sys_error(saveerrno);
-    err_load_strings(SYS_str_reasons);
-}
-#endif
-
 static void ERR_STATE_free(ERR_STATE *s)
 {
     int i;
@@ -322,7 +244,6 @@ int ERR_load_ERR_strings(void)
 
     err_load_strings(ERR_str_libraries);
     err_load_strings(ERR_str_reasons);
-    build_SYS_str_reasons();
 #endif
     return 1;
 }
@@ -569,8 +490,8 @@ static unsigned long get_error_values(ERR_GET_ACTION g,
 
 void ERR_error_string_n(unsigned long e, char *buf, size_t len)
 {
-    char lsbuf[64], rsbuf[64];
-    const char *ls, *rs;
+    char lsbuf[64], rsbuf[256];
+    const char *ls, *rs = NULL;
     unsigned long f = 0, l, r;
 
     if (len == 0)
@@ -583,8 +504,19 @@ void ERR_error_string_n(unsigned long e, char *buf, size_t len)
         ls = lsbuf;
     }
 
-    rs = ERR_reason_error_string(e);
+    /*
+     * ERR_reason_error_string() can't safely return system error strings,
+     * since it would call openssl_strerror_r(), which needs a buffer for
+     * thread safety.  So for system errors, we call openssl_strerror_r()
+     * directly instead.
+     */
     r = ERR_GET_REASON(e);
+    if (ERR_SYSTEM_ERROR(e)) {
+        if (openssl_strerror_r(r, rsbuf, sizeof(rsbuf)))
+            rs = rsbuf;
+    } else {
+        rs = ERR_reason_error_string(e);
+    }
     if (rs == NULL) {
         BIO_snprintf(rsbuf, sizeof(rsbuf), "reason(%lu)", r);
         rs = rsbuf;
@@ -642,6 +574,14 @@ const char *ERR_reason_error_string(unsigned long e)
         return NULL;
     }
 
+    /*
+     * ERR_reason_error_string() can't safely return system error strings,
+     * since openssl_strerror_r() needs a buffer for thread safety, and we
+     * haven't got one that would serve any sensible purpose.
+     */
+    if (ERR_SYSTEM_ERROR(e))
+        return NULL;
+
     l = ERR_GET_LIB(e);
     r = ERR_GET_REASON(e);
     d.error = ERR_PACK(l, 0, r);
diff --git a/crypto/err/err_local.h b/crypto/err/err_local.h
index 0374bf6a6f..add49af44c 100644
--- a/crypto/err/err_local.h
+++ b/crypto/err/err_local.h
@@ -38,7 +38,10 @@ static ossl_inline void err_clear_data(ERR_STATE *es, size_t i, int deall)
 static ossl_inline void err_set_error(ERR_STATE *es, size_t i,
                                       int lib, int reason)
 {
-    es->err_buffer[i] = ERR_PACK(lib, 0, reason);
+    es->err_buffer[i] =
+        lib == ERR_LIB_SYS
+        ? (unsigned int)(ERR_SYSTEM_FLAG |  reason)
+        : ERR_PACK(lib, 0, reason);
 }
 
 static ossl_inline void err_set_debug(ERR_STATE *es, size_t i,
diff --git a/crypto/err/err_prn.c b/crypto/err/err_prn.c
index 80cc0ecf1a..f67cf2e32b 100644
--- a/crypto/err/err_prn.c
+++ b/crypto/err/err_prn.c
@@ -23,16 +23,36 @@ void ERR_print_errors_cb(int (*cb) (const char *str, size_t len, void *u),
 {
     CRYPTO_THREAD_ID tid = CRYPTO_THREAD_get_current_id();
     unsigned long l;
-    char buf[ERR_PRINT_BUF_SIZE], *hex;
-    const char *lib, *reason;
     const char *file, *data, *func;
     int line, flags;
 
     while ((l = ERR_get_error_all(&file, &line, &func, &data, &flags)) != 0) {
+        char buf[ERR_PRINT_BUF_SIZE], *hex;
+        const char *lib, *reason = NULL;
+        char rsbuf[256];
+        unsigned long r = ERR_GET_REASON(l);
+
         lib = ERR_lib_error_string(l);
-        reason = ERR_reason_error_string(l);
+
+        /*
+         * ERR_reason_error_string() can't safely return system error strings,
+         * since it would call openssl_strerror_r(), which needs a buffer for
+         * thread safety.  So for system errors, we call openssl_strerror_r()
+         * directly instead.
+         */
+        if (ERR_SYSTEM_ERROR(l)) {
+            if (openssl_strerror_r(r, rsbuf, sizeof(rsbuf)))
+                reason = rsbuf;
+        } else {
+            reason = ERR_reason_error_string(l);
+        }
+
         if (func == NULL)
             func = "unknown function";
+        if (reason == NULL) {
+            BIO_snprintf(rsbuf, sizeof(rsbuf), "reason(%lu)", r);
+            reason = rsbuf;
+        }
         if ((flags & ERR_TXT_STRING) == 0)
             data = "";
         hex = openssl_buf2hexstr_sep((const unsigned char *)&tid, sizeof(tid),
diff --git a/include/openssl/err.h b/include/openssl/err.h
index aa8ffa9765..a40d231ea0 100644
--- a/include/openssl/err.h
+++ b/include/openssl/err.h
@@ -39,6 +39,7 @@ extern "C" {
 #  endif
 # endif
 
+# include <limits.h>
 # include <errno.h>
 
 # define ERR_TXT_MALLOCED        0x01
@@ -163,14 +164,95 @@ struct err_state_st {
 #  define X509err(f, r) ERR_raise_data(ERR_LIB_X509, (r), NULL)
 # endif
 
-# define ERR_PACK(l,f,r) ( \
-        (((unsigned int)(l) & 0x0FF) << 24L) | \
-        (((unsigned int)(f) & 0xFFF) << 12L) | \
-        (((unsigned int)(r) & 0xFFF)       ) )
-# define ERR_GET_LIB(l)          (int)(((l) >> 24L) & 0x0FFL)
-# define ERR_GET_FUNC(l)         (int)(((l) >> 12L) & 0xFFFL)
-# define ERR_GET_REASON(l)       (int)( (l)         & 0xFFFL)
-# define ERR_FATAL_ERROR(l)      (int)( (l)         & ERR_R_FATAL)
+/*-
+ * The error code packs differently depending on if it records a system
+ * error or an OpenSSL error.
+ *
+ * A system error packs like this (we follow POSIX and only allow positive
+ * numbers that fit in an |int|):
+ *
+ * +-+-------------------------------------------------------------+
+ * |1|                     system error number                     |
+ * +-+-------------------------------------------------------------+
+ *
+ * An OpenSSL error packs like this:
+ *
+ * <---------------------------- 32 bits -------------------------->
+ *    <--- 8 bits ---><------------------ 23 bits ----------------->
+ * +-+---------------+---------------------------------------------+
+ * |0|    library    |                    reason                   |
+ * +-+---------------+---------------------------------------------+
+ *
+ * A few of the reason bits are reserved as flags with special meaning:
+ *
+ *                    <4 bits><-------------- 19 bits ------------->
+ *                   +-------+-------------------------------------+
+ *                   | rflags|                reason               |
+ *                   +-------+-------------------------------------+
+ *
+ * We have the reason flags being part of the overall reason code for
+ * backward compatibility reasons, i.e. how ERR_R_FATAL was implemented.
+ */
+
+/* Macros to help decode recorded system errors */
+# define ERR_SYSTEM_FLAG                ((unsigned int)INT_MAX + 1)
+# define ERR_SYSTEM_MASK                ((unsigned int)INT_MAX)
+
+/* Macros to help decode recorded OpenSSL errors */
+# define ERR_LIB_OFFSET                 23L
+# define ERR_LIB_MASK                   0xFF
+# define ERR_RFLAGS_OFFSET              19L
+# define ERR_RFLAGS_MASK                0xF
+# define ERR_REASON_MASK                0X7FFFFF
+
+/*
+ * Reason flags are defined pre-shifted to easily combine with the reason
+ * number.
+ */
+# define ERR_RFLAG_FATAL                (0x1 << ERR_RFLAGS_OFFSET)
+
+# define ERR_SYSTEM_ERROR(errcode)      (((errcode) & ERR_SYSTEM_FLAG) != 0)
+
+static ossl_inline int ERR_GET_LIB(unsigned long errcode)
+{
+    if (ERR_SYSTEM_ERROR(errcode))
+        return ERR_LIB_SYS;
+    return (errcode >> ERR_LIB_OFFSET) & ERR_LIB_MASK;
+}
+
+static ossl_inline int ERR_GET_FUNC(unsigned long errcode)
+{
+    return 0;
+}
+
+static ossl_inline int ERR_GET_RFLAGS(unsigned long errcode)
+{
+    if (ERR_SYSTEM_ERROR(errcode))
+        return 0;
+    return errcode & (ERR_RFLAGS_MASK << ERR_RFLAGS_OFFSET);
+}
+
+static ossl_inline int ERR_GET_REASON(unsigned long errcode)
+{
+    if (ERR_SYSTEM_ERROR(errcode))
+        return errcode & ERR_SYSTEM_MASK;
+    return errcode & ERR_REASON_MASK;
+}
+
+static ossl_inline int ERR_FATAL_ERROR(unsigned long errcode)
+{
+    return (ERR_GET_RFLAGS(errcode) & ERR_RFLAG_FATAL) != 0;
+}
+
+/*
+ * ERR_PACK is a helper macro to properly pack OpenSSL error codes and may
+ * only be used for that purpose.  System errors are packed internally.
+ * ERR_PACK takes reason flags and reason code combined in |reason|.
+ * ERR_PACK ignores |func|, that parameter is just legacy from pre-3.0 OpenSSL.
+ */
+# define ERR_PACK(lib,func,reason)                                      \
+    ( (((unsigned long)(lib)    & ERR_LIB_MASK   ) << ERR_LIB_OFFSET) | \
+      (((unsigned long)(reason) & ERR_REASON_MASK)) )
 
 # ifndef OPENSSL_NO_DEPRECATED_3_0
 #  define SYS_F_FOPEN             0
@@ -200,7 +282,7 @@ struct err_state_st {
 #  define SYS_F_SENDFILE          0
 # endif
 
-/* reasons */
+/* "we came from here" global reason codes, range 1..63 */
 # define ERR_R_SYS_LIB   ERR_LIB_SYS/* 2 */
 # define ERR_R_BN_LIB    ERR_LIB_BN/* 3 */
 # define ERR_R_RSA_LIB   ERR_LIB_RSA/* 4 */
@@ -221,21 +303,26 @@ struct err_state_st {
 # define ERR_R_ECDSA_LIB ERR_LIB_ECDSA/* 42 */
 # define ERR_R_OSSL_STORE_LIB ERR_LIB_OSSL_STORE/* 44 */
 
-# define ERR_R_NESTED_ASN1_ERROR                 58
-# define ERR_R_MISSING_ASN1_EOS                  63
-
-/* fatal error */
-# define ERR_R_FATAL                             64
-# define ERR_R_MALLOC_FAILURE                    (1|ERR_R_FATAL)
-# define ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED       (2|ERR_R_FATAL)
-# define ERR_R_PASSED_NULL_PARAMETER             (3|ERR_R_FATAL)
-# define ERR_R_INTERNAL_ERROR                    (4|ERR_R_FATAL)
-# define ERR_R_DISABLED                          (5|ERR_R_FATAL)
-# define ERR_R_INIT_FAIL                         (6|ERR_R_FATAL)
-# define ERR_R_PASSED_INVALID_ARGUMENT           (7)
-# define ERR_R_OPERATION_FAIL                    (8|ERR_R_FATAL)
-# define ERR_R_INVALID_PROVIDER_FUNCTIONS        (9|ERR_R_FATAL)
-# define ERR_R_INTERRUPTED_OR_CANCELLED          (10)
+/*
+ * global reason codes, range 64..99 (sub-system specific codes start at 100)
+ *
+ * ERR_R_FATAL had dual purposes in pre-3.0 OpenSSL, as a standalone reason
+ * code as well as a fatal flag.  This is still possible to do, as 2**6 (64)
+ * is present in the whole range of global reason codes.
+ */
+# define ERR_R_FATAL                             (64|ERR_RFLAG_FATAL)
+# define ERR_R_MALLOC_FAILURE                    (65|ERR_RFLAG_FATAL)
+# define ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED       (66|ERR_RFLAG_FATAL)
+# define ERR_R_PASSED_NULL_PARAMETER             (67|ERR_RFLAG_FATAL)
+# define ERR_R_INTERNAL_ERROR                    (68|ERR_RFLAG_FATAL)
+# define ERR_R_DISABLED                          (69|ERR_RFLAG_FATAL)
+# define ERR_R_INIT_FAIL                         (70|ERR_RFLAG_FATAL)
+# define ERR_R_PASSED_INVALID_ARGUMENT           (71)
+# define ERR_R_OPERATION_FAIL                    (72|ERR_RFLAG_FATAL)
+# define ERR_R_INVALID_PROVIDER_FUNCTIONS        (73|ERR_RFLAG_FATAL)
+# define ERR_R_INTERRUPTED_OR_CANCELLED          (74)
+# define ERR_R_NESTED_ASN1_ERROR                 (76)
+# define ERR_R_MISSING_ASN1_EOS                  (77)
 
 /*
  * 99 is the maximum possible ERR_R_... code, higher values are reserved for
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index dd83f373b2..a252761ca4 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -2072,7 +2072,8 @@ ossl_ssize_t SSL_sendfile(SSL *s, int fd, off_t offset, size_t size, int flags)
     }
 
 #ifdef OPENSSL_NO_KTLS
-    ERR_raise_data(ERR_LIB_SYS, ERR_R_INTERNAL_ERROR, "calling sendfile()");
+    ERR_raise_data(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR,
+                   "can't call ktls_sendfile(), ktls disabled");
     return -1;
 #else
     ret = ktls_sendfile(SSL_get_wfd(s), fd, offset, size, flags);
diff --git a/test/errtest.c b/test/errtest.c
index f7a6f85470..e8c7d44306 100644
--- a/test/errtest.c
+++ b/test/errtest.c
@@ -25,24 +25,53 @@
 
 static int test_print_error_format(void)
 {
-    static const char expected_format[] =
-        ":error::system library:%s:Operation not permitted:"
+    /* Variables used to construct an error line */
+    const char *func = OPENSSL_FUNC;
 # ifndef OPENSSL_NO_FILENAMES
-        "errtest.c:30:";
+    const char *file = OPENSSL_FILE;
+    const int line = OPENSSL_LINE;
 # else
-        ":0:";
+    const char *file = "";
+    const int line = 0;
 # endif
-    char expected[256];
+    /* The format for OpenSSL error lines */
+    const char *expected_format = ":error::system library:%s:%s:%s:%d";
+    /*-
+     *                                                    ^^ ^^ ^^ ^^
+     * function name -------------------------------------++ || || ||
+     * reason string (system error string) ------------------++ || ||
+     * file name -----------------------------------------------++ ||
+     * line number ------------------------------------------------++
+     */
+    char expected[512];
+
     char *out = NULL, *p = NULL;
     int ret = 0, len;
     BIO *bio = NULL;
+    const int syserr = EPERM;
+    int reasoncode;
+
+    /*
+     * We set a mark here so we can clear the system error that we generate
+     * with ERR_PUT_error().  That is, after all, just a simulation to verify
+     * ERR_print_errors() output, not a real error.
+     */
+    ERR_set_mark();
 
-    BIO_snprintf(expected, sizeof(expected), expected_format, OPENSSL_FUNC);
+    ERR_PUT_error(ERR_LIB_SYS, 0, syserr, file, line);
+    reasoncode = ERR_GET_REASON(ERR_peek_error());
+
+    if (!TEST_int_eq(reasoncode, syserr)) {
+        ERR_pop_to_mark();
+        goto err;
+    }
+
+    BIO_snprintf(expected, sizeof(expected), expected_format,
+                 func, strerror(syserr), file, line);
 
     if (!TEST_ptr(bio = BIO_new(BIO_s_mem())))
-        return 0;
+        goto err;
 
-    ERR_PUT_error(ERR_LIB_SYS, 0, 1, "errtest.c", 30);
     ERR_print_errors(bio);
 
     if (!TEST_int_gt(len = BIO_get_mem_data(bio, &out), 0))
@@ -106,7 +135,7 @@ static int raised_error(void)
     file = __FILE__;
     line = __LINE__ + 2; /* The error is generated on the ERR_raise_data line */
 #endif
-    ERR_raise_data(ERR_LIB_SYS, ERR_R_INTERNAL_ERROR,
+    ERR_raise_data(ERR_LIB_NONE, ERR_R_INTERNAL_ERROR,
                    "calling exit()");
     if (!TEST_ulong_ne(e = ERR_get_error_all(&f, &l, NULL, &data, NULL), 0)
             || !TEST_int_eq(ERR_GET_REASON(e), ERR_R_INTERNAL_ERROR)
diff --git a/test/recipes/02-test_errstr.t b/test/recipes/02-test_errstr.t
index 76e0bba43c..53a4ef8412 100644
--- a/test/recipes/02-test_errstr.t
+++ b/test/recipes/02-test_errstr.t
@@ -11,15 +11,9 @@ no strict 'refs';               # To be able to use strings as function refs
 use OpenSSL::Test;
 use OpenSSL::Test::Utils;
 use Errno qw(:POSIX);
-use POSIX qw(strerror);
+use POSIX qw(:limits_h strerror);
 
-# We actually have space for up to 4095 error messages,
-# numerically speaking...  but we're currently only using
-# numbers 1 through 127.
-# This constant should correspond to the same constant
-# defined in crypto/err/err.c, or at least must not be
-# assigned a greater number.
-use constant NUM_SYS_STR_REASONS => 127;
+use Data::Dumper;
 
 setup('test_errstr');
 
@@ -40,84 +34,40 @@ plan skip_all => 'This is unsupported on MSYS/MinGW or MSWin32'
 plan skip_all => 'OpenSSL is configured "no-autoerrinit" or "no-err"'
     if disabled('autoerrinit') || disabled('err');
 
-# These are POSIX error names, which Errno implements as functions
-# (this is documented)
-my @posix_errors = @{$Errno::EXPORT_TAGS{POSIX}};
-
-if ($^O eq 'MSWin32') {
-    # On Windows, these errors have been observed to not always be loaded by
-    # apps/openssl, while they are in perl, which causes a difference that we
-    # consider a false alarm.  So we skip checking these errors.
-    # Because we can't know exactly what symbols exist in a perticular perl
-    # version, we resort to discovering them directly in the Errno package
-    # symbol table.
-    my @error_skiplist = qw(
-        ENETDOWN
-        ENETUNREACH
-        ENETRESET
-        ECONNABORTED
-        EISCONN
-        ENOTCONN
-        ESHUTDOWN
-        ETOOMANYREFS
-        ETIMEDOUT
-        EHOSTDOWN
-        EHOSTUNREACH
-        EALREADY
-        EINPROGRESS
-        ESTALE
-        EUCLEAN
-        ENOTNAM
-        ENAVAIL
-        ENOMEDIUM
-        ENOKEY
-    );
-    @posix_errors =
-        grep {
-            my $x = $_;
-            ! grep {
-                exists $Errno::{$_} && $x == $Errno::{$_}
-            } @error_skiplist
-        } @posix_errors;
-}
+# OpenSSL constants found in <openssl/err.h>
+use constant ERR_SYSTEM_FLAG => INT_MAX + 1;
+use constant ERR_LIB_OFFSET => 23; # Offset of the "library" errcode section
+
+# OpenSSL "library" numbers
+use constant ERR_LIB_NONE => 1;
 
-plan tests => scalar @posix_errors
+# We use Errno::EXPORT_OK as a list of known errno values on the current
+# system.  libcrypto's ERR should either use the same string as perl, or if
+# it was outside the range that ERR looks at, ERR gives the reason string
+# "reason(nnn)", where nnn is the errno number.
+
+plan tests => scalar @Errno::EXPORT_OK
     +1                          # Checking that error 128 gives 'reason(128)'
     +1                          # Checking that error 0 gives the library name
     ;
 
-foreach my $errname (@posix_errors) {
-    my $errnum = "Errno::$errname"->();
-
- SKIP: {
-        skip "Error $errname ($errnum) isn't within our range", 1
-            if $errnum > NUM_SYS_STR_REASONS;
-
-        my $perr = eval {
-            # Set $! to the error number...
-            local $! = $errnum;
-            # ... and $! will give you the error string back
-            $!
-        };
-
-        # We know that the system reasons are in OpenSSL error library 2
-        my @oerr = run(app([ qw(openssl errstr), sprintf("2%06x", $errnum) ]),
-                       capture => 1);
-        $oerr[0] =~ s|\R$||;
-        @oerr = split_error($oerr[0]);
-        ok($oerr[3] eq $perr, "($errnum) '$oerr[3]' == '$perr'");
-    }
+# Test::More:ok() has a sub prototype, which means we need to use the '&ok'
+# syntax to force it to accept a list as a series of arguments.
+
+foreach my $errname (@Errno::EXPORT_OK) {
+    # The error names are perl constants, which are implemented as functions
+    # returning the numeric value of that name.
+    &ok(match_syserr_reason("Errno::$errname"->()))
 }
 
-my @after = run(app([ qw(openssl errstr 2000080) ]), capture => 1);
-$after[0] =~ s|\R$||;
- at after = split_error($after[0]);
-ok($after[3] eq "reason(128)", "(128) '$after[3]' == 'reason(128)'");
+# OpenSSL library 1 is the "unknown" library
+&ok(match_opensslerr_reason(ERR_LIB_NONE << ERR_LIB_OFFSET | 256,
+                            "reason(256)"));
+# Reason code 0 of any library gives the library name as reason
+&ok(match_opensslerr_reason(ERR_LIB_NONE << ERR_LIB_OFFSET |   0,
+                            "unknown library"));
 
-my @zero = run(app([ qw(openssl errstr 2000000) ]), capture => 1);
-$zero[0] =~ s|\R$||;
- at zero = split_error($zero[0]);
-ok($zero[3] eq "system library", "(0) '$zero[3]' == 'system library'");
+exit 0;
 
 # For an error string "error:xxxxxxxx:lib:func:reason", this returns
 # the following array:
@@ -132,3 +82,54 @@ sub split_error {
 
     return @erritems;
 }
+
+# Compares the first argument as string to each of the arguments 3 and on,
+# and returns an array of two elements:
+# 0:  True if the first argument matched any of the others, otherwise false
+# 1:  A string describing the test
+# The returned array can be used as the arguments to Test::More::ok()
+sub match_any {
+    my $first = shift;
+    my $desc = shift;
+    my @strings = @_;
+
+    if (scalar @strings > 1) {
+        $desc = "match '$first' ($desc) with one of ( '"
+            . join("', '", @strings) . "' )";
+    } else {
+        $desc = "match '$first' ($desc) with '$strings[0]'";
+    }
+
+    return ( scalar( grep { $first eq $_ } @strings ) > 0,
+             $desc );
+}
+
+sub match_opensslerr_reason {
+    my $errcode = shift;
+    my @strings = @_;
+
+    my $errcode_hex = sprintf "%x", $errcode;
+    my $reason =
+        ( run(app([ qw(openssl errstr), $errcode_hex ]), capture => 1) )[0];
+    $reason =~ s|\R$||;
+    $reason = ( split_error($reason) )[3];
+
+    return match_any($reason, $errcode, @strings);
+}
+
+sub match_syserr_reason {
+    my $errcode = shift;
+
+    my @strings = ();
+    # The POSIX reason string
+    push @strings, eval {
+          # Set $! to the error number...
+          local $! = $errcode;
+          # ... and $! will give you the error string back
+          $!
+    };
+    # The OpenSSL fallback string
+    push @strings, "reason($errcode)";
+
+    return match_opensslerr_reason(ERR_SYSTEM_FLAG | $errcode, @strings);
+}


More information about the openssl-commits mailing list