xref: /openssl/crypto/err/err_save.c (revision 94300d8d)
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