Coverage Report

Created: 2024-11-21 07:03

/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
}