tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

tracker.c (10278B)


      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 {
     29    return (PLHashNumber)((char *)key - (char *)NULL);
     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 
     40 static PRStatus
     41 trackerOnceFunc(void *arg)
     42 {
     43    nssPointerTracker *tracker = (nssPointerTracker *)arg;
     44 
     45    tracker->lock = PZ_NewLock(nssILockOther);
     46    if ((PZLock *)NULL == 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 *)NULL);
     53    if ((PLHashTable *)NULL == tracker->table) {
     54        PZ_DestroyLock(tracker->lock);
     55        tracker->lock = (PZLock *)NULL;
     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 
     83 NSS_IMPLEMENT PRStatus
     84 nssPointerTracker_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 
    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 {
    148    PZLock *lock;
    149 
    150    if ((nssPointerTracker *)NULL == tracker) {
    151        nss_SetError(NSS_ERROR_INVALID_POINTER);
    152        return PR_FAILURE;
    153    }
    154 
    155    if ((PZLock *)NULL == tracker->lock) {
    156        nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED);
    157        return PR_FAILURE;
    158    }
    159 
    160    lock = tracker->lock;
    161    PZ_Lock(lock);
    162 
    163    if ((PLHashTable *)NULL == tracker->table) {
    164        PZ_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);
    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    PL_HashTableDestroy(tracker->table);
    185    /* memset(tracker, 0, sizeof(nssPointerTracker)); */
    186    tracker->once = zero_once;
    187    tracker->lock = (PZLock *)NULL;
    188    tracker->table = (PLHashTable *)NULL;
    189 
    190    PZ_Unlock(lock);
    191    PZ_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 
    219 NSS_IMPLEMENT PRStatus
    220 nssPointerTracker_add(nssPointerTracker *tracker, const void *pointer)
    221 {
    222    void *check;
    223    PLHashEntry *entry;
    224 
    225    if ((nssPointerTracker *)NULL == tracker) {
    226        nss_SetError(NSS_ERROR_INVALID_POINTER);
    227        return PR_FAILURE;
    228    }
    229 
    230    if ((PZLock *)NULL == tracker->lock) {
    231        nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED);
    232        return PR_FAILURE;
    233    }
    234 
    235    PZ_Lock(tracker->lock);
    236 
    237    if ((PLHashTable *)NULL == tracker->table) {
    238        PZ_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 != check) {
    245        PZ_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);
    253 
    254    if ((PLHashEntry *)NULL == 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 
    285 NSS_IMPLEMENT PRStatus
    286 nssPointerTracker_remove(nssPointerTracker *tracker, const void *pointer)
    287 {
    288    PRBool registered;
    289 
    290    if ((nssPointerTracker *)NULL == tracker) {
    291        nss_SetError(NSS_ERROR_INVALID_POINTER);
    292        return PR_FAILURE;
    293    }
    294 
    295    if ((PZLock *)NULL == tracker->lock) {
    296        nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED);
    297        return PR_FAILURE;
    298    }
    299 
    300    PZ_Lock(tracker->lock);
    301 
    302    if ((PLHashTable *)NULL == tracker->table) {
    303        PZ_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);
    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 
    344 NSS_IMPLEMENT PRStatus
    345 nssPointerTracker_verify(nssPointerTracker *tracker, const void *pointer)
    346 {
    347    void *check;
    348 
    349    if ((nssPointerTracker *)NULL == tracker) {
    350        nss_SetError(NSS_ERROR_INVALID_POINTER);
    351        return PR_FAILURE;
    352    }
    353 
    354    if ((PZLock *)NULL == tracker->lock) {
    355        nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED);
    356        return PR_FAILURE;
    357    }
    358 
    359    PZ_Lock(tracker->lock);
    360 
    361    if ((PLHashTable *)NULL == tracker->table) {
    362        PZ_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);
    369 
    370    if ((void *)NULL == check) {
    371        nss_SetError(NSS_ERROR_POINTER_NOT_REGISTERED);
    372        return PR_FAILURE;
    373    }
    374 
    375    return PR_SUCCESS;
    376 }
    377 
    378 #endif /* DEBUG */