summaryrefslogtreecommitdiff
path: root/security/nss/lib/base/tracker.c
diff options
context:
space:
mode:
Diffstat (limited to 'security/nss/lib/base/tracker.c')
-rw-r--r--security/nss/lib/base/tracker.c544
1 files changed, 544 insertions, 0 deletions
diff --git a/security/nss/lib/base/tracker.c b/security/nss/lib/base/tracker.c
new file mode 100644
index 000000000..00d499354
--- /dev/null
+++ b/security/nss/lib/base/tracker.c
@@ -0,0 +1,544 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+
+#ifdef DEBUG
+static const char CVS_ID[] = "@(#) $RCSfile$ $Revision$ $Date$ $Name$";
+#endif /* DEBUG */
+
+/*
+ * 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
+
+/*
+ * call_once
+ *
+ * Unfortunately, NSPR's PR_CallOnce function doesn't accept a closure
+ * variable. So I have a static version here which does. This code
+ * is based on NSPR's, and uses the NSPR function to initialize the
+ * required lock.
+ */
+
+/*
+ * The is the "once block" that's passed to the "real" PR_CallOnce
+ * function, to call the local initializer myOnceFunction once.
+ */
+static PRCallOnceType myCallOnce;
+
+/*
+ * This structure is used by the call_once function to make sure that
+ * any "other" threads calling the call_once don't return too quickly,
+ * before the initializer has finished.
+ */
+static struct {
+ PRLock *ml;
+ PRCondVar *cv;
+} mod_init;
+
+/*
+ * This is the initializer for the above mod_init structure.
+ */
+static PRStatus
+myOnceFunction
+(
+ void
+)
+{
+ mod_init.ml = PR_NewLock();
+ if( (PRLock *)NULL == mod_init.ml ) {
+ return PR_FAILURE;
+ }
+
+ mod_init.cv = PR_NewCondVar(mod_init.ml);
+ if( (PRCondVar *)NULL == mod_init.cv ) {
+ PR_DestroyLock(mod_init.ml);
+ mod_init.ml = (PRLock *)NULL;
+ return PR_FAILURE;
+ }
+
+ return PR_SUCCESS;
+}
+
+/*
+ * The nss call_once callback takes a closure argument.
+ */
+typedef PRStatus (PR_CALLBACK *nssCallOnceFN)(void *arg);
+
+/*
+ * NSS's call_once function.
+ */
+static PRStatus
+call_once
+(
+ PRCallOnceType *once,
+ nssCallOnceFN func,
+ void *arg
+)
+{
+ PRStatus rv;
+
+ if( !myCallOnce.initialized ) {
+ rv = PR_CallOnce(&myCallOnce, myOnceFunction);
+ if( PR_SUCCESS != rv ) {
+ return rv;
+ }
+ }
+
+ if( !once->initialized ) {
+ if( 0 == PR_AtomicSet(&once->inProgress, 1) ) {
+ once->status = (*func)(arg);
+ PR_Lock(mod_init.ml);
+ once->initialized = 1;
+ PR_NotifyAllCondVar(mod_init.cv);
+ PR_Unlock(mod_init.ml);
+ } else {
+ PR_Lock(mod_init.ml);
+ while( !once->initialized ) {
+ PR_WaitCondVar(mod_init.cv, PR_INTERVAL_NO_TIMEOUT);
+ }
+ PR_Unlock(mod_init.ml);
+ }
+ }
+
+ return once->status;
+}
+
+/*
+ * Now we actually get to my own "call once" payload function.
+ * But wait, to create the hash, I need a hash function!
+ */
+
+/*
+ * 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 PR_CALLBACK PLHashNumber
+identity_hash
+(
+ const void *key
+)
+{
+ return (PLHashNumber)key;
+}
+
+/*
+ * 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 = PR_NewLock();
+ if( (PRLock *)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 ) {
+ PR_DestroyLock(tracker->lock);
+ tracker->lock = (PRLock *)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 = call_once(&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 PR_CALLBACK PRIntn
+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
+)
+{
+ PRLock *lock;
+ PRIntn count;
+
+ if( (nssPointerTracker *)NULL == tracker ) {
+ nss_SetError(NSS_ERROR_INVALID_POINTER);
+ return PR_FAILURE;
+ }
+
+ if( (PRLock *)NULL == tracker->lock ) {
+ nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED);
+ return PR_FAILURE;
+ }
+
+ lock = tracker->lock;
+ PR_Lock(lock);
+
+ if( (PLHashTable *)NULL == tracker->table ) {
+ PR_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 ) {
+ PR_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 = (PRLock *)NULL;
+ tracker->table = (PLHashTable *)NULL;
+
+ PR_Unlock(lock);
+ PR_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( (PRLock *)NULL == tracker->lock ) {
+ nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED);
+ return PR_FAILURE;
+ }
+
+ PR_Lock(tracker->lock);
+
+ if( (PLHashTable *)NULL == tracker->table ) {
+ PR_Unlock(tracker->lock);
+ nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED);
+ return PR_FAILURE;
+ }
+
+ check = PL_HashTableLookup(tracker->table, pointer);
+ if( (void *)NULL != check ) {
+ PR_Unlock(tracker->lock);
+ nss_SetError(NSS_ERROR_DUPLICATE_POINTER);
+ return PR_FAILURE;
+ }
+
+ entry = PL_HashTableAdd(tracker->table, pointer, (void *)pointer);
+
+ PR_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( (PRLock *)NULL == tracker->lock ) {
+ nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED);
+ return PR_FAILURE;
+ }
+
+ PR_Lock(tracker->lock);
+
+ if( (PLHashTable *)NULL == tracker->table ) {
+ PR_Unlock(tracker->lock);
+ nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED);
+ return PR_FAILURE;
+ }
+
+ registered = PL_HashTableRemove(tracker->table, pointer);
+ PR_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( (PRLock *)NULL == tracker->lock ) {
+ nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED);
+ return PR_FAILURE;
+ }
+
+ PR_Lock(tracker->lock);
+
+ if( (PLHashTable *)NULL == tracker->table ) {
+ PR_Unlock(tracker->lock);
+ nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED);
+ return PR_FAILURE;
+ }
+
+ check = PL_HashTableLookup(tracker->table, pointer);
+ PR_Unlock(tracker->lock);
+
+ if( (void *)NULL == check ) {
+ nss_SetError(NSS_ERROR_POINTER_NOT_REGISTERED);
+ return PR_FAILURE;
+ }
+
+ return PR_SUCCESS;
+}
+
+#endif /* DEBUG */