summaryrefslogtreecommitdiffstats
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.c378
1 files changed, 378 insertions, 0 deletions
diff --git a/security/nss/lib/base/tracker.c b/security/nss/lib/base/tracker.c
new file mode 100644
index 0000000000..850add7c4b
--- /dev/null
+++ b/security/nss/lib/base/tracker.c
@@ -0,0 +1,378 @@
+/* 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 */