Open-Source Wikis

/

OpenSSL

/

Subsystems

/

Error handling

openssl/openssl

Error handling

Active contributors: Tomas Mraz, Pauli, Richard Levitte, Matt Caswell

Purpose

OpenSSL functions almost universally return 0/NULL on failure rather than errno-style codes. The detail comes from a per-thread error stack: each failure pushes one or more ERR_STATE records identifying the library, reason, source file, line, and (optionally) extra data.

The error stack is the first thing to check when an OpenSSL call fails. See how-to-contribute/debugging.

Directory layout

crypto/err/
├── err.c                -- the per-thread stack and ERR_get_error / ERR_raise
├── err_blocks.c         -- mark/release blocks for nested calls
├── err_local.h
├── err_prn.c            -- ERR_print_errors_fp/_cb
├── openssl.txt          -- the master list of every error code (regenerated, but committed)
├── openssl_err.c, *_err.c (per library)  -- string tables for reason codes
├── README.md            -- the guide for adding error codes
└── err_mark.c

Each library has its own pair:

  • <lib>err.h (in include/openssl/) — declares <LIB>_R_<REASON> enum values.
  • <lib>_err.c (somewhere under crypto/<lib>/) — maps those reasons to human-readable strings.

util/mkerr.pl generates both from crypto/err/openssl.txt plus annotations in the source files.

How an error is raised

ERR_raise(ERR_LIB_X509, X509_R_BAD_DATE);
ERR_raise_data(ERR_LIB_X509, X509_R_BAD_DATE, "value=%s", str);

ERR_raise records the current file and line. Internally it pushes onto a per-thread stack of fixed size (16 entries by default; the oldest entries are silently discarded on overflow).

There are also legacy per-library raise functions (X509err, SSLerr, etc.) but ERR_raise is preferred for new code.

How an error is consumed

unsigned long e;
while ((e = ERR_get_error()) != 0) {
    char buf[256];
    ERR_error_string_n(e, buf, sizeof(buf));
    fprintf(stderr, "openssl: %s\n", buf);
}

/* or, the one-shot helper: */
ERR_print_errors_fp(stderr);

ERR_get_error() pops the oldest entry. ERR_peek_error() looks without popping. ERR_peek_last_error() looks at the most recent entry. ERR_clear_error() empties the stack — call it at the boundary of your own error handling so old leftover errors don't get attributed to a later call.

The "with data" variant ERR_get_error_all(file, line, func, data, flags) returns the file/line/extra string the caller passed to ERR_raise_data.

How an error code decomposes

A 32-bit error code packs (library, reason):

| L L L L L L L L | R R R R R R R R R R R R R R R R |
       lib                       reason

ERR_GET_LIB(e) and ERR_GET_REASON(e) extract the components. Older releases also packed a function ordinal but it was dropped in 3.0 (the function is now stored as a string in the data fields instead).

ERRLIB* assignments

include/openssl/err.h.in defines every library identifier:

Constant Library
ERR_LIB_NONE unknown
ERR_LIB_SYS errno-derived (wrapped errno value)
ERR_LIB_BN BIGNUM
ERR_LIB_RSA, ERR_LIB_DH, ERR_LIB_EC, ERR_LIB_DSA per-algorithm
ERR_LIB_PEM, ERR_LIB_X509, ERR_LIB_X509V3, ERR_LIB_ASN1, ERR_LIB_OBJ parsing layers
ERR_LIB_CMS, ERR_LIB_PKCS7, ERR_LIB_PKCS12, ERR_LIB_OCSP, ERR_LIB_TS, ERR_LIB_CMP, ERR_LIB_CRMF, ERR_LIB_ESS message formats
ERR_LIB_SSL libssl
ERR_LIB_BIO, ERR_LIB_BUF, ERR_LIB_EVP, ERR_LIB_RAND core libcrypto
ERR_LIB_PROV providers (common)
ERR_LIB_USER application range

Adding a new error code

The procedure (also documented in crypto/err/README.md):

  1. Add a new reason in crypto/err/openssl.txt, in the section for the relevant library:

    X509_R_MY_NEW_REASON:412:my new reason
  2. Run make update. This regenerates include/openssl/x509err.h (the X509_R_* enum) and crypto/x509/x509_err.c (the string table).

  3. Use it: ERR_raise(ERR_LIB_X509, X509_R_MY_NEW_REASON);.

The numeric code is stable forever. Once shipped, even if you remove the use, you don't reuse the number.

Error stack policy

Conventions visible from reading libcrypto:

  • Functions returning int return 1 on success and 0 on error, except a few that have historically returned >0 for partial. Always check the actual function's man page.
  • Functions returning a pointer return NULL on error.
  • Cleanup must not push errors: if X509_free encounters trouble it must silently soldier on.
  • Boundary clear: at the top of a public API, the implementer typically does ERR_clear_error() only when retrying inside the function; callers are expected to clear before a fresh operation.
  • Provider errors: providers raise into the ERR_LIB_PROV range using proverr.h. The string table is in providers/common/provider_err.c.

Marking and releasing blocks

ERR_set_mark() records a "high water mark" on the stack; ERR_pop_to_mark() discards any errors raised since the mark. This is used inside the library to attempt-and-recover without burdening the caller's stack with intermediate failures:

ERR_set_mark();
if (try_pkcs8_oid(...) > 0) { ERR_pop_to_mark(); return ok; }
ERR_clear_last_mark();   /* commit any errors raised */
return -1;

Integration points

  • Every library raises into the same thread-local stack.
  • apps/openssl calls ERR_print_errors(bio_err) after a subcommand fails.
  • The TLS state machine pushes alerts that map onto SSL_R_* reasons; SSL_get_error(ssl, ret) plus the error stack together identify what went wrong.
  • The trace facility (OSSL_TRACE) is independent of the error stack — for free-form logging.

Entry points for modification

  • New error code: edit crypto/err/openssl.txt, run make update, commit. Treat the numeric assignment as immutable.
  • Larger stack size: not advised — applications rely on the default; bumping it may hide error-clearing bugs.
  • Provider error reporting: providers/common/provider_err.c and proverr.h. Use ERR_raise_data() so reasons can carry runtime context.

Documentation

  • crypto/err/README.md — internal contributor guide.
  • doc/man3/ERR_get_error.pod, ERR_print_errors.pod, ERR_set_mark.pod, ERR_load_strings.pod — API.
  • doc/man7/openssl-env.pod — environment-variable hooks.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Error handling – OpenSSL wiki | Factory