1 /*
2 * Copyright 2023 The OpenSSL Project Authors. All Rights Reserved.
3 *
4 * Licensed under the Apache License 2.0 (the "License"). You may not use
5 * this file except in compliance with the License. You can obtain a copy
6 * in the file LICENSE in the source distribution or at
7 * https://www.openssl.org/source/license.html
8 */
9
10 #define OSSL_FORCE_ERR_STATE
11
12 #include <openssl/err.h>
13 #include "err_local.h"
14
15 /*
16 * Save and restore error state.
17 * We are using CRYPTO_zalloc(.., NULL, 0) instead of OPENSSL_malloc() in
18 * these functions to prevent mem alloc error loop.
19 */
20
OSSL_ERR_STATE_new(void)21 ERR_STATE *OSSL_ERR_STATE_new(void)
22 {
23 return CRYPTO_zalloc(sizeof(ERR_STATE), NULL, 0);
24 }
25
OSSL_ERR_STATE_save(ERR_STATE * es)26 void OSSL_ERR_STATE_save(ERR_STATE *es)
27 {
28 size_t i;
29 ERR_STATE *thread_es;
30
31 if (es == NULL)
32 return;
33
34 for (i = 0; i < ERR_NUM_ERRORS; i++)
35 err_clear(es, i, 1);
36
37 thread_es = ossl_err_get_state_int();
38 if (thread_es == NULL)
39 return;
40
41 memcpy(es, thread_es, sizeof(*es));
42 /* Taking over the pointers, just clear the thread state. */
43 memset(thread_es, 0, sizeof(*thread_es));
44 }
45
OSSL_ERR_STATE_save_to_mark(ERR_STATE * es)46 void OSSL_ERR_STATE_save_to_mark(ERR_STATE *es)
47 {
48 size_t i, j, count;
49 int top;
50 ERR_STATE *thread_es;
51
52 if (es == NULL)
53 return;
54
55 thread_es = ossl_err_get_state_int();
56 if (thread_es == NULL) {
57 for (i = 0; i < ERR_NUM_ERRORS; ++i)
58 err_clear(es, i, 1);
59
60 es->top = es->bottom = 0;
61 return;
62 }
63
64 /* Determine number of errors we are going to move. */
65 for (count = 0, top = thread_es->top;
66 thread_es->bottom != top
67 && thread_es->err_marks[top] == 0;
68 ++count)
69 top = top > 0 ? top - 1 : ERR_NUM_ERRORS - 1;
70
71 /* Move the errors, preserving order. */
72 for (i = 0, j = top; i < count; ++i) {
73 j = (j + 1) % ERR_NUM_ERRORS;
74
75 err_clear(es, i, 1);
76
77 /* Move the error entry to the given ERR_STATE. */
78 es->err_flags[i] = thread_es->err_flags[j];
79 es->err_marks[i] = 0;
80 es->err_buffer[i] = thread_es->err_buffer[j];
81 es->err_data[i] = thread_es->err_data[j];
82 es->err_data_size[i] = thread_es->err_data_size[j];
83 es->err_data_flags[i] = thread_es->err_data_flags[j];
84 es->err_file[i] = thread_es->err_file[j];
85 es->err_line[i] = thread_es->err_line[j];
86 es->err_func[i] = thread_es->err_func[j];
87
88 thread_es->err_flags[j] = 0;
89 thread_es->err_buffer[j] = 0;
90 thread_es->err_data[j] = NULL;
91 thread_es->err_data_size[j] = 0;
92 thread_es->err_data_flags[j] = 0;
93 thread_es->err_file[j] = NULL;
94 thread_es->err_line[j] = 0;
95 thread_es->err_func[j] = NULL;
96 }
97
98 if (i > 0) {
99 thread_es->top = top;
100 /* If we moved anything, es's stack always starts at [0]. */
101 es->top = i - 1;
102 es->bottom = ERR_NUM_ERRORS - 1;
103 } else {
104 /* Didn't move anything - empty stack */
105 es->top = es->bottom = 0;
106 }
107
108 /* Erase extra space as a precaution. */
109 for (; i < ERR_NUM_ERRORS; ++i)
110 err_clear(es, i, 1);
111 }
112
OSSL_ERR_STATE_restore(const ERR_STATE * es)113 void OSSL_ERR_STATE_restore(const ERR_STATE *es)
114 {
115 size_t i;
116 ERR_STATE *thread_es;
117
118 if (es == NULL || es->bottom == es->top)
119 return;
120
121 thread_es = ossl_err_get_state_int();
122 if (thread_es == NULL)
123 return;
124
125 for (i = (size_t)es->bottom; i != (size_t)es->top;) {
126 size_t top;
127
128 i = (i + 1) % ERR_NUM_ERRORS;
129 if ((es->err_flags[i] & ERR_FLAG_CLEAR) != 0)
130 continue;
131
132 err_get_slot(thread_es);
133 top = thread_es->top;
134 err_clear(thread_es, top, 0);
135
136 thread_es->err_flags[top] = es->err_flags[i];
137 thread_es->err_buffer[top] = es->err_buffer[i];
138
139 err_set_debug(thread_es, top, es->err_file[i], es->err_line[i],
140 es->err_func[i]);
141
142 if (es->err_data[i] != NULL && es->err_data_size[i] != 0) {
143 void *data;
144 size_t data_sz = es->err_data_size[i];
145
146 data = CRYPTO_malloc(data_sz, NULL, 0);
147 if (data != NULL) {
148 memcpy(data, es->err_data[i], data_sz);
149 err_set_data(thread_es, top, data, data_sz,
150 es->err_data_flags[i] | ERR_TXT_MALLOCED);
151 }
152 } else {
153 err_clear_data(thread_es, top, 0);
154 }
155 }
156 }
157