1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/security/nss/lib/base/tracker.c Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,411 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +/* 1.9 + * tracker.c 1.10 + * 1.11 + * This file contains the code used by the pointer-tracking calls used 1.12 + * in the debug builds to catch bad pointers. The entire contents are 1.13 + * only available in debug builds (both internal and external builds). 1.14 + */ 1.15 + 1.16 +#ifndef BASE_H 1.17 +#include "base.h" 1.18 +#endif /* BASE_H */ 1.19 + 1.20 +#ifdef DEBUG 1.21 +/* 1.22 + * identity_hash 1.23 + * 1.24 + * This static callback is a PLHashFunction as defined in plhash.h 1.25 + * It merely returns the value of the object pointer as its hash. 1.26 + * There are no possible errors. 1.27 + */ 1.28 + 1.29 +static PLHashNumber PR_CALLBACK 1.30 +identity_hash 1.31 +( 1.32 + const void *key 1.33 +) 1.34 +{ 1.35 + return (PLHashNumber)key; 1.36 +} 1.37 + 1.38 +/* 1.39 + * trackerOnceFunc 1.40 + * 1.41 + * This function is called once, using the nssCallOnce function above. 1.42 + * It creates a new pointer tracker object; initialising its hash 1.43 + * table and protective lock. 1.44 + */ 1.45 + 1.46 +static PRStatus 1.47 +trackerOnceFunc 1.48 +( 1.49 + void *arg 1.50 +) 1.51 +{ 1.52 + nssPointerTracker *tracker = (nssPointerTracker *)arg; 1.53 + 1.54 + tracker->lock = PZ_NewLock(nssILockOther); 1.55 + if( (PZLock *)NULL == tracker->lock ) { 1.56 + return PR_FAILURE; 1.57 + } 1.58 + 1.59 + tracker->table = PL_NewHashTable(0, 1.60 + identity_hash, 1.61 + PL_CompareValues, 1.62 + PL_CompareValues, 1.63 + (PLHashAllocOps *)NULL, 1.64 + (void *)NULL); 1.65 + if( (PLHashTable *)NULL == tracker->table ) { 1.66 + PZ_DestroyLock(tracker->lock); 1.67 + tracker->lock = (PZLock *)NULL; 1.68 + return PR_FAILURE; 1.69 + } 1.70 + 1.71 + return PR_SUCCESS; 1.72 +} 1.73 + 1.74 +/* 1.75 + * nssPointerTracker_initialize 1.76 + * 1.77 + * This method is only present in debug builds. 1.78 + * 1.79 + * This routine initializes an nssPointerTracker object. Note that 1.80 + * the object must have been declared *static* to guarantee that it 1.81 + * is in a zeroed state initially. This routine is idempotent, and 1.82 + * may even be safely called by multiple threads simultaneously with 1.83 + * the same argument. This routine returns a PRStatus value; if 1.84 + * successful, it will return PR_SUCCESS. On failure it will set an 1.85 + * error on the error stack and return PR_FAILURE. 1.86 + * 1.87 + * The error may be one of the following values: 1.88 + * NSS_ERROR_NO_MEMORY 1.89 + * 1.90 + * Return value: 1.91 + * PR_SUCCESS 1.92 + * PR_FAILURE 1.93 + */ 1.94 + 1.95 +NSS_IMPLEMENT PRStatus 1.96 +nssPointerTracker_initialize 1.97 +( 1.98 + nssPointerTracker *tracker 1.99 +) 1.100 +{ 1.101 + PRStatus rv = PR_CallOnceWithArg(&tracker->once, trackerOnceFunc, tracker); 1.102 + if( PR_SUCCESS != rv ) { 1.103 + nss_SetError(NSS_ERROR_NO_MEMORY); 1.104 + } 1.105 + 1.106 + return rv; 1.107 +} 1.108 + 1.109 +#ifdef DONT_DESTROY_EMPTY_TABLES 1.110 +/* See same #ifdef below */ 1.111 +/* 1.112 + * count_entries 1.113 + * 1.114 + * This static routine is a PLHashEnumerator, as defined in plhash.h. 1.115 + * It merely causes the enumeration function to count the number of 1.116 + * entries. 1.117 + */ 1.118 + 1.119 +static PRIntn PR_CALLBACK 1.120 +count_entries 1.121 +( 1.122 + PLHashEntry *he, 1.123 + PRIntn index, 1.124 + void *arg 1.125 +) 1.126 +{ 1.127 + return HT_ENUMERATE_NEXT; 1.128 +} 1.129 +#endif /* DONT_DESTROY_EMPTY_TABLES */ 1.130 + 1.131 +/* 1.132 + * zero_once 1.133 + * 1.134 + * This is a guaranteed zeroed once block. It's used to help clear 1.135 + * the tracker. 1.136 + */ 1.137 + 1.138 +static const PRCallOnceType zero_once; 1.139 + 1.140 +/* 1.141 + * nssPointerTracker_finalize 1.142 + * 1.143 + * This method is only present in debug builds. 1.144 + * 1.145 + * This routine returns the nssPointerTracker object to the pre- 1.146 + * initialized state, releasing all resources used by the object. 1.147 + * It will *NOT* destroy the objects being tracked by the pointer 1.148 + * (should any remain), and therefore cannot be used to "sweep up" 1.149 + * remaining objects. This routine returns a PRStatus value; if 1.150 + * successful, it will return PR_SUCCES. On failure it will set an 1.151 + * error on the error stack and return PR_FAILURE. If any objects 1.152 + * remain in the tracker when it is finalized, that will be treated 1.153 + * as an error. 1.154 + * 1.155 + * The error may be one of the following values: 1.156 + * NSS_ERROR_INVALID_POINTER 1.157 + * NSS_ERROR_TRACKER_NOT_INITIALIZED 1.158 + * NSS_ERROR_TRACKER_NOT_EMPTY 1.159 + * 1.160 + * Return value: 1.161 + * PR_SUCCESS 1.162 + * PR_FAILURE 1.163 + */ 1.164 + 1.165 +NSS_IMPLEMENT PRStatus 1.166 +nssPointerTracker_finalize 1.167 +( 1.168 + nssPointerTracker *tracker 1.169 +) 1.170 +{ 1.171 + PZLock *lock; 1.172 + 1.173 + if( (nssPointerTracker *)NULL == tracker ) { 1.174 + nss_SetError(NSS_ERROR_INVALID_POINTER); 1.175 + return PR_FAILURE; 1.176 + } 1.177 + 1.178 + if( (PZLock *)NULL == tracker->lock ) { 1.179 + nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED); 1.180 + return PR_FAILURE; 1.181 + } 1.182 + 1.183 + lock = tracker->lock; 1.184 + PZ_Lock(lock); 1.185 + 1.186 + if( (PLHashTable *)NULL == tracker->table ) { 1.187 + PZ_Unlock(lock); 1.188 + nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED); 1.189 + return PR_FAILURE; 1.190 + } 1.191 + 1.192 +#ifdef DONT_DESTROY_EMPTY_TABLES 1.193 + /* 1.194 + * I changed my mind; I think we don't want this after all. 1.195 + * Comments? 1.196 + */ 1.197 + count = PL_HashTableEnumerateEntries(tracker->table, 1.198 + count_entries, 1.199 + (void *)NULL); 1.200 + 1.201 + if( 0 != count ) { 1.202 + PZ_Unlock(lock); 1.203 + nss_SetError(NSS_ERROR_TRACKER_NOT_EMPTY); 1.204 + return PR_FAILURE; 1.205 + } 1.206 +#endif /* DONT_DESTROY_EMPTY_TABLES */ 1.207 + 1.208 + PL_HashTableDestroy(tracker->table); 1.209 + /* memset(tracker, 0, sizeof(nssPointerTracker)); */ 1.210 + tracker->once = zero_once; 1.211 + tracker->lock = (PZLock *)NULL; 1.212 + tracker->table = (PLHashTable *)NULL; 1.213 + 1.214 + PZ_Unlock(lock); 1.215 + PZ_DestroyLock(lock); 1.216 + 1.217 + return PR_SUCCESS; 1.218 +} 1.219 + 1.220 +/* 1.221 + * nssPointerTracker_add 1.222 + * 1.223 + * This method is only present in debug builds. 1.224 + * 1.225 + * This routine adds the specified pointer to the nssPointerTracker 1.226 + * object. It should be called in constructor objects to register 1.227 + * new valid objects. The nssPointerTracker is threadsafe, but this 1.228 + * call is not idempotent. This routine returns a PRStatus value; 1.229 + * if successful it will return PR_SUCCESS. On failure it will set 1.230 + * an error on the error stack and return PR_FAILURE. 1.231 + * 1.232 + * The error may be one of the following values: 1.233 + * NSS_ERROR_INVALID_POINTER 1.234 + * NSS_ERROR_NO_MEMORY 1.235 + * NSS_ERROR_TRACKER_NOT_INITIALIZED 1.236 + * NSS_ERROR_DUPLICATE_POINTER 1.237 + * 1.238 + * Return value: 1.239 + * PR_SUCCESS 1.240 + * PR_FAILURE 1.241 + */ 1.242 + 1.243 +NSS_IMPLEMENT PRStatus 1.244 +nssPointerTracker_add 1.245 +( 1.246 + nssPointerTracker *tracker, 1.247 + const void *pointer 1.248 +) 1.249 +{ 1.250 + void *check; 1.251 + PLHashEntry *entry; 1.252 + 1.253 + if( (nssPointerTracker *)NULL == tracker ) { 1.254 + nss_SetError(NSS_ERROR_INVALID_POINTER); 1.255 + return PR_FAILURE; 1.256 + } 1.257 + 1.258 + if( (PZLock *)NULL == tracker->lock ) { 1.259 + nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED); 1.260 + return PR_FAILURE; 1.261 + } 1.262 + 1.263 + PZ_Lock(tracker->lock); 1.264 + 1.265 + if( (PLHashTable *)NULL == tracker->table ) { 1.266 + PZ_Unlock(tracker->lock); 1.267 + nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED); 1.268 + return PR_FAILURE; 1.269 + } 1.270 + 1.271 + check = PL_HashTableLookup(tracker->table, pointer); 1.272 + if( (void *)NULL != check ) { 1.273 + PZ_Unlock(tracker->lock); 1.274 + nss_SetError(NSS_ERROR_DUPLICATE_POINTER); 1.275 + return PR_FAILURE; 1.276 + } 1.277 + 1.278 + entry = PL_HashTableAdd(tracker->table, pointer, (void *)pointer); 1.279 + 1.280 + PZ_Unlock(tracker->lock); 1.281 + 1.282 + if( (PLHashEntry *)NULL == entry ) { 1.283 + nss_SetError(NSS_ERROR_NO_MEMORY); 1.284 + return PR_FAILURE; 1.285 + } 1.286 + 1.287 + return PR_SUCCESS; 1.288 +} 1.289 + 1.290 +/* 1.291 + * nssPointerTracker_remove 1.292 + * 1.293 + * This method is only present in debug builds. 1.294 + * 1.295 + * This routine removes the specified pointer from the 1.296 + * nssPointerTracker object. It does not call any destructor for the 1.297 + * object; rather, this should be called from the object's destructor. 1.298 + * The nssPointerTracker is threadsafe, but this call is not 1.299 + * idempotent. This routine returns a PRStatus value; if successful 1.300 + * it will return PR_SUCCESS. On failure it will set an error on the 1.301 + * error stack and return PR_FAILURE. 1.302 + * 1.303 + * The error may be one of the following values: 1.304 + * NSS_ERROR_INVALID_POINTER 1.305 + * NSS_ERROR_TRACKER_NOT_INITIALIZED 1.306 + * NSS_ERROR_POINTER_NOT_REGISTERED 1.307 + * 1.308 + * Return value: 1.309 + * PR_SUCCESS 1.310 + * PR_FAILURE 1.311 + */ 1.312 + 1.313 +NSS_IMPLEMENT PRStatus 1.314 +nssPointerTracker_remove 1.315 +( 1.316 + nssPointerTracker *tracker, 1.317 + const void *pointer 1.318 +) 1.319 +{ 1.320 + PRBool registered; 1.321 + 1.322 + if( (nssPointerTracker *)NULL == tracker ) { 1.323 + nss_SetError(NSS_ERROR_INVALID_POINTER); 1.324 + return PR_FAILURE; 1.325 + } 1.326 + 1.327 + if( (PZLock *)NULL == tracker->lock ) { 1.328 + nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED); 1.329 + return PR_FAILURE; 1.330 + } 1.331 + 1.332 + PZ_Lock(tracker->lock); 1.333 + 1.334 + if( (PLHashTable *)NULL == tracker->table ) { 1.335 + PZ_Unlock(tracker->lock); 1.336 + nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED); 1.337 + return PR_FAILURE; 1.338 + } 1.339 + 1.340 + registered = PL_HashTableRemove(tracker->table, pointer); 1.341 + PZ_Unlock(tracker->lock); 1.342 + 1.343 + if( !registered ) { 1.344 + nss_SetError(NSS_ERROR_POINTER_NOT_REGISTERED); 1.345 + return PR_FAILURE; 1.346 + } 1.347 + 1.348 + return PR_SUCCESS; 1.349 +} 1.350 + 1.351 +/* 1.352 + * nssPointerTracker_verify 1.353 + * 1.354 + * This method is only present in debug builds. 1.355 + * 1.356 + * This routine verifies that the specified pointer has been registered 1.357 + * with the nssPointerTracker object. The nssPointerTracker object is 1.358 + * threadsafe, and this call may be safely called from multiple threads 1.359 + * simultaneously with the same arguments. This routine returns a 1.360 + * PRStatus value; if the pointer is registered this will return 1.361 + * PR_SUCCESS. Otherwise it will set an error on the error stack and 1.362 + * return PR_FAILURE. Although the error is suitable for leaving on 1.363 + * the stack, callers may wish to augment the information available by 1.364 + * placing a more type-specific error on the stack. 1.365 + * 1.366 + * The error may be one of the following values: 1.367 + * NSS_ERROR_INVALID_POINTER 1.368 + * NSS_ERROR_TRACKER_NOT_INITIALIZED 1.369 + * NSS_ERROR_POINTER_NOT_REGISTERED 1.370 + * 1.371 + * Return value: 1.372 + * PR_SUCCESS 1.373 + * PR_FAILRUE 1.374 + */ 1.375 + 1.376 +NSS_IMPLEMENT PRStatus 1.377 +nssPointerTracker_verify 1.378 +( 1.379 + nssPointerTracker *tracker, 1.380 + const void *pointer 1.381 +) 1.382 +{ 1.383 + void *check; 1.384 + 1.385 + if( (nssPointerTracker *)NULL == tracker ) { 1.386 + nss_SetError(NSS_ERROR_INVALID_POINTER); 1.387 + return PR_FAILURE; 1.388 + } 1.389 + 1.390 + if( (PZLock *)NULL == tracker->lock ) { 1.391 + nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED); 1.392 + return PR_FAILURE; 1.393 + } 1.394 + 1.395 + PZ_Lock(tracker->lock); 1.396 + 1.397 + if( (PLHashTable *)NULL == tracker->table ) { 1.398 + PZ_Unlock(tracker->lock); 1.399 + nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED); 1.400 + return PR_FAILURE; 1.401 + } 1.402 + 1.403 + check = PL_HashTableLookup(tracker->table, pointer); 1.404 + PZ_Unlock(tracker->lock); 1.405 + 1.406 + if( (void *)NULL == check ) { 1.407 + nss_SetError(NSS_ERROR_POINTER_NOT_REGISTERED); 1.408 + return PR_FAILURE; 1.409 + } 1.410 + 1.411 + return PR_SUCCESS; 1.412 +} 1.413 + 1.414 +#endif /* DEBUG */