/src/nss-nspr/nss/lib/base/error.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
2 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
3 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
4 | | |
5 | | /* |
6 | | * error.c |
7 | | * |
8 | | * This file contains the code implementing the per-thread error |
9 | | * stacks upon which most NSS routines report their errors. |
10 | | */ |
11 | | |
12 | | #ifndef BASE_H |
13 | | #include "base.h" |
14 | | #endif /* BASE_H */ |
15 | | #include <limits.h> /* for UINT_MAX */ |
16 | | #include <string.h> /* for memmove */ |
17 | | |
18 | | #if defined(__MINGW32__) |
19 | | #include <windows.h> |
20 | | #endif |
21 | | |
22 | 0 | #define NSS_MAX_ERROR_STACK_COUNT 16 /* error codes */ |
23 | | |
24 | | /* |
25 | | * The stack itself has a header, and a sequence of integers. |
26 | | * The header records the amount of space (as measured in stack |
27 | | * slots) already allocated for the stack, and the count of the |
28 | | * number of records currently being used. |
29 | | */ |
30 | | |
31 | | struct stack_header_str { |
32 | | PRUint16 space; |
33 | | PRUint16 count; |
34 | | }; |
35 | | |
36 | | struct error_stack_str { |
37 | | struct stack_header_str header; |
38 | | PRInt32 stack[1]; |
39 | | }; |
40 | | typedef struct error_stack_str error_stack; |
41 | | |
42 | | /* |
43 | | * error_stack_index |
44 | | * |
45 | | * Thread-private data must be indexed. This is that index. |
46 | | * See PR_NewThreadPrivateIndex for more information. |
47 | | * |
48 | | * Thread-private data indexes are in the range [0, 127]. |
49 | | */ |
50 | | |
51 | 4 | #define INVALID_TPD_INDEX UINT_MAX |
52 | | static PRUintn error_stack_index = INVALID_TPD_INDEX; |
53 | | |
54 | | /* |
55 | | * call_once |
56 | | * |
57 | | * The thread-private index must be obtained (once!) at runtime. |
58 | | * This block is used for that one-time call. |
59 | | */ |
60 | | |
61 | | static PRCallOnceType error_call_once; |
62 | | static const PRCallOnceType error_call_again; |
63 | | |
64 | | /* |
65 | | * error_once_function |
66 | | * |
67 | | * This is the once-called callback. |
68 | | */ |
69 | | static PRStatus |
70 | | error_once_function(void) |
71 | 2 | { |
72 | | |
73 | | /* |
74 | | * This #ifdef function is redundant. It performs the same thing as the |
75 | | * else case. |
76 | | * |
77 | | * However, the MinGW version looks up the function from nss3's export |
78 | | * table, and on MinGW _that_ behaves differently than passing a |
79 | | * function pointer in a different module because MinGW has |
80 | | * -mnop-fun-dllimport specified, which generates function thunks for |
81 | | * cross-module calls. And when a module (like nssckbi) gets unloaded, |
82 | | * and you try to call into that thunk (which is now missing) you'll |
83 | | * crash. So we do this bit of ugly to avoid that crash. Fortunately |
84 | | * this is the only place we've had to do this. |
85 | | */ |
86 | | #if defined(__MINGW32__) |
87 | | HMODULE nss3 = GetModuleHandleW(L"nss3"); |
88 | | if (nss3) { |
89 | | PRThreadPrivateDTOR freePtr = (PRThreadPrivateDTOR)GetProcAddress(nss3, "PR_Free"); |
90 | | if (freePtr) { |
91 | | return PR_NewThreadPrivateIndex(&error_stack_index, freePtr); |
92 | | } |
93 | | } |
94 | | return PR_NewThreadPrivateIndex(&error_stack_index, PR_Free); |
95 | | #else |
96 | 2 | return PR_NewThreadPrivateIndex(&error_stack_index, PR_Free); |
97 | 2 | #endif |
98 | 2 | } |
99 | | |
100 | | /* |
101 | | * error_get_my_stack |
102 | | * |
103 | | * This routine returns the calling thread's error stack, creating |
104 | | * it if necessary. It may return NULL upon error, which implicitly |
105 | | * means that it ran out of memory. |
106 | | */ |
107 | | |
108 | | static error_stack * |
109 | | error_get_my_stack(void) |
110 | 4 | { |
111 | 4 | PRStatus st; |
112 | 4 | error_stack *rv; |
113 | 4 | PRUintn new_size; |
114 | 4 | PRUint32 new_bytes; |
115 | 4 | error_stack *new_stack; |
116 | | |
117 | 4 | if (INVALID_TPD_INDEX == error_stack_index) { |
118 | 2 | st = PR_CallOnce(&error_call_once, error_once_function); |
119 | 2 | if (PR_SUCCESS != st) { |
120 | 0 | return (error_stack *)NULL; |
121 | 0 | } |
122 | 2 | } |
123 | | |
124 | 4 | rv = (error_stack *)PR_GetThreadPrivate(error_stack_index); |
125 | 4 | if ((error_stack *)NULL == rv) { |
126 | | /* Doesn't exist; create one */ |
127 | 2 | new_size = 16; |
128 | 2 | } else if (rv->header.count == rv->header.space && |
129 | 2 | rv->header.count < NSS_MAX_ERROR_STACK_COUNT) { |
130 | | /* Too small, expand it */ |
131 | 0 | new_size = PR_MIN(rv->header.space * 2, NSS_MAX_ERROR_STACK_COUNT); |
132 | 2 | } else { |
133 | | /* Okay, return it */ |
134 | 2 | return rv; |
135 | 2 | } |
136 | | |
137 | 2 | new_bytes = (new_size * sizeof(PRInt32)) + sizeof(error_stack); |
138 | | /* Use NSPR's calloc/realloc, not NSS's, to avoid loops! */ |
139 | 2 | new_stack = PR_Calloc(1, new_bytes); |
140 | | |
141 | 2 | if ((error_stack *)NULL != new_stack) { |
142 | 2 | if ((error_stack *)NULL != rv) { |
143 | 0 | (void)nsslibc_memcpy(new_stack, rv, rv->header.space); |
144 | 0 | } |
145 | 2 | new_stack->header.space = new_size; |
146 | 2 | } |
147 | | |
148 | | /* Set the value, whether or not the allocation worked */ |
149 | 2 | PR_SetThreadPrivate(error_stack_index, new_stack); |
150 | 2 | return new_stack; |
151 | 4 | } |
152 | | |
153 | | /* |
154 | | * The error stack |
155 | | * |
156 | | * The public methods relating to the error stack are: |
157 | | * |
158 | | * NSS_GetError |
159 | | * NSS_GetErrorStack |
160 | | * |
161 | | * The nonpublic methods relating to the error stack are: |
162 | | * |
163 | | * nss_SetError |
164 | | * nss_ClearErrorStack |
165 | | * |
166 | | */ |
167 | | |
168 | | /* |
169 | | * NSS_GetError |
170 | | * |
171 | | * This routine returns the highest-level (most general) error set |
172 | | * by the most recent NSS library routine called by the same thread |
173 | | * calling this routine. |
174 | | * |
175 | | * This routine cannot fail. However, it may return zero, which |
176 | | * indicates that the previous NSS library call did not set an error. |
177 | | * |
178 | | * Return value: |
179 | | * 0 if no error has been set |
180 | | * A nonzero error number |
181 | | */ |
182 | | |
183 | | NSS_IMPLEMENT PRInt32 |
184 | | NSS_GetError(void) |
185 | 0 | { |
186 | 0 | error_stack *es = error_get_my_stack(); |
187 | |
|
188 | 0 | if ((error_stack *)NULL == es) { |
189 | 0 | return NSS_ERROR_NO_MEMORY; /* Good guess! */ |
190 | 0 | } |
191 | | |
192 | 0 | if (0 == es->header.count) { |
193 | 0 | return 0; |
194 | 0 | } |
195 | | |
196 | 0 | return es->stack[es->header.count - 1]; |
197 | 0 | } |
198 | | |
199 | | /* |
200 | | * NSS_GetErrorStack |
201 | | * |
202 | | * This routine returns a pointer to an array of integers, containing |
203 | | * the entire sequence or "stack" of errors set by the most recent NSS |
204 | | * library routine called by the same thread calling this routine. |
205 | | * NOTE: the caller DOES NOT OWN the memory pointed to by the return |
206 | | * value. The pointer will remain valid until the calling thread |
207 | | * calls another NSS routine. The lowest-level (most specific) error |
208 | | * is first in the array, and the highest-level is last. The array is |
209 | | * zero-terminated. This routine may return NULL upon error; this |
210 | | * indicates a low-memory situation. |
211 | | * |
212 | | * Return value: |
213 | | * NULL upon error, which is an implied NSS_ERROR_NO_MEMORY |
214 | | * A NON-caller-owned pointer to an array of integers |
215 | | */ |
216 | | |
217 | | NSS_IMPLEMENT PRInt32 * |
218 | | NSS_GetErrorStack(void) |
219 | 0 | { |
220 | 0 | error_stack *es = error_get_my_stack(); |
221 | |
|
222 | 0 | if ((error_stack *)NULL == es) { |
223 | 0 | return (PRInt32 *)NULL; |
224 | 0 | } |
225 | | |
226 | | /* Make sure it's terminated */ |
227 | 0 | es->stack[es->header.count] = 0; |
228 | |
|
229 | 0 | return es->stack; |
230 | 0 | } |
231 | | |
232 | | /* |
233 | | * nss_SetError |
234 | | * |
235 | | * This routine places a new error code on the top of the calling |
236 | | * thread's error stack. Calling this routine wiht an error code |
237 | | * of zero will clear the error stack. |
238 | | */ |
239 | | |
240 | | NSS_IMPLEMENT void |
241 | | nss_SetError(PRUint32 error) |
242 | 0 | { |
243 | 0 | error_stack *es; |
244 | |
|
245 | 0 | if (0 == error) { |
246 | 0 | nss_ClearErrorStack(); |
247 | 0 | return; |
248 | 0 | } |
249 | | |
250 | 0 | es = error_get_my_stack(); |
251 | 0 | if ((error_stack *)NULL == es) { |
252 | | /* Oh, well. */ |
253 | 0 | return; |
254 | 0 | } |
255 | | |
256 | 0 | if (es->header.count < es->header.space) { |
257 | 0 | es->stack[es->header.count++] = error; |
258 | 0 | } else { |
259 | 0 | memmove(es->stack, es->stack + 1, |
260 | 0 | (es->header.space - 1) * (sizeof es->stack[0])); |
261 | 0 | es->stack[es->header.space - 1] = error; |
262 | 0 | } |
263 | 0 | return; |
264 | 0 | } |
265 | | |
266 | | /* |
267 | | * nss_ClearErrorStack |
268 | | * |
269 | | * This routine clears the calling thread's error stack. |
270 | | */ |
271 | | |
272 | | NSS_IMPLEMENT void |
273 | | nss_ClearErrorStack(void) |
274 | 4 | { |
275 | 4 | error_stack *es = error_get_my_stack(); |
276 | 4 | if ((error_stack *)NULL == es) { |
277 | | /* Oh, well. */ |
278 | 0 | return; |
279 | 0 | } |
280 | | |
281 | 4 | es->header.count = 0; |
282 | 4 | es->stack[0] = 0; |
283 | 4 | return; |
284 | 4 | } |
285 | | |
286 | | /* |
287 | | * nss_DestroyErrorStack |
288 | | * |
289 | | * This routine frees the calling thread's error stack. |
290 | | */ |
291 | | |
292 | | NSS_IMPLEMENT void |
293 | | nss_DestroyErrorStack(void) |
294 | 0 | { |
295 | 0 | if (INVALID_TPD_INDEX != error_stack_index) { |
296 | 0 | PR_SetThreadPrivate(error_stack_index, NULL); |
297 | 0 | error_stack_index = INVALID_TPD_INDEX; |
298 | 0 | error_call_once = error_call_again; /* allow to init again */ |
299 | 0 | } |
300 | 0 | return; |
301 | 0 | } |