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.cEach library has its own pair:
<lib>err.h(ininclude/openssl/) — declares<LIB>_R_<REASON>enum values.<lib>_err.c(somewhere undercrypto/<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 reasonERR_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):
Add a new reason in
crypto/err/openssl.txt, in the section for the relevant library:X509_R_MY_NEW_REASON:412:my new reasonRun
make update. This regeneratesinclude/openssl/x509err.h(theX509_R_*enum) andcrypto/x509/x509_err.c(the string table).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
intreturn1on success and0on error, except a few that have historically returned>0for partial. Always check the actual function's man page. - Functions returning a pointer return
NULLon error. - Cleanup must not push errors: if
X509_freeencounters 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_PROVrange usingproverr.h. The string table is inproviders/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/opensslcallsERR_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, runmake 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.candproverr.h. UseERR_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.