tenfourfox/security/nss/lib/base/tracker.c
Cameron Kaiser c9b2922b70 hello FPR
2017-04-19 00:56:45 -07:00

412 lines
10 KiB
C

/* 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/. */
/*
* 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
)
{
return (PLHashNumber)((char *)key - (char *)NULL);
}
/*
* 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 */