Error Handling
s2n-tls functions that return 'int' return 0 to indicate success and -1 to indicate
failure. s2n-tls functions that return pointer types return NULL in the case of
failure. When an s2n-tls function returns a failure, s2n_errno will be set to a value
corresponding to the error. This error value can be translated into a string
explaining the error in English by calling s2n_strerror(s2n_errno, "EN")
.
A string containing human readable error name, can be generated with s2n_strerror_name()
.
A string containing internal debug information, including filename and line number, can be generated with s2n_strerror_debug()
.
This string is useful to include when reporting issues to the s2n-tls development team.
Example:
if (s2n_config_set_cipher_preferences(config, prefs) < 0) {
printf("Setting cipher prefs failed! %s : %s", s2n_strerror(s2n_errno, "EN"), s2n_strerror_debug(s2n_errno, "EN"));
return -1;
}
NOTE: To avoid possible confusion, s2n_errno should be cleared after processing an error: s2n_errno = S2N_ERR_T_OK
When using s2n-tls outside of C
, the address of the thread-local s2n_errno
may be obtained by calling the s2n_errno_location()
function.
This will ensure that the same TLS mechanisms are used with which s2n-tls was compiled.
Error Types
s2n-tls organizes errors into different "types" to allow applications to handle error values without catching all possibilities.
Applications using non-blocking I/O should check the error type to determine if the I/O operation failed because it would block or for some other error. To retrieve the type for a given error use s2n_error_get_type()
.
Applications should perform any error handling logic using these high level types:
Here's an example that handles errors based on type:
#define SUCCESS 0
#define FAILURE 1
#define RETRY 2
s2n_errno = S2N_ERR_T_OK;
if (s2n_negotiate(conn, &blocked) < 0) {
switch(s2n_error_get_type(s2n_errno)) {
case S2N_ERR_T_BLOCKED:
/* Blocked, come back later */
return RETRY;
case S2N_ERR_T_CLOSED:
return SUCCESS;
case S2N_ERR_T_IO:
handle_io_err(errno);
return FAILURE;
case S2N_ERR_T_PROTO:
handle_proto_err();
return FAILURE;
case S2N_ERR_T_ALERT:
log_alert(s2n_connection_get_alert(conn));
return FAILURE;
/* Everything else */
default:
log_other_error();
return FAILURE;
}
}
Blinding
Blinding is a mitigation against timing side-channels which in some cases can leak information about encrypted data. By default s2n-tls will cause a thread to sleep between 10 and 30 seconds whenever tampering is detected.
Setting the S2N_SELF_SERVICE_BLINDING
option with s2n_connection_set_blinding()
turns off this behavior. This is useful for applications that are handling many connections in a single thread. In that case, if s2n_recv()
or s2n_negotiate()
return an error, self-service applications must call s2n_connection_get_delay()
and pause activity on the connection for the specified number of nanoseconds before calling close()
or shutdown()
. s2n_shutdown()
will fail if called before the blinding delay elapses.
Stacktraces
s2n-tls has an mechanism to capture stacktraces when errors occur.
This mechanism is off by default, but can be enabled in code by calling s2n_stack_traces_enabled_set()
.
It can be enabled globally by setting the environment variable S2N_PRINT_STACKTRACE=1
.
Call s2n_print_stacktrace()
to print your stacktrace.
Note: Enabling stacktraces can significantly slow down unit tests, causing failures on tests (such as s2n_cbc_verify
) that measure the timing of events.