Coverage Report

Created: 2025-07-01 06:25

/src/nss/lib/base/tracker.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
 * tracker.c
7
 *
8
 * This file contains the code used by the pointer-tracking calls used
9
 * in the debug builds to catch bad pointers.  The entire contents are
10
 * only available in debug builds (both internal and external builds).
11
 */
12
13
#ifndef BASE_H
14
#include "base.h"
15
#endif /* BASE_H */
16
17
#ifdef DEBUG
18
/*
19
 * identity_hash
20
 *
21
 * This static callback is a PLHashFunction as defined in plhash.h
22
 * It merely returns the value of the object pointer as its hash.
23
 * There are no possible errors.
24
 */
25
26
static PLHashNumber PR_CALLBACK
27
identity_hash(const void *key)
28
0
{
29
0
    return (PLHashNumber)((char *)key - (char *)NULL);
30
0
}
31
32
/*
33
 * trackerOnceFunc
34
 *
35
 * This function is called once, using the nssCallOnce function above.
36
 * It creates a new pointer tracker object; initialising its hash
37
 * table and protective lock.
38
 */
39
40
static PRStatus
41
trackerOnceFunc(void *arg)
42
0
{
43
0
    nssPointerTracker *tracker = (nssPointerTracker *)arg;
44
45
0
    tracker->lock = PZ_NewLock(nssILockOther);
46
0
    if ((PZLock *)NULL == tracker->lock) {
47
0
        return PR_FAILURE;
48
0
    }
49
50
0
    tracker->table =
51
0
        PL_NewHashTable(0, identity_hash, PL_CompareValues, PL_CompareValues,
52
0
                        (PLHashAllocOps *)NULL, (void *)NULL);
53
0
    if ((PLHashTable *)NULL == tracker->table) {
54
0
        PZ_DestroyLock(tracker->lock);
55
0
        tracker->lock = (PZLock *)NULL;
56
0
        return PR_FAILURE;
57
0
    }
58
59
0
    return PR_SUCCESS;
60
0
}
61
62
/*
63
 * nssPointerTracker_initialize
64
 *
65
 * This method is only present in debug builds.
66
 *
67
 * This routine initializes an nssPointerTracker object.  Note that
68
 * the object must have been declared *static* to guarantee that it
69
 * is in a zeroed state initially.  This routine is idempotent, and
70
 * may even be safely called by multiple threads simultaneously with
71
 * the same argument.  This routine returns a PRStatus value; if
72
 * successful, it will return PR_SUCCESS.  On failure it will set an
73
 * error on the error stack and return PR_FAILURE.
74
 *
75
 * The error may be one of the following values:
76
 *  NSS_ERROR_NO_MEMORY
77
 *
78
 * Return value:
79
 *  PR_SUCCESS
80
 *  PR_FAILURE
81
 */
82
83
NSS_IMPLEMENT PRStatus
84
nssPointerTracker_initialize(nssPointerTracker *tracker)
85
0
{
86
0
    PRStatus rv = PR_CallOnceWithArg(&tracker->once, trackerOnceFunc, tracker);
87
0
    if (PR_SUCCESS != rv) {
88
0
        nss_SetError(NSS_ERROR_NO_MEMORY);
89
0
    }
90
91
0
    return rv;
92
0
}
93
94
#ifdef DONT_DESTROY_EMPTY_TABLES
95
/* See same #ifdef below */
96
/*
97
 * count_entries
98
 *
99
 * This static routine is a PLHashEnumerator, as defined in plhash.h.
100
 * It merely causes the enumeration function to count the number of
101
 * entries.
102
 */
103
104
static PRIntn PR_CALLBACK
105
count_entries(PLHashEntry *he, PRIntn index, void *arg)
106
{
107
    return HT_ENUMERATE_NEXT;
108
}
109
#endif /* DONT_DESTROY_EMPTY_TABLES */
110
111
/*
112
 * zero_once
113
 *
114
 * This is a guaranteed zeroed once block.  It's used to help clear
115
 * the tracker.
116
 */
117
118
static const PRCallOnceType zero_once;
119
120
/*
121
 * nssPointerTracker_finalize
122
 *
123
 * This method is only present in debug builds.
124
 *
125
 * This routine returns the nssPointerTracker object to the pre-
126
 * initialized state, releasing all resources used by the object.
127
 * It will *NOT* destroy the objects being tracked by the pointer
128
 * (should any remain), and therefore cannot be used to "sweep up"
129
 * remaining objects.  This routine returns a PRStatus value; if
130
 * successful, it will return PR_SUCCES.  On failure it will set an
131
 * error on the error stack and return PR_FAILURE.  If any objects
132
 * remain in the tracker when it is finalized, that will be treated
133
 * as an error.
134
 *
135
 * The error may be one of the following values:
136
 *  NSS_ERROR_INVALID_POINTER
137
 *  NSS_ERROR_TRACKER_NOT_INITIALIZED
138
 *  NSS_ERROR_TRACKER_NOT_EMPTY
139
 *
140
 * Return value:
141
 *  PR_SUCCESS
142
 *  PR_FAILURE
143
 */
144
145
NSS_IMPLEMENT PRStatus
146
nssPointerTracker_finalize(nssPointerTracker *tracker)
147
0
{
148
0
    PZLock *lock;
149
150
0
    if ((nssPointerTracker *)NULL == tracker) {
151
0
        nss_SetError(NSS_ERROR_INVALID_POINTER);
152
0
        return PR_FAILURE;
153
0
    }
154
155
0
    if ((PZLock *)NULL == tracker->lock) {
156
0
        nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED);
157
0
        return PR_FAILURE;
158
0
    }
159
160
0
    lock = tracker->lock;
161
0
    PZ_Lock(lock);
162
163
0
    if ((PLHashTable *)NULL == tracker->table) {
164
0
        PZ_Unlock(lock);
165
0
        nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED);
166
0
        return PR_FAILURE;
167
0
    }
168
169
#ifdef DONT_DESTROY_EMPTY_TABLES
170
    /*
171
     * I changed my mind; I think we don't want this after all.
172
     * Comments?
173
     */
174
    count = PL_HashTableEnumerateEntries(tracker->table, count_entries,
175
                                         (void *)NULL);
176
177
    if (0 != count) {
178
        PZ_Unlock(lock);
179
        nss_SetError(NSS_ERROR_TRACKER_NOT_EMPTY);
180
        return PR_FAILURE;
181
    }
182
#endif /* DONT_DESTROY_EMPTY_TABLES */
183
184
0
    PL_HashTableDestroy(tracker->table);
185
    /* memset(tracker, 0, sizeof(nssPointerTracker)); */
186
0
    tracker->once = zero_once;
187
0
    tracker->lock = (PZLock *)NULL;
188
0
    tracker->table = (PLHashTable *)NULL;
189
190
0
    PZ_Unlock(lock);
191
0
    PZ_DestroyLock(lock);
192
193
0
    return PR_SUCCESS;
194
0
}
195
196
/*
197
 * nssPointerTracker_add
198
 *
199
 * This method is only present in debug builds.
200
 *
201
 * This routine adds the specified pointer to the nssPointerTracker
202
 * object.  It should be called in constructor objects to register
203
 * new valid objects.  The nssPointerTracker is threadsafe, but this
204
 * call is not idempotent.  This routine returns a PRStatus value;
205
 * if successful it will return PR_SUCCESS.  On failure it will set
206
 * an error on the error stack and return PR_FAILURE.
207
 *
208
 * The error may be one of the following values:
209
 *  NSS_ERROR_INVALID_POINTER
210
 *  NSS_ERROR_NO_MEMORY
211
 *  NSS_ERROR_TRACKER_NOT_INITIALIZED
212
 *  NSS_ERROR_DUPLICATE_POINTER
213
 *
214
 * Return value:
215
 *  PR_SUCCESS
216
 *  PR_FAILURE
217
 */
218
219
NSS_IMPLEMENT PRStatus
220
nssPointerTracker_add(nssPointerTracker *tracker, const void *pointer)
221
0
{
222
0
    void *check;
223
0
    PLHashEntry *entry;
224
225
0
    if ((nssPointerTracker *)NULL == tracker) {
226
0
        nss_SetError(NSS_ERROR_INVALID_POINTER);
227
0
        return PR_FAILURE;
228
0
    }
229
230
0
    if ((PZLock *)NULL == tracker->lock) {
231
0
        nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED);
232
0
        return PR_FAILURE;
233
0
    }
234
235
0
    PZ_Lock(tracker->lock);
236
237
0
    if ((PLHashTable *)NULL == tracker->table) {
238
0
        PZ_Unlock(tracker->lock);
239
0
        nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED);
240
0
        return PR_FAILURE;
241
0
    }
242
243
0
    check = PL_HashTableLookup(tracker->table, pointer);
244
0
    if ((void *)NULL != check) {
245
0
        PZ_Unlock(tracker->lock);
246
0
        nss_SetError(NSS_ERROR_DUPLICATE_POINTER);
247
0
        return PR_FAILURE;
248
0
    }
249
250
0
    entry = PL_HashTableAdd(tracker->table, pointer, (void *)pointer);
251
252
0
    PZ_Unlock(tracker->lock);
253
254
0
    if ((PLHashEntry *)NULL == entry) {
255
0
        nss_SetError(NSS_ERROR_NO_MEMORY);
256
0
        return PR_FAILURE;
257
0
    }
258
259
0
    return PR_SUCCESS;
260
0
}
261
262
/*
263
 * nssPointerTracker_remove
264
 *
265
 * This method is only present in debug builds.
266
 *
267
 * This routine removes the specified pointer from the
268
 * nssPointerTracker object.  It does not call any destructor for the
269
 * object; rather, this should be called from the object's destructor.
270
 * The nssPointerTracker is threadsafe, but this call is not
271
 * idempotent.  This routine returns a PRStatus value; if successful
272
 * it will return PR_SUCCESS.  On failure it will set an error on the
273
 * error stack and return PR_FAILURE.
274
 *
275
 * The error may be one of the following values:
276
 *  NSS_ERROR_INVALID_POINTER
277
 *  NSS_ERROR_TRACKER_NOT_INITIALIZED
278
 *  NSS_ERROR_POINTER_NOT_REGISTERED
279
 *
280
 * Return value:
281
 *  PR_SUCCESS
282
 *  PR_FAILURE
283
 */
284
285
NSS_IMPLEMENT PRStatus
286
nssPointerTracker_remove(nssPointerTracker *tracker, const void *pointer)
287
0
{
288
0
    PRBool registered;
289
290
0
    if ((nssPointerTracker *)NULL == tracker) {
291
0
        nss_SetError(NSS_ERROR_INVALID_POINTER);
292
0
        return PR_FAILURE;
293
0
    }
294
295
0
    if ((PZLock *)NULL == tracker->lock) {
296
0
        nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED);
297
0
        return PR_FAILURE;
298
0
    }
299
300
0
    PZ_Lock(tracker->lock);
301
302
0
    if ((PLHashTable *)NULL == tracker->table) {
303
0
        PZ_Unlock(tracker->lock);
304
0
        nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED);
305
0
        return PR_FAILURE;
306
0
    }
307
308
0
    registered = PL_HashTableRemove(tracker->table, pointer);
309
0
    PZ_Unlock(tracker->lock);
310
311
0
    if (!registered) {
312
0
        nss_SetError(NSS_ERROR_POINTER_NOT_REGISTERED);
313
0
        return PR_FAILURE;
314
0
    }
315
316
0
    return PR_SUCCESS;
317
0
}
318
319
/*
320
 * nssPointerTracker_verify
321
 *
322
 * This method is only present in debug builds.
323
 *
324
 * This routine verifies that the specified pointer has been registered
325
 * with the nssPointerTracker object.  The nssPointerTracker object is
326
 * threadsafe, and this call may be safely called from multiple threads
327
 * simultaneously with the same arguments.  This routine returns a
328
 * PRStatus value; if the pointer is registered this will return
329
 * PR_SUCCESS.  Otherwise it will set an error on the error stack and
330
 * return PR_FAILURE.  Although the error is suitable for leaving on
331
 * the stack, callers may wish to augment the information available by
332
 * placing a more type-specific error on the stack.
333
 *
334
 * The error may be one of the following values:
335
 *  NSS_ERROR_INVALID_POINTER
336
 *  NSS_ERROR_TRACKER_NOT_INITIALIZED
337
 *  NSS_ERROR_POINTER_NOT_REGISTERED
338
 *
339
 * Return value:
340
 *  PR_SUCCESS
341
 *  PR_FAILRUE
342
 */
343
344
NSS_IMPLEMENT PRStatus
345
nssPointerTracker_verify(nssPointerTracker *tracker, const void *pointer)
346
0
{
347
0
    void *check;
348
349
0
    if ((nssPointerTracker *)NULL == tracker) {
350
0
        nss_SetError(NSS_ERROR_INVALID_POINTER);
351
0
        return PR_FAILURE;
352
0
    }
353
354
0
    if ((PZLock *)NULL == tracker->lock) {
355
0
        nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED);
356
0
        return PR_FAILURE;
357
0
    }
358
359
0
    PZ_Lock(tracker->lock);
360
361
0
    if ((PLHashTable *)NULL == tracker->table) {
362
0
        PZ_Unlock(tracker->lock);
363
0
        nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED);
364
0
        return PR_FAILURE;
365
0
    }
366
367
0
    check = PL_HashTableLookup(tracker->table, pointer);
368
0
    PZ_Unlock(tracker->lock);
369
370
0
    if ((void *)NULL == check) {
371
0
        nss_SetError(NSS_ERROR_POINTER_NOT_REGISTERED);
372
0
        return PR_FAILURE;
373
0
    }
374
375
0
    return PR_SUCCESS;
376
0
}
377
378
#endif /* DEBUG */