security/nss/lib/base/tracker.c

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial