mirror of https://github.com/openssl/openssl
QUIC err handling: Save and restore error state
We save the error state from the thread that encountered a permanent error condition caused by system or internal error to the QUIC_CHANNEL. Then we restore it whenever we are returning to a user call when protocol is shutdown. Reviewed-by: Paul Dale <pauli@openssl.org> Reviewed-by: Hugo Landau <hlandau@openssl.org> Reviewed-by: Matt Caswell <matt@openssl.org> (Merged from https://github.com/openssl/openssl/pull/21087)
This commit is contained in:
parent
5c3474ea56
commit
9c3ea4e1d7
|
@ -1,3 +1,3 @@
|
|||
LIBS=../../libcrypto
|
||||
SOURCE[../../libcrypto]=\
|
||||
err_blocks.c err_mark.c err.c err_all.c err_all_legacy.c err_prn.c
|
||||
err_blocks.c err_mark.c err.c err_all.c err_all_legacy.c err_prn.c err_save.c
|
||||
|
|
|
@ -33,7 +33,6 @@ ERR_STATE *ERR_get_state(void);
|
|||
static int err_load_strings(const ERR_STRING_DATA *str);
|
||||
#endif
|
||||
|
||||
static void ERR_STATE_free(ERR_STATE *s);
|
||||
#ifndef OPENSSL_NO_ERR
|
||||
static ERR_STRING_DATA ERR_str_libraries[] = {
|
||||
{ERR_PACK(ERR_LIB_NONE, 0, 0), "unknown library"},
|
||||
|
@ -199,7 +198,7 @@ static ERR_STRING_DATA *int_err_get_item(const ERR_STRING_DATA *d)
|
|||
}
|
||||
#endif
|
||||
|
||||
static void ERR_STATE_free(ERR_STATE *state)
|
||||
void OSSL_ERR_STATE_free(ERR_STATE *state)
|
||||
{
|
||||
int i;
|
||||
|
||||
|
@ -649,7 +648,7 @@ static void err_delete_thread_state(void *unused)
|
|||
return;
|
||||
|
||||
CRYPTO_THREAD_set_local(&err_thread_local, NULL);
|
||||
ERR_STATE_free(state);
|
||||
OSSL_ERR_STATE_free(state);
|
||||
}
|
||||
|
||||
#ifndef OPENSSL_NO_DEPRECATED_1_1_0
|
||||
|
@ -689,8 +688,7 @@ ERR_STATE *ossl_err_get_state_int(void)
|
|||
if (!CRYPTO_THREAD_set_local(&err_thread_local, (ERR_STATE*)-1))
|
||||
return NULL;
|
||||
|
||||
/* calling CRYPTO_zalloc(.., NULL, 0) prevents mem alloc error loop */
|
||||
state = CRYPTO_zalloc(sizeof(*state), NULL, 0);
|
||||
state = OSSL_ERR_STATE_new();
|
||||
if (state == NULL) {
|
||||
CRYPTO_THREAD_set_local(&err_thread_local, NULL);
|
||||
return NULL;
|
||||
|
@ -698,7 +696,7 @@ ERR_STATE *ossl_err_get_state_int(void)
|
|||
|
||||
if (!ossl_init_thread_start(NULL, NULL, err_delete_thread_state)
|
||||
|| !CRYPTO_THREAD_set_local(&err_thread_local, state)) {
|
||||
ERR_STATE_free(state);
|
||||
OSSL_ERR_STATE_free(state);
|
||||
CRYPTO_THREAD_set_local(&err_thread_local, NULL);
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -66,8 +66,9 @@ static ossl_inline void err_set_debug(ERR_STATE *es, size_t i,
|
|||
OPENSSL_free(es->err_func[i]);
|
||||
if (fn == NULL || fn[0] == '\0')
|
||||
es->err_func[i] = NULL;
|
||||
else
|
||||
es->err_func[i] = OPENSSL_strdup(fn);
|
||||
else if ((es->err_func[i] = CRYPTO_malloc(strlen(fn) + 1,
|
||||
NULL, 0)) != NULL)
|
||||
strcpy(es->err_func[i], fn);
|
||||
}
|
||||
|
||||
static ossl_inline void err_set_data(ERR_STATE *es, size_t i,
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* Copyright 2023 The OpenSSL Project Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License 2.0 (the "License"). You may not use
|
||||
* this file except in compliance with the License. You can obtain a copy
|
||||
* in the file LICENSE in the source distribution or at
|
||||
* https://www.openssl.org/source/license.html
|
||||
*/
|
||||
|
||||
#define OSSL_FORCE_ERR_STATE
|
||||
|
||||
#include <openssl/err.h>
|
||||
#include "err_local.h"
|
||||
|
||||
/*
|
||||
* Save and restore error state.
|
||||
* We are using CRYPTO_zalloc(.., NULL, 0) instead of OPENSSL_malloc() in
|
||||
* these functions to prevent mem alloc error loop.
|
||||
*/
|
||||
|
||||
ERR_STATE *OSSL_ERR_STATE_new(void)
|
||||
{
|
||||
return CRYPTO_zalloc(sizeof(ERR_STATE), NULL, 0);
|
||||
}
|
||||
|
||||
void OSSL_ERR_STATE_save(ERR_STATE *es)
|
||||
{
|
||||
size_t i;
|
||||
ERR_STATE *thread_es;
|
||||
|
||||
if (es == NULL)
|
||||
return;
|
||||
|
||||
for (i = 0; i < ERR_NUM_ERRORS; i++)
|
||||
err_clear(es, i, 1);
|
||||
|
||||
thread_es = ossl_err_get_state_int();
|
||||
if (thread_es == NULL)
|
||||
return;
|
||||
|
||||
memcpy(es, thread_es, sizeof(*es));
|
||||
/* Taking over the pointers, just clear the thread state. */
|
||||
memset(thread_es, 0, sizeof(*thread_es));
|
||||
}
|
||||
|
||||
void OSSL_ERR_STATE_restore(const ERR_STATE *es)
|
||||
{
|
||||
size_t i;
|
||||
ERR_STATE *thread_es;
|
||||
|
||||
if (es == NULL || es->bottom == es->top)
|
||||
return;
|
||||
|
||||
thread_es = ossl_err_get_state_int();
|
||||
if (thread_es == NULL)
|
||||
return;
|
||||
|
||||
for (i = (size_t)es->bottom; i != (size_t)es->top;) {
|
||||
size_t top;
|
||||
|
||||
i = (i + 1) % ERR_NUM_ERRORS;
|
||||
if ((es->err_flags[i] & ERR_FLAG_CLEAR) != 0)
|
||||
continue;
|
||||
|
||||
err_get_slot(thread_es);
|
||||
top = thread_es->top;
|
||||
err_clear(thread_es, top, 0);
|
||||
|
||||
thread_es->err_flags[top] = es->err_flags[i];
|
||||
thread_es->err_buffer[top] = es->err_buffer[i];
|
||||
|
||||
err_set_debug(thread_es, top, es->err_file[i], es->err_line[i],
|
||||
es->err_func[i]);
|
||||
|
||||
if (es->err_data[i] != NULL && es->err_data_size[i] != 0) {
|
||||
void *data;
|
||||
size_t data_sz = es->err_data_size[i];
|
||||
|
||||
data = CRYPTO_malloc(data_sz, NULL, 0);
|
||||
if (data != NULL) {
|
||||
memcpy(data, es->err_data[i], data_sz);
|
||||
err_set_data(thread_es, top, data, data_sz,
|
||||
es->err_data_flags[i] | ERR_TXT_MALLOCED);
|
||||
}
|
||||
} else {
|
||||
err_clear_data(thread_es, top, 0);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1687,6 +1687,10 @@ DEPEND[html/man3/OSSL_ENCODER_to_bio.html]=man3/OSSL_ENCODER_to_bio.pod
|
|||
GENERATE[html/man3/OSSL_ENCODER_to_bio.html]=man3/OSSL_ENCODER_to_bio.pod
|
||||
DEPEND[man/man3/OSSL_ENCODER_to_bio.3]=man3/OSSL_ENCODER_to_bio.pod
|
||||
GENERATE[man/man3/OSSL_ENCODER_to_bio.3]=man3/OSSL_ENCODER_to_bio.pod
|
||||
DEPEND[html/man3/OSSL_ERR_STATE_save.html]=man3/OSSL_ERR_STATE_save.pod
|
||||
GENERATE[html/man3/OSSL_ERR_STATE_save.html]=man3/OSSL_ERR_STATE_save.pod
|
||||
DEPEND[man/man3/OSSL_ERR_STATE_save.3]=man3/OSSL_ERR_STATE_save.pod
|
||||
GENERATE[man/man3/OSSL_ERR_STATE_save.3]=man3/OSSL_ERR_STATE_save.pod
|
||||
DEPEND[html/man3/OSSL_ESS_check_signing_certs.html]=man3/OSSL_ESS_check_signing_certs.pod
|
||||
GENERATE[html/man3/OSSL_ESS_check_signing_certs.html]=man3/OSSL_ESS_check_signing_certs.pod
|
||||
DEPEND[man/man3/OSSL_ESS_check_signing_certs.3]=man3/OSSL_ESS_check_signing_certs.pod
|
||||
|
@ -3325,6 +3329,7 @@ html/man3/OSSL_ENCODER.html \
|
|||
html/man3/OSSL_ENCODER_CTX.html \
|
||||
html/man3/OSSL_ENCODER_CTX_new_for_pkey.html \
|
||||
html/man3/OSSL_ENCODER_to_bio.html \
|
||||
html/man3/OSSL_ERR_STATE_save.html \
|
||||
html/man3/OSSL_ESS_check_signing_certs.html \
|
||||
html/man3/OSSL_HPKE_CTX_new.html \
|
||||
html/man3/OSSL_HTTP_REQ_CTX.html \
|
||||
|
@ -3963,6 +3968,7 @@ man/man3/OSSL_ENCODER.3 \
|
|||
man/man3/OSSL_ENCODER_CTX.3 \
|
||||
man/man3/OSSL_ENCODER_CTX_new_for_pkey.3 \
|
||||
man/man3/OSSL_ENCODER_to_bio.3 \
|
||||
man/man3/OSSL_ERR_STATE_save.3 \
|
||||
man/man3/OSSL_ESS_check_signing_certs.3 \
|
||||
man/man3/OSSL_HPKE_CTX_new.3 \
|
||||
man/man3/OSSL_HTTP_REQ_CTX.3 \
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
=pod
|
||||
|
||||
=head1 NAME
|
||||
|
||||
OSSL_ERR_STATE_new, OSSL_ERR_STATE_save, OSSL_ERR_STATE_restore,
|
||||
OSSL_ERR_STATE_free - saving and restoring error state
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
#include <openssl/err.h>
|
||||
|
||||
ERR_STATE *OSSL_ERR_STATE_new(void);
|
||||
void OSSL_ERR_STATE_save(ERR_STATE *es);
|
||||
void OSSL_ERR_STATE_restore(const ERR_STATE *es);
|
||||
void OSSL_ERR_STATE_free(ERR_STATE *es);
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
These functions save and restore the error state from the thread
|
||||
local error state to a preallocated error state structure.
|
||||
|
||||
OSSL_ERR_STATE_new() allocates an empty error state structure to
|
||||
be used when saving and restoring thread error state.
|
||||
|
||||
OSSL_ERR_STATE_save() saves the thread error state to I<es>. It
|
||||
subsequently clears the thread error state. Any previously saved
|
||||
state in I<es> is cleared prior to saving the new state.
|
||||
|
||||
OSSL_ERR_STATE_restore() adds all the error entries from the
|
||||
saved state I<es> to the thread error state. Existing entries in
|
||||
the thread error state are not affected if there is enough space
|
||||
for all the added entries. Any allocated data in the saved error
|
||||
entries is duplicated on adding to the thread state.
|
||||
|
||||
OSSL_ERR_STATE_free() frees the saved error state I<es>.
|
||||
|
||||
=head1 RETURN VALUES
|
||||
|
||||
OSSL_ERR_STATE_new() returns a pointer to the allocated ERR_STATE
|
||||
structure or NULL on error.
|
||||
|
||||
OSSL_ERR_STATE_save(), OSSL_ERR_STATE_restore(), OSSL_ERR_STATE_free()
|
||||
do not return any values.
|
||||
|
||||
=head1 NOTES
|
||||
|
||||
OSSL_ERR_STATE_save() cannot fail as it takes over any allocated
|
||||
data from the thread error state.
|
||||
|
||||
OSSL_ERR_STATE_restore() is a best effort function. The only failure
|
||||
that can happen during its operation is when memory allocation fails.
|
||||
Because it manipulates the thread error state it avoids raising memory
|
||||
errors on such failure. At worst the restored error entries will be
|
||||
missing the auxiliary error data.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
L<ERR_raise(3)>, L<ERR_get_error(3)>, L<ERR_clear_error(3)>
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
Copyright 2023 The OpenSSL Project Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License 2.0 (the "License"). You may not use
|
||||
this file except in compliance with the License. You can obtain a copy
|
||||
in the file LICENSE in the source distribution or at
|
||||
L<https://www.openssl.org/source/license.html>.
|
||||
|
||||
=cut
|
|
@ -222,6 +222,9 @@ void ossl_quic_channel_raise_protocol_error(QUIC_CHANNEL *ch,
|
|||
*/
|
||||
int ossl_quic_channel_net_error(QUIC_CHANNEL *ch);
|
||||
|
||||
/* Restore saved error state (best effort) */
|
||||
void ossl_quic_channel_restore_err_state(QUIC_CHANNEL *ch);
|
||||
|
||||
/* For RXDP use. */
|
||||
void ossl_quic_channel_on_remote_conn_close(QUIC_CHANNEL *ch,
|
||||
OSSL_QUIC_FRAME_CONN_CLOSE *f);
|
||||
|
|
|
@ -486,6 +486,11 @@ int ERR_set_mark(void);
|
|||
int ERR_pop_to_mark(void);
|
||||
int ERR_clear_last_mark(void);
|
||||
|
||||
ERR_STATE *OSSL_ERR_STATE_new(void);
|
||||
void OSSL_ERR_STATE_save(ERR_STATE *es);
|
||||
void OSSL_ERR_STATE_restore(const ERR_STATE *es);
|
||||
void OSSL_ERR_STATE_free(ERR_STATE *es);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -7,12 +7,13 @@
|
|||
* https://www.openssl.org/source/license.html
|
||||
*/
|
||||
|
||||
#include <openssl/rand.h>
|
||||
#include <openssl/err.h>
|
||||
#include "internal/quic_channel.h"
|
||||
#include "internal/quic_error.h"
|
||||
#include "internal/quic_rx_depack.h"
|
||||
#include "../ssl_local.h"
|
||||
#include "quic_channel_local.h"
|
||||
#include <openssl/rand.h>
|
||||
|
||||
/*
|
||||
* NOTE: While this channel implementation currently has basic server support,
|
||||
|
@ -352,6 +353,7 @@ static void ch_cleanup(QUIC_CHANNEL *ch)
|
|||
ossl_qrx_free(ch->qrx);
|
||||
ossl_quic_demux_free(ch->demux);
|
||||
OPENSSL_free(ch->local_transport_params);
|
||||
OSSL_ERR_STATE_free(ch->err_state);
|
||||
}
|
||||
|
||||
QUIC_CHANNEL *ossl_quic_channel_new(const QUIC_CHANNEL_ARGS *args)
|
||||
|
@ -2554,11 +2556,23 @@ void ossl_quic_channel_on_new_conn_id(QUIC_CHANNEL *ch,
|
|||
}
|
||||
}
|
||||
|
||||
static void ch_save_err_state(QUIC_CHANNEL *ch)
|
||||
{
|
||||
if (ch->err_state == NULL)
|
||||
ch->err_state = OSSL_ERR_STATE_new();
|
||||
|
||||
if (ch->err_state == NULL)
|
||||
return;
|
||||
|
||||
OSSL_ERR_STATE_save(ch->err_state);
|
||||
}
|
||||
|
||||
static void ch_raise_net_error(QUIC_CHANNEL *ch)
|
||||
{
|
||||
QUIC_TERMINATE_CAUSE tcause = {0};
|
||||
|
||||
ch->net_error = 1;
|
||||
ch_save_err_state(ch);
|
||||
|
||||
tcause.error_code = QUIC_ERR_INTERNAL_ERROR;
|
||||
|
||||
|
@ -2574,6 +2588,14 @@ int ossl_quic_channel_net_error(QUIC_CHANNEL *ch)
|
|||
return ch->net_error;
|
||||
}
|
||||
|
||||
void ossl_quic_channel_restore_err_state(QUIC_CHANNEL *ch)
|
||||
{
|
||||
if (ch == NULL)
|
||||
return;
|
||||
|
||||
OSSL_ERR_STATE_restore(ch->err_state);
|
||||
}
|
||||
|
||||
void ossl_quic_channel_raise_protocol_error(QUIC_CHANNEL *ch,
|
||||
uint64_t error_code,
|
||||
uint64_t frame_type,
|
||||
|
@ -2581,6 +2603,10 @@ void ossl_quic_channel_raise_protocol_error(QUIC_CHANNEL *ch,
|
|||
{
|
||||
QUIC_TERMINATE_CAUSE tcause = {0};
|
||||
|
||||
if (error_code == QUIC_ERR_INTERNAL_ERROR)
|
||||
/* Internal errors might leave some errors on the stack. */
|
||||
ch_save_err_state(ch);
|
||||
|
||||
tcause.error_code = error_code;
|
||||
tcause.frame_type = frame_type;
|
||||
|
||||
|
|
|
@ -402,6 +402,9 @@ struct quic_channel_st {
|
|||
|
||||
/* Permanent net error encountered */
|
||||
unsigned int net_error : 1;
|
||||
|
||||
/* Saved error stack in case permanent error was encountered */
|
||||
ERR_STATE *err_state;
|
||||
};
|
||||
|
||||
# endif
|
||||
|
|
|
@ -130,6 +130,16 @@ static int quic_raise_non_normal_error(QCTX *ctx,
|
|||
{
|
||||
va_list args;
|
||||
|
||||
if (ctx != NULL) {
|
||||
if (ctx->is_stream && ctx->xso != NULL)
|
||||
ctx->xso->last_error = SSL_ERROR_SSL;
|
||||
else if (!ctx->is_stream && ctx->qc != NULL)
|
||||
ctx->qc->last_error = SSL_ERROR_SSL;
|
||||
|
||||
if (reason == SSL_R_PROTOCOL_IS_SHUTDOWN && ctx->qc != NULL)
|
||||
ossl_quic_channel_restore_err_state(ctx->qc->ch);
|
||||
}
|
||||
|
||||
ERR_new();
|
||||
ERR_set_debug(file, line, func);
|
||||
|
||||
|
@ -137,13 +147,6 @@ static int quic_raise_non_normal_error(QCTX *ctx,
|
|||
ERR_vset_error(ERR_LIB_SSL, reason, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
if (ctx != NULL) {
|
||||
if (ctx->is_stream && ctx->xso != NULL)
|
||||
ctx->xso->last_error = SSL_ERROR_SSL;
|
||||
else if (!ctx->is_stream && ctx->qc != NULL)
|
||||
ctx->qc->last_error = SSL_ERROR_SSL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -5518,3 +5518,7 @@ X509_STORE_CTX_init_rpk ? 3_2_0 EXIST::FUNCTION:
|
|||
X509_STORE_CTX_get0_rpk ? 3_2_0 EXIST::FUNCTION:
|
||||
X509_STORE_CTX_set0_rpk ? 3_2_0 EXIST::FUNCTION:
|
||||
CRYPTO_atomic_load_int ? 3_2_0 EXIST::FUNCTION:
|
||||
OSSL_ERR_STATE_new ? 3_2_0 EXIST::FUNCTION:
|
||||
OSSL_ERR_STATE_save ? 3_2_0 EXIST::FUNCTION:
|
||||
OSSL_ERR_STATE_restore ? 3_2_0 EXIST::FUNCTION:
|
||||
OSSL_ERR_STATE_free ? 3_2_0 EXIST::FUNCTION:
|
||||
|
|
Loading…
Reference in New Issue