2018-05-04 16:08:28 +02:00
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
2015-10-21 05:03:22 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* tracker.c
|
|
|
|
*
|
|
|
|
* This file contains the code used by the pointer-tracking calls used
|
|
|
|
* in the debug builds to catch bad pointers. The entire contents are
|
|
|
|
* only available in debug builds (both internal and external builds).
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef BASE_H
|
|
|
|
#include "base.h"
|
|
|
|
#endif /* BASE_H */
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
/*
|
|
|
|
* identity_hash
|
|
|
|
*
|
|
|
|
* This static callback is a PLHashFunction as defined in plhash.h
|
|
|
|
* It merely returns the value of the object pointer as its hash.
|
|
|
|
* There are no possible errors.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static PLHashNumber PR_CALLBACK
|
|
|
|
identity_hash
|
|
|
|
(
|
|
|
|
const void *key
|
|
|
|
)
|
|
|
|
{
|
cherry-picked mozilla NSS upstream changes (to rev bad5fd065fa1, which is on par with 3.20):
bug1001332, 56b691c003ad, bug1086145, bug1054069, bug1155922, bug991783, bug1125025, bug1162521, bug1162644, bug1132941, bug1164364, bug1166205, bug1166163, bug1166515, bug1138554, bug1167046, bug1167043, bug1169451, bug1172128, bug1170322, bug102794, bug1128184, bug557830, bug1174648, bug1180244, bug1177784, bug1173413, bug1169174, bug1084669, bug951455, bug1183395, bug1177430, bug1183827, bug1160139, bug1154106, bug1142209, bug1185033, bug1193467, bug1182667(with sha512 changes backed out, which breaks VC6 compilation), bug1158489, bug337796
2018-07-12 15:44:51 +02:00
|
|
|
return (PLHashNumber)((char *)key - (char *)NULL);
|
2015-10-21 05:03:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* trackerOnceFunc
|
|
|
|
*
|
|
|
|
* This function is called once, using the nssCallOnce function above.
|
|
|
|
* It creates a new pointer tracker object; initialising its hash
|
|
|
|
* table and protective lock.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static PRStatus
|
|
|
|
trackerOnceFunc
|
|
|
|
(
|
|
|
|
void *arg
|
|
|
|
)
|
|
|
|
{
|
|
|
|
nssPointerTracker *tracker = (nssPointerTracker *)arg;
|
|
|
|
|
|
|
|
tracker->lock = PZ_NewLock(nssILockOther);
|
|
|
|
if( (PZLock *)NULL == tracker->lock ) {
|
|
|
|
return PR_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
tracker->table = PL_NewHashTable(0,
|
|
|
|
identity_hash,
|
|
|
|
PL_CompareValues,
|
|
|
|
PL_CompareValues,
|
|
|
|
(PLHashAllocOps *)NULL,
|
|
|
|
(void *)NULL);
|
|
|
|
if( (PLHashTable *)NULL == tracker->table ) {
|
|
|
|
PZ_DestroyLock(tracker->lock);
|
|
|
|
tracker->lock = (PZLock *)NULL;
|
|
|
|
return PR_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return PR_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* nssPointerTracker_initialize
|
|
|
|
*
|
|
|
|
* This method is only present in debug builds.
|
|
|
|
*
|
|
|
|
* This routine initializes an nssPointerTracker object. Note that
|
|
|
|
* the object must have been declared *static* to guarantee that it
|
|
|
|
* is in a zeroed state initially. This routine is idempotent, and
|
|
|
|
* may even be safely called by multiple threads simultaneously with
|
|
|
|
* the same argument. This routine returns a PRStatus value; if
|
|
|
|
* successful, it will return PR_SUCCESS. On failure it will set an
|
|
|
|
* error on the error stack and return PR_FAILURE.
|
|
|
|
*
|
|
|
|
* The error may be one of the following values:
|
|
|
|
* NSS_ERROR_NO_MEMORY
|
|
|
|
*
|
|
|
|
* Return value:
|
|
|
|
* PR_SUCCESS
|
|
|
|
* PR_FAILURE
|
|
|
|
*/
|
|
|
|
|
|
|
|
NSS_IMPLEMENT PRStatus
|
|
|
|
nssPointerTracker_initialize
|
|
|
|
(
|
|
|
|
nssPointerTracker *tracker
|
|
|
|
)
|
|
|
|
{
|
|
|
|
PRStatus rv = PR_CallOnceWithArg(&tracker->once, trackerOnceFunc, tracker);
|
|
|
|
if( PR_SUCCESS != rv ) {
|
|
|
|
nss_SetError(NSS_ERROR_NO_MEMORY);
|
|
|
|
}
|
|
|
|
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef DONT_DESTROY_EMPTY_TABLES
|
|
|
|
/* See same #ifdef below */
|
|
|
|
/*
|
|
|
|
* count_entries
|
|
|
|
*
|
|
|
|
* This static routine is a PLHashEnumerator, as defined in plhash.h.
|
|
|
|
* It merely causes the enumeration function to count the number of
|
|
|
|
* entries.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static PRIntn PR_CALLBACK
|
|
|
|
count_entries
|
|
|
|
(
|
|
|
|
PLHashEntry *he,
|
|
|
|
PRIntn index,
|
|
|
|
void *arg
|
|
|
|
)
|
|
|
|
{
|
|
|
|
return HT_ENUMERATE_NEXT;
|
|
|
|
}
|
|
|
|
#endif /* DONT_DESTROY_EMPTY_TABLES */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* zero_once
|
|
|
|
*
|
|
|
|
* This is a guaranteed zeroed once block. It's used to help clear
|
|
|
|
* the tracker.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static const PRCallOnceType zero_once;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* nssPointerTracker_finalize
|
|
|
|
*
|
|
|
|
* This method is only present in debug builds.
|
|
|
|
*
|
|
|
|
* This routine returns the nssPointerTracker object to the pre-
|
|
|
|
* initialized state, releasing all resources used by the object.
|
|
|
|
* It will *NOT* destroy the objects being tracked by the pointer
|
|
|
|
* (should any remain), and therefore cannot be used to "sweep up"
|
|
|
|
* remaining objects. This routine returns a PRStatus value; if
|
|
|
|
* successful, it will return PR_SUCCES. On failure it will set an
|
|
|
|
* error on the error stack and return PR_FAILURE. If any objects
|
|
|
|
* remain in the tracker when it is finalized, that will be treated
|
|
|
|
* as an error.
|
|
|
|
*
|
|
|
|
* The error may be one of the following values:
|
|
|
|
* NSS_ERROR_INVALID_POINTER
|
|
|
|
* NSS_ERROR_TRACKER_NOT_INITIALIZED
|
|
|
|
* NSS_ERROR_TRACKER_NOT_EMPTY
|
|
|
|
*
|
|
|
|
* Return value:
|
|
|
|
* PR_SUCCESS
|
|
|
|
* PR_FAILURE
|
|
|
|
*/
|
|
|
|
|
|
|
|
NSS_IMPLEMENT PRStatus
|
|
|
|
nssPointerTracker_finalize
|
|
|
|
(
|
|
|
|
nssPointerTracker *tracker
|
|
|
|
)
|
|
|
|
{
|
|
|
|
PZLock *lock;
|
|
|
|
|
|
|
|
if( (nssPointerTracker *)NULL == tracker ) {
|
|
|
|
nss_SetError(NSS_ERROR_INVALID_POINTER);
|
|
|
|
return PR_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( (PZLock *)NULL == tracker->lock ) {
|
|
|
|
nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED);
|
|
|
|
return PR_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
lock = tracker->lock;
|
|
|
|
PZ_Lock(lock);
|
|
|
|
|
|
|
|
if( (PLHashTable *)NULL == tracker->table ) {
|
|
|
|
PZ_Unlock(lock);
|
|
|
|
nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED);
|
|
|
|
return PR_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef DONT_DESTROY_EMPTY_TABLES
|
|
|
|
/*
|
|
|
|
* I changed my mind; I think we don't want this after all.
|
|
|
|
* Comments?
|
|
|
|
*/
|
|
|
|
count = PL_HashTableEnumerateEntries(tracker->table,
|
|
|
|
count_entries,
|
|
|
|
(void *)NULL);
|
|
|
|
|
|
|
|
if( 0 != count ) {
|
|
|
|
PZ_Unlock(lock);
|
|
|
|
nss_SetError(NSS_ERROR_TRACKER_NOT_EMPTY);
|
|
|
|
return PR_FAILURE;
|
|
|
|
}
|
|
|
|
#endif /* DONT_DESTROY_EMPTY_TABLES */
|
|
|
|
|
|
|
|
PL_HashTableDestroy(tracker->table);
|
|
|
|
/* memset(tracker, 0, sizeof(nssPointerTracker)); */
|
|
|
|
tracker->once = zero_once;
|
|
|
|
tracker->lock = (PZLock *)NULL;
|
|
|
|
tracker->table = (PLHashTable *)NULL;
|
|
|
|
|
|
|
|
PZ_Unlock(lock);
|
|
|
|
PZ_DestroyLock(lock);
|
|
|
|
|
|
|
|
return PR_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* nssPointerTracker_add
|
|
|
|
*
|
|
|
|
* This method is only present in debug builds.
|
|
|
|
*
|
|
|
|
* This routine adds the specified pointer to the nssPointerTracker
|
|
|
|
* object. It should be called in constructor objects to register
|
|
|
|
* new valid objects. The nssPointerTracker is threadsafe, but this
|
|
|
|
* call is not idempotent. This routine returns a PRStatus value;
|
|
|
|
* if successful it will return PR_SUCCESS. On failure it will set
|
|
|
|
* an error on the error stack and return PR_FAILURE.
|
|
|
|
*
|
|
|
|
* The error may be one of the following values:
|
|
|
|
* NSS_ERROR_INVALID_POINTER
|
|
|
|
* NSS_ERROR_NO_MEMORY
|
|
|
|
* NSS_ERROR_TRACKER_NOT_INITIALIZED
|
|
|
|
* NSS_ERROR_DUPLICATE_POINTER
|
|
|
|
*
|
|
|
|
* Return value:
|
|
|
|
* PR_SUCCESS
|
|
|
|
* PR_FAILURE
|
|
|
|
*/
|
|
|
|
|
|
|
|
NSS_IMPLEMENT PRStatus
|
|
|
|
nssPointerTracker_add
|
|
|
|
(
|
|
|
|
nssPointerTracker *tracker,
|
|
|
|
const void *pointer
|
|
|
|
)
|
|
|
|
{
|
|
|
|
void *check;
|
|
|
|
PLHashEntry *entry;
|
|
|
|
|
|
|
|
if( (nssPointerTracker *)NULL == tracker ) {
|
|
|
|
nss_SetError(NSS_ERROR_INVALID_POINTER);
|
|
|
|
return PR_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( (PZLock *)NULL == tracker->lock ) {
|
|
|
|
nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED);
|
|
|
|
return PR_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
PZ_Lock(tracker->lock);
|
|
|
|
|
|
|
|
if( (PLHashTable *)NULL == tracker->table ) {
|
|
|
|
PZ_Unlock(tracker->lock);
|
|
|
|
nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED);
|
|
|
|
return PR_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
check = PL_HashTableLookup(tracker->table, pointer);
|
|
|
|
if( (void *)NULL != check ) {
|
|
|
|
PZ_Unlock(tracker->lock);
|
|
|
|
nss_SetError(NSS_ERROR_DUPLICATE_POINTER);
|
|
|
|
return PR_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
entry = PL_HashTableAdd(tracker->table, pointer, (void *)pointer);
|
|
|
|
|
|
|
|
PZ_Unlock(tracker->lock);
|
|
|
|
|
|
|
|
if( (PLHashEntry *)NULL == entry ) {
|
|
|
|
nss_SetError(NSS_ERROR_NO_MEMORY);
|
|
|
|
return PR_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return PR_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* nssPointerTracker_remove
|
|
|
|
*
|
|
|
|
* This method is only present in debug builds.
|
|
|
|
*
|
|
|
|
* This routine removes the specified pointer from the
|
|
|
|
* nssPointerTracker object. It does not call any destructor for the
|
|
|
|
* object; rather, this should be called from the object's destructor.
|
|
|
|
* The nssPointerTracker is threadsafe, but this call is not
|
|
|
|
* idempotent. This routine returns a PRStatus value; if successful
|
|
|
|
* it will return PR_SUCCESS. On failure it will set an error on the
|
|
|
|
* error stack and return PR_FAILURE.
|
|
|
|
*
|
|
|
|
* The error may be one of the following values:
|
|
|
|
* NSS_ERROR_INVALID_POINTER
|
|
|
|
* NSS_ERROR_TRACKER_NOT_INITIALIZED
|
|
|
|
* NSS_ERROR_POINTER_NOT_REGISTERED
|
|
|
|
*
|
|
|
|
* Return value:
|
|
|
|
* PR_SUCCESS
|
|
|
|
* PR_FAILURE
|
|
|
|
*/
|
|
|
|
|
|
|
|
NSS_IMPLEMENT PRStatus
|
|
|
|
nssPointerTracker_remove
|
|
|
|
(
|
|
|
|
nssPointerTracker *tracker,
|
|
|
|
const void *pointer
|
|
|
|
)
|
|
|
|
{
|
|
|
|
PRBool registered;
|
|
|
|
|
|
|
|
if( (nssPointerTracker *)NULL == tracker ) {
|
|
|
|
nss_SetError(NSS_ERROR_INVALID_POINTER);
|
|
|
|
return PR_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( (PZLock *)NULL == tracker->lock ) {
|
|
|
|
nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED);
|
|
|
|
return PR_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
PZ_Lock(tracker->lock);
|
|
|
|
|
|
|
|
if( (PLHashTable *)NULL == tracker->table ) {
|
|
|
|
PZ_Unlock(tracker->lock);
|
|
|
|
nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED);
|
|
|
|
return PR_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
registered = PL_HashTableRemove(tracker->table, pointer);
|
|
|
|
PZ_Unlock(tracker->lock);
|
|
|
|
|
|
|
|
if( !registered ) {
|
|
|
|
nss_SetError(NSS_ERROR_POINTER_NOT_REGISTERED);
|
|
|
|
return PR_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return PR_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* nssPointerTracker_verify
|
|
|
|
*
|
|
|
|
* This method is only present in debug builds.
|
|
|
|
*
|
|
|
|
* This routine verifies that the specified pointer has been registered
|
|
|
|
* with the nssPointerTracker object. The nssPointerTracker object is
|
|
|
|
* threadsafe, and this call may be safely called from multiple threads
|
|
|
|
* simultaneously with the same arguments. This routine returns a
|
|
|
|
* PRStatus value; if the pointer is registered this will return
|
|
|
|
* PR_SUCCESS. Otherwise it will set an error on the error stack and
|
|
|
|
* return PR_FAILURE. Although the error is suitable for leaving on
|
|
|
|
* the stack, callers may wish to augment the information available by
|
|
|
|
* placing a more type-specific error on the stack.
|
|
|
|
*
|
|
|
|
* The error may be one of the following values:
|
|
|
|
* NSS_ERROR_INVALID_POINTER
|
|
|
|
* NSS_ERROR_TRACKER_NOT_INITIALIZED
|
|
|
|
* NSS_ERROR_POINTER_NOT_REGISTERED
|
|
|
|
*
|
|
|
|
* Return value:
|
|
|
|
* PR_SUCCESS
|
|
|
|
* PR_FAILRUE
|
|
|
|
*/
|
|
|
|
|
|
|
|
NSS_IMPLEMENT PRStatus
|
|
|
|
nssPointerTracker_verify
|
|
|
|
(
|
|
|
|
nssPointerTracker *tracker,
|
|
|
|
const void *pointer
|
|
|
|
)
|
|
|
|
{
|
|
|
|
void *check;
|
|
|
|
|
|
|
|
if( (nssPointerTracker *)NULL == tracker ) {
|
|
|
|
nss_SetError(NSS_ERROR_INVALID_POINTER);
|
|
|
|
return PR_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( (PZLock *)NULL == tracker->lock ) {
|
|
|
|
nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED);
|
|
|
|
return PR_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
PZ_Lock(tracker->lock);
|
|
|
|
|
|
|
|
if( (PLHashTable *)NULL == tracker->table ) {
|
|
|
|
PZ_Unlock(tracker->lock);
|
|
|
|
nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED);
|
|
|
|
return PR_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
check = PL_HashTableLookup(tracker->table, pointer);
|
|
|
|
PZ_Unlock(tracker->lock);
|
|
|
|
|
|
|
|
if( (void *)NULL == check ) {
|
|
|
|
nss_SetError(NSS_ERROR_POINTER_NOT_REGISTERED);
|
|
|
|
return PR_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return PR_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* DEBUG */
|