summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--crypto/err/build.info2
-rw-r--r--crypto/err/err.c10
-rw-r--r--crypto/err/err_local.h5
-rw-r--r--crypto/err/err_save.c89
-rw-r--r--doc/build.info6
-rw-r--r--doc/man3/OSSL_ERR_STATE_save.pod69
-rw-r--r--include/internal/quic_channel.h3
-rw-r--r--include/openssl/err.h.in5
-rw-r--r--ssl/quic/quic_channel.c28
-rw-r--r--ssl/quic/quic_channel_local.h3
-rw-r--r--ssl/quic/quic_impl.c17
-rw-r--r--util/libcrypto.num4
12 files changed, 224 insertions, 17 deletions
diff --git a/crypto/err/build.info b/crypto/err/build.info
index ee37938418..8552d7c393 100644
--- a/crypto/err/build.info
+++ b/crypto/err/build.info
@@ -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
diff --git a/crypto/err/err.c b/crypto/err/err.c
index a6c5ff6132..972856ad23 100644
--- a/crypto/err/err.c
+++ b/crypto/err/err.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;
}
diff --git a/crypto/err/err_local.h b/crypto/err/err_local.h
index 7d785ab618..202ac35ad4 100644
--- a/crypto/err/err_local.h
+++ b/crypto/err/err_local.h
@@ -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,
diff --git a/crypto/err/err_save.c b/crypto/err/err_save.c
new file mode 100644
index 0000000000..a471b22682
--- /dev/null
+++ b/crypto/err/err_save.c
@@ -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);
+ }
+ }
+}
diff --git a/doc/build.info b/doc/build.info
index 24d4050a45..7b96381240 100644
--- a/doc/build.info
+++ b/doc/build.info
@@ -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 \
diff --git a/doc/man3/OSSL_ERR_STATE_save.pod b/doc/man3/OSSL_ERR_STATE_save.pod
new file mode 100644
index 0000000000..79f3aba5f8
--- /dev/null
+++ b/doc/man3/OSSL_ERR_STATE_save.pod
@@ -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
diff --git a/include/internal/quic_channel.h b/include/internal/quic_channel.h
index bfa5818574..0606a11698 100644
--- a/include/internal/quic_channel.h
+++ b/include/internal/quic_channel.h
@@ -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);
diff --git a/include/openssl/err.h.in b/include/openssl/err.h.in
index 26b98800f9..075d683f8d 100644
--- a/include/openssl/err.h.in
+++ b/include/openssl/err.h.in
@@ -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
diff --git a/ssl/quic/quic_channel.c b/ssl/quic/quic_channel.c
index 0ea0c8110a..3edb059ad4 100644
--- a/ssl/quic/quic_channel.c
+++ b/ssl/quic/quic_channel.c
@@ -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;
diff --git a/ssl/quic/quic_channel_local.h b/ssl/quic/quic_channel_local.h
index 69469780aa..44ebc23f22 100644
--- a/ssl/quic/quic_channel_local.h
+++ b/ssl/quic/quic_channel_local.h
@@ -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
diff --git a/ssl/quic/quic_impl.c b/ssl/quic/quic_impl.c
index c0f65c2138..2eb1602764 100644
--- a/ssl/quic/quic_impl.c
+++ b/ssl/quic/quic_impl.c
@@ -130,20 +130,23 @@ static int quic_raise_non_normal_error(QCTX *ctx,
{
va_list args;
- ERR_new();
- ERR_set_debug(file, line, func);
-
- va_start(args, fmt);
- 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;
+
+ 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);
+
+ va_start(args, fmt);
+ ERR_vset_error(ERR_LIB_SSL, reason, fmt, args);
+ va_end(args);
+
return 0;
}
diff --git a/util/libcrypto.num b/util/libcrypto.num
index d909721a36..3195ccfbd8 100644
--- a/util/libcrypto.num
+++ b/util/libcrypto.num
@@ -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: