Bug Summary

File:root/firefox-clang/security/nss/lib/base/tracker.c
Warning:line 29, column 39
Subtraction of a probably non-null pointer and a null pointer may result in undefined behavior

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -O2 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name tracker.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -relaxed-aliasing -ffp-contract=off -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/security/nss/lib/base/base_nssb -fcoverage-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/security/nss/lib/base/base_nssb -resource-dir /usr/lib/llvm-22/lib/clang/22 -include /root/firefox-clang/obj-x86_64-pc-linux-gnu/mozilla-config.h -U _FORTIFY_SOURCE -D _FORTIFY_SOURCE=2 -D _GLIBCXX_ASSERTIONS -D DEBUG -D NSS_FIPS_DISABLED -D NSS_NO_INIT_SUPPORT -D NSS_X86_OR_X64 -D NSS_X64 -D NSS_USE_64 -D USE_UTIL_DIRECTLY -D NO_NSPR_10_SUPPORT -D SSL_DISABLE_DEPRECATED_CIPHER_SUITE_NAMES -D LINUX2_1 -D LINUX -D linux -D _DEFAULT_SOURCE -D _BSD_SOURCE -D _POSIX_SOURCE -D SDB_MEASURE_USE_TEMP_DIR -D HAVE_STRERROR -D XP_UNIX -D _REENTRANT -D NSS_DISABLE_DBM -D NSS_DISABLE_LIBPKIX -I /root/firefox-clang/security/nss/lib/base -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/security/nss/lib/base/base_nssb -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nspr -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/private/nss -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nss -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include -D MOZILLA_CLIENT -internal-isystem /usr/lib/llvm-22/lib/clang/22/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/15/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wno-error=tautological-type-limit-compare -Wno-range-loop-analysis -Wno-error=deprecated-declarations -Wno-error=array-bounds -Wno-error=free-nonheap-object -Wno-error=atomic-alignment -Wno-error=deprecated-builtins -Wno-psabi -Wno-error=builtin-macro-redefined -Wno-unknown-warning-option -Wno-character-conversion -ferror-limit 19 -fstrict-flex-arrays=1 -stack-protector 2 -fstack-clash-protection -ftrivial-auto-var-init=pattern -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -vectorize-loops -vectorize-slp -analyzer-checker optin.performance.Padding -analyzer-output=html -analyzer-config stable-report-filename=true -faddrsig -fdwarf2-cfi-asm -o /tmp/scan-build-2026-01-01-094441-2823109-1 -x c /root/firefox-clang/security/nss/lib/base/tracker.c
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 DEBUG1
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
26static PLHashNumber PR_CALLBACK
27identity_hash(const void *key)
28{
29 return (PLHashNumber)((char *)key - (char *)NULL((void*)0));
Subtraction of a probably non-null pointer and a null pointer may result in undefined behavior
30}
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
40static PRStatus
41trackerOnceFunc(void *arg)
42{
43 nssPointerTracker *tracker = (nssPointerTracker *)arg;
44
45 tracker->lock = PZ_NewLock(nssILockOther)PR_NewLock();
46 if ((PZLockPRLock *)NULL((void*)0) == tracker->lock) {
47 return PR_FAILURE;
48 }
49
50 tracker->table =
51 PL_NewHashTable(0, identity_hash, PL_CompareValues, PL_CompareValues,
52 (PLHashAllocOps *)NULL((void*)0), (void *)NULL((void*)0));
53 if ((PLHashTable *)NULL((void*)0) == tracker->table) {
54 PZ_DestroyLock(tracker->lock)PR_DestroyLock((tracker->lock));
55 tracker->lock = (PZLockPRLock *)NULL((void*)0);
56 return PR_FAILURE;
57 }
58
59 return PR_SUCCESS;
60}
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
83NSS_IMPLEMENT PRStatus
84nssPointerTracker_initialize(nssPointerTracker *tracker)
85{
86 PRStatus rv = PR_CallOnceWithArg(&tracker->once, trackerOnceFunc, tracker);
87 if (PR_SUCCESS != rv) {
88 nss_SetError(NSS_ERROR_NO_MEMORY);
89 }
90
91 return rv;
92}
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
104static PRIntn PR_CALLBACK
105count_entries(PLHashEntry *he, PRIntn index, void *arg)
106{
107 return HT_ENUMERATE_NEXT0;
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
118static 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
145NSS_IMPLEMENT PRStatus
146nssPointerTracker_finalize(nssPointerTracker *tracker)
147{
148 PZLockPRLock *lock;
149
150 if ((nssPointerTracker *)NULL((void*)0) == tracker) {
151 nss_SetError(NSS_ERROR_INVALID_POINTER);
152 return PR_FAILURE;
153 }
154
155 if ((PZLockPRLock *)NULL((void*)0) == tracker->lock) {
156 nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED);
157 return PR_FAILURE;
158 }
159
160 lock = tracker->lock;
161 PZ_Lock(lock)PR_Lock((lock));
162
163 if ((PLHashTable *)NULL((void*)0) == tracker->table) {
164 PZ_Unlock(lock)PR_Unlock((lock));
165 nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED);
166 return PR_FAILURE;
167 }
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((void*)0));
176
177 if (0 != count) {
178 PZ_Unlock(lock)PR_Unlock((lock));
179 nss_SetError(NSS_ERROR_TRACKER_NOT_EMPTY);
180 return PR_FAILURE;
181 }
182#endif /* DONT_DESTROY_EMPTY_TABLES */
183
184 PL_HashTableDestroy(tracker->table);
185 /* memset(tracker, 0, sizeof(nssPointerTracker)); */
186 tracker->once = zero_once;
187 tracker->lock = (PZLockPRLock *)NULL((void*)0);
188 tracker->table = (PLHashTable *)NULL((void*)0);
189
190 PZ_Unlock(lock)PR_Unlock((lock));
191 PZ_DestroyLock(lock)PR_DestroyLock((lock));
192
193 return PR_SUCCESS;
194}
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
219NSS_IMPLEMENT PRStatus
220nssPointerTracker_add(nssPointerTracker *tracker, const void *pointer)
221{
222 void *check;
223 PLHashEntry *entry;
224
225 if ((nssPointerTracker *)NULL((void*)0) == tracker) {
226 nss_SetError(NSS_ERROR_INVALID_POINTER);
227 return PR_FAILURE;
228 }
229
230 if ((PZLockPRLock *)NULL((void*)0) == tracker->lock) {
231 nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED);
232 return PR_FAILURE;
233 }
234
235 PZ_Lock(tracker->lock)PR_Lock((tracker->lock));
236
237 if ((PLHashTable *)NULL((void*)0) == tracker->table) {
238 PZ_Unlock(tracker->lock)PR_Unlock((tracker->lock));
239 nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED);
240 return PR_FAILURE;
241 }
242
243 check = PL_HashTableLookup(tracker->table, pointer);
244 if ((void *)NULL((void*)0) != check) {
245 PZ_Unlock(tracker->lock)PR_Unlock((tracker->lock));
246 nss_SetError(NSS_ERROR_DUPLICATE_POINTER);
247 return PR_FAILURE;
248 }
249
250 entry = PL_HashTableAdd(tracker->table, pointer, (void *)pointer);
251
252 PZ_Unlock(tracker->lock)PR_Unlock((tracker->lock));
253
254 if ((PLHashEntry *)NULL((void*)0) == entry) {
255 nss_SetError(NSS_ERROR_NO_MEMORY);
256 return PR_FAILURE;
257 }
258
259 return PR_SUCCESS;
260}
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
285NSS_IMPLEMENT PRStatus
286nssPointerTracker_remove(nssPointerTracker *tracker, const void *pointer)
287{
288 PRBool registered;
289
290 if ((nssPointerTracker *)NULL((void*)0) == tracker) {
291 nss_SetError(NSS_ERROR_INVALID_POINTER);
292 return PR_FAILURE;
293 }
294
295 if ((PZLockPRLock *)NULL((void*)0) == tracker->lock) {
296 nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED);
297 return PR_FAILURE;
298 }
299
300 PZ_Lock(tracker->lock)PR_Lock((tracker->lock));
301
302 if ((PLHashTable *)NULL((void*)0) == tracker->table) {
303 PZ_Unlock(tracker->lock)PR_Unlock((tracker->lock));
304 nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED);
305 return PR_FAILURE;
306 }
307
308 registered = PL_HashTableRemove(tracker->table, pointer);
309 PZ_Unlock(tracker->lock)PR_Unlock((tracker->lock));
310
311 if (!registered) {
312 nss_SetError(NSS_ERROR_POINTER_NOT_REGISTERED);
313 return PR_FAILURE;
314 }
315
316 return PR_SUCCESS;
317}
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
344NSS_IMPLEMENT PRStatus
345nssPointerTracker_verify(nssPointerTracker *tracker, const void *pointer)
346{
347 void *check;
348
349 if ((nssPointerTracker *)NULL((void*)0) == tracker) {
350 nss_SetError(NSS_ERROR_INVALID_POINTER);
351 return PR_FAILURE;
352 }
353
354 if ((PZLockPRLock *)NULL((void*)0) == tracker->lock) {
355 nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED);
356 return PR_FAILURE;
357 }
358
359 PZ_Lock(tracker->lock)PR_Lock((tracker->lock));
360
361 if ((PLHashTable *)NULL((void*)0) == tracker->table) {
362 PZ_Unlock(tracker->lock)PR_Unlock((tracker->lock));
363 nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED);
364 return PR_FAILURE;
365 }
366
367 check = PL_HashTableLookup(tracker->table, pointer);
368 PZ_Unlock(tracker->lock)PR_Unlock((tracker->lock));
369
370 if ((void *)NULL((void*)0) == check) {
371 nss_SetError(NSS_ERROR_POINTER_NOT_REGISTERED);
372 return PR_FAILURE;
373 }
374
375 return PR_SUCCESS;
376}
377
378#endif /* DEBUG */