[gnome-keyring: 1/2] Secure memory tagging



commit 3f60c65cc0f535362ad6eb77ec08e63c235110e2
Author: Stef Walter <stef memberwebs com>
Date:   Thu Oct 14 19:38:39 2010 +0000

    Secure memory tagging
    
     * Tag all secure memory with strings, and print out diagnostic
       report of what's in use, upon request.

 daemon/control/gkd-control-client.c     |    2 +
 daemon/control/gkd-control-server.c     |    2 +
 daemon/dbus/gkd-secret-unlock.c         |    2 +
 daemon/gkd-main.c                       |   60 ++++++
 daemon/gpg-agent/gkd-gpg-agent-ops.c    |    2 +
 daemon/ssh-agent/gkd-ssh-agent-ops.c    |    3 +-
 daemon/ssh-agent/gkd-ssh-agent.c        |    2 +
 egg/egg-dh.c                            |    2 +
 egg/egg-entry-buffer.c                  |    2 +
 egg/egg-libgcrypt.c                     |    2 +
 egg/egg-openssl.c                       |    2 +
 egg/egg-secure-memory.c                 |  309 +++++++++++++++++++------------
 egg/egg-secure-memory.h                 |   31 +++-
 egg/egg-symkey.c                        |    2 +
 egg/tests/test-secmem.c                 |   24 ++-
 gcr/gcr-parser.c                        |    2 +
 pkcs11/gkm/gkm-aes-key.c                |    2 +
 pkcs11/gkm/gkm-aes-mechanism.c          |    2 +
 pkcs11/gkm/gkm-data-der.c               |    2 +
 pkcs11/gkm/gkm-dh-mechanism.c           |    2 +
 pkcs11/gkm/gkm-generic-key.c            |    2 +
 pkcs11/gkm/gkm-hkdf-mechanism.c         |    2 +
 pkcs11/gkm/gkm-null-mechanism.c         |    2 +
 pkcs11/gkm/gkm-secret.c                 |    2 +
 pkcs11/gnome2-store/gkm-gnome2-file.c   |    2 +
 pkcs11/secret-store/gkm-secret-binary.c |    2 +
 pkcs11/wrap-layer/gkm-wrap-login.c      |    2 +
 ui/gku-prompt-tool.c                    |    2 +
 ui/gku-prompt-util.c                    |    2 +
 ui/gku-prompt.c                         |    2 +
 ui/tests/test-util.c                    |    2 +
 31 files changed, 342 insertions(+), 137 deletions(-)
---
diff --git a/daemon/control/gkd-control-client.c b/daemon/control/gkd-control-client.c
index a52dc0f..70b8006 100644
--- a/daemon/control/gkd-control-client.c
+++ b/daemon/control/gkd-control-client.c
@@ -36,6 +36,8 @@
 #include <sys/un.h>
 #include <sys/stat.h>
 
+EGG_SECURE_DECLARE (control_client);
+
 static int
 control_connect (const gchar *path)
 {
diff --git a/daemon/control/gkd-control-server.c b/daemon/control/gkd-control-server.c
index 6e06297..4e4db3e 100644
--- a/daemon/control/gkd-control-server.c
+++ b/daemon/control/gkd-control-server.c
@@ -47,6 +47,8 @@ typedef struct _ControlData {
 	gsize position;
 } ControlData;
 
+EGG_SECURE_DECLARE (control_server);
+
 /* -----------------------------------------------------------------------------------
  * CONTROL SERVER
  */
diff --git a/daemon/dbus/gkd-secret-unlock.c b/daemon/dbus/gkd-secret-unlock.c
index ff233e1..0c466e7 100644
--- a/daemon/dbus/gkd-secret-unlock.c
+++ b/daemon/dbus/gkd-secret-unlock.c
@@ -83,6 +83,8 @@ G_DEFINE_TYPE_WITH_CODE (GkdSecretUnlock, gkd_secret_unlock, G_TYPE_OBJECT,
 static guint unique_prompt_number = 0;
 static GQueue unlock_prompt_queue = G_QUEUE_INIT;
 
+EGG_SECURE_DECLARE (secret_unlock);
+
 /* -----------------------------------------------------------------------------
  * INTERNAL
  */
diff --git a/daemon/gkd-main.c b/daemon/gkd-main.c
index b60507d..0fe17bc 100644
--- a/daemon/gkd-main.c
+++ b/daemon/gkd-main.c
@@ -78,6 +78,8 @@ typedef int socklen_t;
 #define GKD_COMP_SSH        "ssh"
 #define GKD_COMP_GPG        "gpg"
 
+EGG_SECURE_DECLARE (daemon_main);
+
 /* -----------------------------------------------------------------------------
  * COMMAND LINE
  */
@@ -322,6 +324,58 @@ prepare_logging ()
 	g_set_printerr_handler (printerr_handler);
 }
 
+#ifdef WITH_TESTABLE
+
+static void
+dump_diagnostics (void)
+{
+	egg_secure_rec *records;
+	egg_secure_rec *rec;
+	unsigned int count, i;
+	GHashTable *table;
+	GHashTableIter iter;
+	gsize request = 0;
+	gsize block = 0;
+
+	g_printerr ("------------------- Secure Memory --------------------\n");
+	g_printerr (" Tag                          Used            Space\n");
+	g_printerr ("------------------------------------------------------\n");
+
+	records = egg_secure_records (&count);
+	table = g_hash_table_new (g_str_hash, g_str_equal);
+	for (i = 0; i < count; i++) {
+		if (!records[i].tag)
+			records[i].tag = "<unused>";
+		rec = g_hash_table_lookup (table, records[i].tag);
+		if (rec == NULL)
+			g_hash_table_insert (table, (gchar *)records[i].tag, &records[i]);
+		else {
+			rec->block_length += records[i].block_length;
+			rec->request_length += records[i].request_length;
+		}
+		block += records[i].block_length;
+		request += records[i].request_length;
+	}
+
+	g_hash_table_iter_init (&iter, table);
+	while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&rec))
+		g_printerr (" %-20s %12lu %16lu\n", rec->tag,
+		            (unsigned long)rec->request_length,
+		            (unsigned long)rec->block_length);
+
+	if (count > 0)
+		g_printerr ("------------------------------------------------------\n");
+
+	g_printerr (" %-20s %12lu %16lu\n", "Total",
+	            (unsigned long)request, (unsigned long)block);
+	g_printerr ("------------------------------------------------------\n");
+
+	g_hash_table_destroy (table);
+	free (records);
+}
+
+#endif /* WITH_TESTABLE */
+
 /* -----------------------------------------------------------------------------
  * SIGNALS
  */
@@ -343,6 +397,11 @@ signal_thread (gpointer user_data)
 		}
 
 		switch (sig) {
+		case SIGUSR1:
+#ifdef WITH_TESTABLE
+			dump_diagnostics ();
+#endif /* WITH_TESTABLE */
+			break;
 		case SIGPIPE:
 			/* Ignore */
 			break;
@@ -378,6 +437,7 @@ setup_signal_handling (GMainLoop *loop)
 	sigaddset (&signal_set, SIGPIPE);
 	sigaddset (&signal_set, SIGHUP);
 	sigaddset (&signal_set, SIGTERM);
+	sigaddset (&signal_set, SIGUSR1);
 	pthread_sigmask (SIG_BLOCK, &signal_set, NULL);
 
 	res = pthread_create (&sig_thread, NULL, signal_thread, loop);
diff --git a/daemon/gpg-agent/gkd-gpg-agent-ops.c b/daemon/gpg-agent/gkd-gpg-agent-ops.c
index 97cb946..58aeb8f 100644
--- a/daemon/gpg-agent/gkd-gpg-agent-ops.c
+++ b/daemon/gpg-agent/gkd-gpg-agent-ops.c
@@ -44,6 +44,8 @@
 #define COLLECTION    "session"
 #define N_COLLECTION  7
 
+EGG_SECURE_DECLARE (gpg_agent_ops);
+
 /* ----------------------------------------------------------------------------------
  * PASSWORD STUFF
  */
diff --git a/daemon/ssh-agent/gkd-ssh-agent-ops.c b/daemon/ssh-agent/gkd-ssh-agent-ops.c
index 691ebe0..a87e28e 100644
--- a/daemon/ssh-agent/gkd-ssh-agent-ops.c
+++ b/daemon/ssh-agent/gkd-ssh-agent-ops.c
@@ -39,11 +39,12 @@
 #include <string.h>
 #include <stdio.h>
 
-
 #define V1_LABEL "SSH1 RSA Key"
 
 typedef gboolean (*ObjectForeachFunc) (GckObject *object, gpointer user_data);
 
+EGG_SECURE_DECLARE (ssh_agent_ops);
+
 /* ---------------------------------------------------------------------------- */
 
 
diff --git a/daemon/ssh-agent/gkd-ssh-agent.c b/daemon/ssh-agent/gkd-ssh-agent.c
index 649d681..2f83631 100644
--- a/daemon/ssh-agent/gkd-ssh-agent.c
+++ b/daemon/ssh-agent/gkd-ssh-agent.c
@@ -47,6 +47,8 @@ typedef int socklen_t;
 /* The loaded PKCS#11 modules */
 static GList *pkcs11_modules = NULL;
 
+EGG_SECURE_DECLARE (ssh_agent);
+
 static gboolean
 read_all (int fd, guchar *buf, int len)
 {
diff --git a/egg/egg-dh.c b/egg/egg-dh.c
index bed524e..85dec77 100644
--- a/egg/egg-dh.c
+++ b/egg/egg-dh.c
@@ -27,6 +27,8 @@
 /* Enabling this is a complete security compromise */
 #define DEBUG_DH_SECRET 0
 
+EGG_SECURE_DECLARE (dh);
+
 typedef struct _DHGroup {
 	const gchar *name;
 	guint bits;
diff --git a/egg/egg-entry-buffer.c b/egg/egg-entry-buffer.c
index afb4093..cac1f2d 100644
--- a/egg/egg-entry-buffer.c
+++ b/egg/egg-entry-buffer.c
@@ -28,6 +28,8 @@
 
 #include <string.h>
 
+EGG_SECURE_DECLARE (entry_buffer);
+
 /* Initial size of buffer, in bytes */
 #define MIN_SIZE 16
 
diff --git a/egg/egg-libgcrypt.c b/egg/egg-libgcrypt.c
index 8e1eb70..e5b3f55 100644
--- a/egg/egg-libgcrypt.c
+++ b/egg/egg-libgcrypt.c
@@ -28,6 +28,8 @@
 
 #include <gcrypt.h>
 
+EGG_SECURE_DECLARE (libgcrypt);
+
 static void
 log_handler (gpointer unused, int unknown, const gchar *msg, va_list va)
 {
diff --git a/egg/egg-openssl.c b/egg/egg-openssl.c
index 63d92ab..45daccb 100644
--- a/egg/egg-openssl.c
+++ b/egg/egg-openssl.c
@@ -56,6 +56,8 @@
 #define PEM_PREF_END      "-----END "
 #define PEM_PREF_END_L    9
 
+EGG_SECURE_DECLARE (openssl);
+
 static void
 parse_header_lines (const gchar *hbeg, const gchar *hend, GHashTable **result)
 {
diff --git a/egg/egg-secure-memory.c b/egg/egg-secure-memory.c
index facc9fe..ab63d45 100644
--- a/egg/egg-secure-memory.c
+++ b/egg/egg-secure-memory.c
@@ -97,20 +97,22 @@ typedef void* word_t;
 typedef struct _Cell {
 	word_t *words;          /* Pointer to secure memory */
 	size_t n_words;         /* Amount of secure memory in words */
-	size_t allocated;       /* Amount actually requested by app, in bytes, 0 if unused */
-	struct _Cell *next;     /* Next in unused memory ring, or NULL if used */
-	struct _Cell *prev;     /* Previous in unused memory ring, or NULL if used */
+	size_t requested;       /* Amount actually requested by app, in bytes, 0 if unused */
+	const char *tag;        /* Tag which describes the allocation */
+	struct _Cell *next;     /* Next in memory ring */
+	struct _Cell *prev;     /* Previous in memory ring */
 } Cell;
 
 /* 
  * A block of secure memory. This structure is the header in that block.
  */
 typedef struct _Block {
-	word_t *words;          /* Actual memory hangs off here */
-	size_t n_words;         /* Number of words in block */
-	size_t used;            /* Number of used allocations */
-	struct _Cell* unused;   /* Ring of unused allocations */
-	struct _Block *next;    /* Next block in list */ 
+	word_t *words;              /* Actual memory hangs off here */
+	size_t n_words;             /* Number of words in block */
+	size_t n_used;              /* Number of used allocations */
+	struct _Cell* used_cells;   /* Ring of used allocations */
+	struct _Cell* unused_cells; /* Ring of unused allocations */
+	struct _Block *next;        /* Next block in list */
 } Block;
 
 /* -----------------------------------------------------------------------------
@@ -463,7 +465,9 @@ sec_neighbor_after (Block *block, Cell *cell)
 }
 
 static void*
-sec_alloc (Block *block, size_t length)
+sec_alloc (Block *block,
+           const char *tag,
+           size_t length)
 {
 	Cell *cell, *other;
 	size_t n_words;
@@ -471,8 +475,9 @@ sec_alloc (Block *block, size_t length)
 	
 	ASSERT (block);
 	ASSERT (length);
+	ASSERT (tag);
 
-	if (!block->unused)
+	if (!block->unused_cells)
 		return NULL;
 
 	/* 
@@ -486,10 +491,10 @@ sec_alloc (Block *block, size_t length)
 	n_words = sec_size_to_words (length) + 2;
 	
 	/* Look for a cell of at least our required size */
-	cell = block->unused;
+	cell = block->unused_cells;
 	while (cell->n_words < n_words) {
 		cell = cell->next;
-		if (cell == block->unused) {
+		if (cell == block->unused_cells) {
 			cell = NULL;
 			break;
 		}
@@ -497,8 +502,9 @@ sec_alloc (Block *block, size_t length)
 	
 	if (!cell)
 		return NULL;
-	
-	ASSERT (cell->allocated == 0);
+
+	ASSERT (cell->tag == NULL);
+	ASSERT (cell->requested == 0);
 	ASSERT (cell->prev);
 	ASSERT (cell->words);
 	sec_check_guards (cell);
@@ -520,10 +526,12 @@ sec_alloc (Block *block, size_t length)
 	}
 	
 	if (cell->next)
-		sec_remove_cell_ring (&block->unused, cell);
-	
-	++block->used;
-	cell->allocated = length;
+		sec_remove_cell_ring (&block->unused_cells, cell);
+
+	++block->n_used;
+	cell->tag = tag;
+	cell->requested = length;
+	sec_insert_cell_ring (&block->used_cells, cell);
 	memory = sec_cell_to_memory (cell);
 	
 #ifdef WITH_VALGRIND
@@ -559,16 +567,19 @@ sec_free (Block *block, void *memory)
 #endif
 
 	sec_check_guards (cell);
-	sec_clear_memory (memory, 0, cell->allocated);
+	sec_clear_memory (memory, 0, cell->requested);
 
 	sec_check_guards (cell);
-	ASSERT (cell->next == NULL);
-	ASSERT (cell->prev == NULL);
-	ASSERT (cell->allocated > 0);
+	ASSERT (cell->requested > 0);
+	ASSERT (cell->tag != NULL);
+
+	/* Remove from the used cell ring */
+	sec_remove_cell_ring (&block->used_cells, cell);
 
         /* Find previous unallocated neighbor, and merge if possible */
         other = sec_neighbor_before (block, cell);
-        if (other && other->allocated == 0) {
+        if (other && other->requested == 0) {
+        	ASSERT (other->tag == NULL);
         	ASSERT (other->next && other->prev);
         	other->n_words += cell->n_words;
         	sec_write_guards (other);
@@ -578,12 +589,13 @@ sec_free (Block *block, void *memory)
         
         /* Find next unallocated neighbor, and merge if possible */
         other = sec_neighbor_after (block, cell);
-        if (other && other->allocated == 0) {
+        if (other && other->requested == 0) {
+        	ASSERT (other->tag == NULL);
         	ASSERT (other->next && other->prev);
         	other->n_words += cell->n_words;
         	other->words = cell->words;
         	if (cell->next)
-        		sec_remove_cell_ring (&block->unused, cell);
+        		sec_remove_cell_ring (&block->unused_cells, cell);
         	sec_write_guards (other);
         	pool_free (cell);
         	cell = other;
@@ -591,25 +603,30 @@ sec_free (Block *block, void *memory)
 
         /* Add to the unused list if not already there */
         if (!cell->next)
-        	sec_insert_cell_ring (&block->unused, cell);
-        
-        cell->allocated = 0;
-        --block->used;
+        	sec_insert_cell_ring (&block->unused_cells, cell);
+
+        cell->tag = NULL;
+        cell->requested = 0;
+        --block->n_used;
         return NULL;
 }
 
 static void*
-sec_realloc (Block *block, void *memory, size_t length) 
+sec_realloc (Block *block,
+             const char *tag,
+             void *memory,
+             size_t length)
 {
 	Cell *cell, *other;
 	word_t *word;
 	size_t n_words;
 	size_t valid;
 	void *alloc;
-	
+
 	/* Standard realloc behavior, should have been handled elsewhere */
 	ASSERT (memory != NULL);
 	ASSERT (length > 0);
+	ASSERT (tag != NULL);
 
 	/* Dig out where the meta should be */
 	word = memory;
@@ -625,13 +642,12 @@ sec_realloc (Block *block, void *memory, size_t length)
 	
 	/* Validate that it's actually for real */
 	sec_check_guards (cell);
-	ASSERT (cell->allocated > 0);
-	ASSERT (cell->next == NULL);
-	ASSERT (cell->prev == NULL);
-	
+	ASSERT (cell->requested > 0);
+	ASSERT (cell->tag != NULL);
+
 	/* The amount of valid data */
-	valid = cell->allocated;
-	
+	valid = cell->requested;
+
 	/* How many words we actually want */
 	n_words = sec_size_to_words (length) + 2;
 
@@ -639,7 +655,7 @@ sec_realloc (Block *block, void *memory, size_t length)
 	if (n_words <= cell->n_words) {
 
 		/* TODO: No shrinking behavior yet */
-		cell->allocated = length;
+		cell->requested = length;
 		alloc = sec_cell_to_memory (cell);
 
 #ifdef WITH_VALGRIND
@@ -662,14 +678,14 @@ sec_realloc (Block *block, void *memory, size_t length)
 
 		/* See if we have a neighbor who can give us some memory */
 		other = sec_neighbor_after (block, cell);
-		if (!other || other->allocated != 0)
+		if (!other || other->requested != 0)
 			break;
 		
 		/* Eat the whole neighbor if not too big */
 		if (n_words - cell->n_words + WASTE >= other->n_words) {
 			cell->n_words += other->n_words;
 			sec_write_guards (cell);
-			sec_remove_cell_ring (&block->unused, other);
+			sec_remove_cell_ring (&block->unused_cells, other);
 			pool_free (other);
 
 		/* Steal from the neighbor */
@@ -683,18 +699,19 @@ sec_realloc (Block *block, void *memory, size_t length)
 	}
 	
 	if (cell->n_words >= n_words) {
-		cell->allocated = length;
+		cell->requested = length;
+		cell->tag = tag;
 		alloc = sec_cell_to_memory (cell);
-		
+
 #ifdef WITH_VALGRIND
 		VALGRIND_MAKE_MEM_DEFINED (alloc, length);
 #endif
 		
 		return sec_clear_memory (alloc, valid, length);
 	}
-	
+
 	/* That didn't work, try alloc/free */
-	alloc = sec_alloc (block, length);
+	alloc = sec_alloc (block, tag, length);
 	if (alloc) {
 		memcpy (alloc, memory, valid);
 		sec_free (block, memory);
@@ -726,15 +743,14 @@ sec_allocated (Block *block, void *memory)
 	cell = *word;
 	
 	sec_check_guards (cell);
-	ASSERT (cell->next == NULL);
-	ASSERT (cell->prev == NULL);
-	ASSERT (cell->allocated > 0);
-	
+	ASSERT (cell->requested > 0);
+	ASSERT (cell->tag != NULL);
+
 #ifdef WITH_VALGRIND
 	VALGRIND_MAKE_MEM_NOACCESS (word, sizeof (word_t));
 #endif
-	
-	return cell->allocated;
+
+	return cell->requested;
 }
 
 static void
@@ -757,15 +773,19 @@ sec_validate (Block *block)
 		sec_check_guards (cell);
 	
 		/* Is it an allocated block? */
-		if (cell->allocated > 0) {
-			ASSERT (cell->next == NULL);
-			ASSERT (cell->prev == NULL);
-			ASSERT (cell->allocated <= (cell->n_words - 2) * sizeof (word_t));
+		if (cell->requested > 0) {
+			ASSERT (cell->tag != NULL);
+			ASSERT (cell->next != NULL);
+			ASSERT (cell->prev != NULL);
+			ASSERT (cell->next->prev == cell);
+			ASSERT (cell->prev->next == cell);
+			ASSERT (cell->requested <= (cell->n_words - 2) * sizeof (word_t));
 		
 			/* An unused block */
 		} else {
-			ASSERT (cell->next);
-			ASSERT (cell->prev);
+			ASSERT (cell->tag == NULL);
+			ASSERT (cell->next != NULL);
+			ASSERT (cell->prev != NULL);
 			ASSERT (cell->next->prev == cell);
 			ASSERT (cell->prev->next == cell);
 		}
@@ -781,13 +801,15 @@ sec_validate (Block *block)
  */
 
 static void*
-sec_acquire_pages (size_t *sz)
+sec_acquire_pages (size_t *sz,
+                   const char *during_tag)
 {
 	void *pages;
 	unsigned long pgsize;
 	
 	ASSERT (sz);
 	ASSERT (*sz);
+	ASSERT (during_tag);
 
 	/* Make sure sz is a multiple of the page size */
 	pgsize = getpagesize ();
@@ -797,16 +819,16 @@ sec_acquire_pages (size_t *sz)
 	pages = mmap (0, *sz, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
 	if (pages == MAP_FAILED) {
 		if (lock_warning && egg_secure_warnings)
-			fprintf (stderr, "couldn't map %lu bytes of private memory: %s\n", 
-			         (unsigned long)*sz, strerror (errno));
+			fprintf (stderr, "couldn't map %lu bytes of memory (%s): %s\n",
+			         (unsigned long)*sz, during_tag, strerror (errno));
 		lock_warning = 0;
 		return NULL;
 	}
 	
 	if (mlock (pages, *sz) < 0) {
 		if (lock_warning && egg_secure_warnings && errno != EPERM) {
-			fprintf (stderr, "couldn't lock %lu bytes of private memory: %s\n", 
-			         (unsigned long)*sz, strerror (errno));
+			fprintf (stderr, "couldn't lock %lu bytes of memory (%s): %s\n",
+			         (unsigned long)*sz, during_tag, strerror (errno));
 			lock_warning = 0;
 		}
 		munmap (pages, *sz);
@@ -854,11 +876,14 @@ sec_release_pages (void *pages, size_t sz)
 static Block *all_blocks = NULL;
 
 static Block* 
-sec_block_create (size_t size)
+sec_block_create (size_t size,
+                  const char *during_tag)
 {
 	Block *block;
 	Cell *cell;
 
+	ASSERT (during_tag);
+
 #if FORCE_FALLBACK_MEMORY
 	/* We can force all all memory to be malloced */
 	return NULL;
@@ -878,7 +903,7 @@ sec_block_create (size_t size)
 	if (size < DEFAULT_BLOCK_SIZE)
 		size = DEFAULT_BLOCK_SIZE;
 		
-	block->words = sec_acquire_pages (&size);
+	block->words = sec_acquire_pages (&size, during_tag);
 	block->n_words = size / sizeof (word_t);
 	if (!block->words) {
 		pool_free (block);
@@ -893,10 +918,10 @@ sec_block_create (size_t size)
 	/* The first cell to allocate from */
 	cell->words = block->words;
 	cell->n_words = block->n_words;
-	cell->allocated = 0;
+	cell->requested = 0;
 	sec_write_guards (cell);
-	sec_insert_cell_ring (&block->unused, cell);
-	
+	sec_insert_cell_ring (&block->unused_cells, cell);
+
 	block->next = all_blocks;
 	all_blocks = block;
 	
@@ -911,7 +936,7 @@ sec_block_destroy (Block *block)
 
 	ASSERT (block);
 	ASSERT (block->words);
-	ASSERT (block->used == 0);
+	ASSERT (block->n_used == 0);
 	
 	/* Remove from the list */
 	for (at = &all_blocks, bl = *at; bl; at = &bl->next, bl = *at) {
@@ -923,11 +948,12 @@ sec_block_destroy (Block *block)
 	
 	/* Must have been found */
 	ASSERT (bl == block);
+	ASSERT (block->used_cells == NULL);
 
 	/* Release all the meta data cells */
-	while (block->unused) {
-		cell = block->unused;
-		sec_remove_cell_ring (&block->unused, cell);
+	while (block->unused_cells) {
+		cell = block->unused_cells;
+		sec_remove_cell_ring (&block->unused_cells, cell);
 		pool_free (cell);
 	}
 	
@@ -942,17 +968,16 @@ sec_block_destroy (Block *block)
  */
 
 void*
-egg_secure_alloc (size_t length)
-{
-	return egg_secure_alloc_full (length, GKR_SECURE_USE_FALLBACK);
-}
-
-void*
-egg_secure_alloc_full (size_t length, int flags)
+egg_secure_alloc_full (const char *tag,
+                       size_t length,
+                       int flags)
 {
 	Block *block;
 	void *memory = NULL;
-		
+
+	if (tag == NULL)
+		tag = "?";
+
 	if (length > 0xFFFFFFFF / 2) {
 		if (egg_secure_warnings)
 			fprintf (stderr, "tried to allocate an insane amount of memory: %lu\n", 
@@ -967,16 +992,16 @@ egg_secure_alloc_full (size_t length, int flags)
 	DO_LOCK ();
 	
 		for (block = all_blocks; block; block = block->next) {
-			memory = sec_alloc (block, length);
+			memory = sec_alloc (block, tag, length);
 			if (memory)
 				break;	
 		}
 	
 		/* None of the current blocks have space, allocate new */
 		if (!memory) {
-			block = sec_block_create (length);
+			block = sec_block_create (length, tag);
 			if (block)
-				memory = sec_alloc (block, length);
+				memory = sec_alloc (block, tag, length);
 		}
 		
 #ifdef WITH_VALGRIND
@@ -985,8 +1010,8 @@ egg_secure_alloc_full (size_t length, int flags)
 #endif
 	
 	DO_UNLOCK ();
-	
-	if (!memory && (flags & GKR_SECURE_USE_FALLBACK)) {
+
+	if (!memory && (flags & EGG_SECURE_USE_FALLBACK)) {
 		memory = egg_memory_fallback (NULL, length);
 		if (memory) /* Our returned memory is always zeroed */
 			memset (memory, 0, length);
@@ -999,19 +1024,19 @@ egg_secure_alloc_full (size_t length, int flags)
 }
 
 void*
-egg_secure_realloc (void *memory, size_t length)
-{
-	return egg_secure_realloc_full (memory, length, GKR_SECURE_USE_FALLBACK);
-}
-
-void*
-egg_secure_realloc_full (void *memory, size_t length, int flags)
+egg_secure_realloc_full (const char *tag,
+                         void *memory,
+                         size_t length,
+                         int flags)
 {
 	Block *block = NULL;
 	size_t previous = 0;
 	int donew = 0;
 	void *alloc = NULL;
-	
+
+	if (tag == NULL)
+		tag = "?";
+
 	if (length > 0xFFFFFFFF / 2) {
 		if (egg_secure_warnings)
 			fprintf (stderr, "tried to allocate an insane amount of memory: %lu\n", 
@@ -1020,7 +1045,7 @@ egg_secure_realloc_full (void *memory, size_t length, int flags)
 	}
 	
 	if (memory == NULL)
-		return egg_secure_alloc_full (length, flags);
+		return egg_secure_alloc_full (tag, length, flags);
 	if (!length) {
 		egg_secure_free_full (memory, flags);
 		return NULL;
@@ -1038,8 +1063,8 @@ egg_secure_realloc_full (void *memory, size_t length, int flags)
 				VALGRIND_FREELIKE_BLOCK (memory, sizeof (word_t));
 #endif
 
-				alloc = sec_realloc (block, memory, length);
-				
+				alloc = sec_realloc (block, tag, memory, length);
+
 #ifdef WITH_VALGRIND
 				/* Now tell valgrind about either the new block or old one */
 				VALGRIND_MALLOCLIKE_BLOCK (alloc ? alloc : memory, 
@@ -1054,13 +1079,13 @@ egg_secure_realloc_full (void *memory, size_t length, int flags)
 		if (block && !alloc)
 			donew = 1;
 
-		if (block && block->used == 0)
+		if (block && block->n_used == 0)
 			sec_block_destroy (block);
 		
 	DO_UNLOCK ();		
 	
 	if (!block) {
-		if ((flags & GKR_SECURE_USE_FALLBACK)) {
+		if ((flags & EGG_SECURE_USE_FALLBACK)) {
 			/* 
 			 * In this case we can't zero the returned memory, 
 			 * because we don't know what the block size was.
@@ -1074,9 +1099,9 @@ egg_secure_realloc_full (void *memory, size_t length, int flags)
 			return NULL;
 		}
 	}
-		
+
 	if (donew) {
-		alloc = egg_secure_alloc_full (length, flags);
+		alloc = egg_secure_alloc_full (tag, length, flags);
 		if (alloc) {
 			memcpy (alloc, memory, previous);
 			egg_secure_free_full (memory, flags);
@@ -1092,7 +1117,7 @@ egg_secure_realloc_full (void *memory, size_t length, int flags)
 void
 egg_secure_free (void *memory)
 {
-	egg_secure_free_full (memory, GKR_SECURE_USE_FALLBACK);
+	egg_secure_free_full (memory, EGG_SECURE_USE_FALLBACK);
 }
 
 void
@@ -1119,14 +1144,14 @@ egg_secure_free_full (void *memory, int flags)
 
 		if (block != NULL) {
 			sec_free (block, memory);
-			if (block->used == 0)
+			if (block->n_used == 0)
 				sec_block_destroy (block);
 		}
 			
 	DO_UNLOCK ();
 	
 	if (!block) {
-		if ((flags & GKR_SECURE_USE_FALLBACK)) {
+		if ((flags & EGG_SECURE_USE_FALLBACK)) {
 			egg_memory_fallback (memory, 0);
 		} else {
 			if (egg_secure_warnings)
@@ -1168,35 +1193,87 @@ egg_secure_validate (void)
 	DO_UNLOCK ();
 }
 
-void
-egg_secure_dump_blocks (void)
+
+static egg_secure_rec *
+records_for_ring (Cell *cell_ring,
+                  egg_secure_rec *records,
+                  unsigned int *count,
+                  unsigned int *total)
 {
+	egg_secure_rec *new_rec;
+	unsigned int allocated = *count;
+	Cell *cell;
+
+	cell = cell_ring;
+	do {
+		if (*count >= allocated) {
+			new_rec = realloc (records, sizeof (egg_secure_rec) * (allocated + 32));
+			if (new_rec == NULL) {
+				*count = 0;
+				free (records);
+				return NULL;
+			} else {
+				records = new_rec;
+				allocated += 32;
+			}
+		}
+
+		if (cell != NULL) {
+			records[*count].request_length = cell->requested;
+			records[*count].block_length = cell->n_words * sizeof (word_t);
+			records[*count].tag = cell->tag;
+			(*count)++;
+			(*total) += cell->n_words;
+			cell = cell->next;
+		}
+	} while (cell != NULL && cell != cell_ring);
+
+	return records;
+}
+
+egg_secure_rec *
+egg_secure_records (unsigned int *count)
+{
+	egg_secure_rec *records = NULL;
 	Block *block = NULL;
+	unsigned int total;
+
+	*count = 0;
 
 	DO_LOCK ();
-	
-		/* Find out where it belongs to */
-		for (block = all_blocks; block; block = block->next) {
-			fprintf (stderr, "----------------------------------------------------\n");
-			fprintf (stderr, "  BLOCK at: 0x%08lx  len: %lu\n", (unsigned long)block, 
-			         (unsigned long)block->n_words * sizeof (word_t));
-			fprintf (stderr, "\n");
+
+		for (block = all_blocks; block != NULL; block = block->next) {
+			total = 0;
+
+			records = records_for_ring (block->unused_cells, records, count, &total);
+			if (records == NULL)
+				break;
+			records = records_for_ring (block->used_cells, records, count, &total);
+			if (records == NULL)
+				break;
+
+			/* Make sure this actualy accounts for all memory */
+			ASSERT (total == block->n_words);
 		}
-		
+
 	DO_UNLOCK ();
+
+	return records;
 }
 
 char*
-egg_secure_strdup (const char *str)
+egg_secure_strdup_full (const char *tag,
+                        const char *str,
+                        int options)
 {
 	size_t len;
 	char *res;
-	
+
 	if (!str)
 		return NULL;
-	
+
 	len = strlen (str) + 1;	
-	res = (char*)egg_secure_alloc (len);
+	res = (char *)egg_secure_alloc_full (tag, len, options);
 	strcpy (res, str);
 	return res;
 }
@@ -1235,5 +1312,5 @@ egg_secure_strfree (char *str)
 	 */
 	
 	egg_secure_strclear (str);
-	egg_secure_free_full (str, GKR_SECURE_USE_FALLBACK);
+	egg_secure_free_full (str, EGG_SECURE_USE_FALLBACK);
 }
diff --git a/egg/egg-secure-memory.h b/egg/egg-secure-memory.h
index 85ce1f6..682811d 100644
--- a/egg/egg-secure-memory.h
+++ b/egg/egg-secure-memory.h
@@ -70,15 +70,22 @@ extern void*  egg_memory_fallback (void *p, size_t length);
  * Allocations return NULL on failure.
  */ 
  
-#define GKR_SECURE_USE_FALLBACK     0x0001
+#define EGG_SECURE_USE_FALLBACK     0x0001
 
-void*  egg_secure_alloc        (size_t length);
+#define EGG_SECURE_DECLARE(tag) \
+	static inline void* egg_secure_alloc (size_t length) { \
+		return egg_secure_alloc_full (G_STRINGIFY (tag), length, EGG_SECURE_USE_FALLBACK); \
+	} \
+	static inline void* egg_secure_realloc (void *p, size_t length) { \
+		return egg_secure_realloc_full (G_STRINGIFY (tag), p, length, EGG_SECURE_USE_FALLBACK); \
+	} \
+	static inline void* egg_secure_strdup (const char *str) { \
+		return egg_secure_strdup_full (G_STRINGIFY (tag), str, EGG_SECURE_USE_FALLBACK); \
+	}
 
-void*  egg_secure_alloc_full   (size_t length, int flags);
+void*  egg_secure_alloc_full   (const char *tag, size_t length, int options);
 
-void*  egg_secure_realloc      (void *p, size_t length);
-
-void*  egg_secure_realloc_full (void *p, size_t length, int fallback);
+void*  egg_secure_realloc_full (const char *tag, void *p, size_t length, int options);
 
 void   egg_secure_free         (void* p); 
 
@@ -90,12 +97,18 @@ int    egg_secure_check        (const void* p);
 
 void   egg_secure_validate     (void);
 
-void   egg_secure_dump_blocks  (void);
-
-char*  egg_secure_strdup       (const char *str);
+char*  egg_secure_strdup_full  (const char *tag, const char *str, int options);
 
 void   egg_secure_strclear     (char *str);
 
 void   egg_secure_strfree      (char *str);
 
+typedef struct {
+	const char *tag;
+	size_t request_length;
+	size_t block_length;
+} egg_secure_rec;
+
+egg_secure_rec *   egg_secure_records    (unsigned int *count);
+
 #endif /* EGG_SECURE_MEMORY_H */
diff --git a/egg/egg-symkey.c b/egg/egg-symkey.c
index a69100f..04d6a91 100644
--- a/egg/egg-symkey.c
+++ b/egg/egg-symkey.c
@@ -26,6 +26,8 @@
 #include "egg-secure-memory.h"
 #include "egg-symkey.h"
 
+EGG_SECURE_DECLARE (symkey);
+
 /* -----------------------------------------------------------------------------
  * QUARKS
  */
diff --git a/egg/tests/test-secmem.c b/egg/tests/test-secmem.c
index b5ee465..20beec9 100644
--- a/egg/tests/test-secmem.c
+++ b/egg/tests/test-secmem.c
@@ -37,7 +37,9 @@ EGG_SECURE_GLIB_DEFINITIONS ();
 /* Declared in egg-secure-memory.c */
 extern int egg_secure_warnings;
 
-/*
+EGG_SECURE_DECLARE (tests);
+
+/* 
  * Each test looks like (on one line):
  *     void unit_test_xxxxx (CuTest* cu)
  *
@@ -69,7 +71,7 @@ test_alloc_free (void)
 	gpointer p;
 	gboolean ret;
 
-	p = egg_secure_alloc_full (512, 0);
+	p = egg_secure_alloc_full ("tests", 512, 0);
 	g_assert (p != NULL);
 	g_assert_cmpint (G_MAXSIZE, ==, find_non_zero (p, 512));
 
@@ -87,12 +89,12 @@ test_realloc_across (void)
 	gpointer p, p2;
 
 	/* Tiny allocation */
-	p = egg_secure_realloc_full (NULL, 1088, 0);
+	p = egg_secure_realloc_full ("tests", NULL, 1088, 0);
 	g_assert (p != NULL);
 	g_assert_cmpint (G_MAXSIZE, ==, find_non_zero (p, 1088));
 
-	/* Reallocate to a large one, will have to have changed blocks */
-	p2 = egg_secure_realloc_full (p, 16200, 0);
+	/* Reallocate to a large one, will have to have changed blocks */	
+	p2 = egg_secure_realloc_full ("tests", p, 16200, 0);
 	g_assert (p2 != NULL);
 	g_assert_cmpint (G_MAXSIZE, ==, find_non_zero (p2, 16200));
 }
@@ -103,13 +105,13 @@ test_alloc_two (void)
 	gpointer p, p2;
 	gboolean ret;
 
-	p2 = egg_secure_alloc_full (4, 0);
+	p2 = egg_secure_alloc_full ("tests", 4, 0);
 	g_assert (p2 != NULL);
 	g_assert_cmpint (G_MAXSIZE, ==, find_non_zero (p2, 4));
 
 	memset (p2, 0x67, 4);
 
-	p = egg_secure_alloc_full (16200, 0);
+	p = egg_secure_alloc_full ("tests", 16200, 0);
 	g_assert (p != NULL);
 	g_assert_cmpint (G_MAXSIZE, ==, find_non_zero (p, 16200));
 
@@ -131,19 +133,19 @@ test_realloc (void)
 
 	len = strlen (str) + 1;
 
-	p = egg_secure_realloc_full (NULL, len, 0);
+	p = egg_secure_realloc_full ("tests", NULL, len, 0);
 	g_assert (p != NULL);
 	g_assert_cmpint (G_MAXSIZE, ==, find_non_zero (p, len));
 
 	strcpy ((gchar*)p, str);
 
-	p2 = egg_secure_realloc_full (p, 512, 0);
+	p2 = egg_secure_realloc_full ("tests", p, 512, 0);
 	g_assert (p2 != NULL);
 	g_assert_cmpint (G_MAXSIZE, ==, find_non_zero (((gchar*)p2) + len, 512 - len));
 
 	g_assert (strcmp (p2, str) == 0);
 
-	p = egg_secure_realloc_full (p2, 0, 0);
+	p = egg_secure_realloc_full ("tests", p2, 0, 0);
 	g_assert (p == NULL);
 }
 
@@ -220,7 +222,7 @@ test_clear (void)
 {
 	gpointer p;
 
-	p = egg_secure_alloc_full (188, 0);
+	p = egg_secure_alloc_full ("tests", 188, 0);
 	g_assert (p != NULL);
 	memset (p, 0x89, 188);
 	g_assert (memchr (p, 0x89, 188) == p);
diff --git a/gcr/gcr-parser.c b/gcr/gcr-parser.c
index e4585b7..ade1ae9 100644
--- a/gcr/gcr-parser.c
+++ b/gcr/gcr-parser.c
@@ -164,6 +164,8 @@ static const ParserFormat parser_normal[];
 static const ParserFormat parser_formats[];
 static ParserFormat* parser_format_lookup (gint format_id);
 
+EGG_SECURE_DECLARE (parser);
+
 /* -----------------------------------------------------------------------------
  * QUARK DEFINITIONS
  */
diff --git a/pkcs11/gkm/gkm-aes-key.c b/pkcs11/gkm/gkm-aes-key.c
index 71f9e08..d80d15d 100644
--- a/pkcs11/gkm/gkm-aes-key.c
+++ b/pkcs11/gkm/gkm-aes-key.c
@@ -47,6 +47,8 @@ static const CK_MECHANISM_TYPE GKM_AES_MECHANISMS[] = {
 	CKM_G_HKDF_SHA256_DERIVE
 };
 
+EGG_SECURE_DECLARE (aes_key);
+
 /* -----------------------------------------------------------------------------
  * INTERNAL
  */
diff --git a/pkcs11/gkm/gkm-aes-mechanism.c b/pkcs11/gkm/gkm-aes-mechanism.c
index 9742450..898aa41 100644
--- a/pkcs11/gkm/gkm-aes-mechanism.c
+++ b/pkcs11/gkm/gkm-aes-mechanism.c
@@ -31,6 +31,8 @@
 #include "egg/egg-padding.h"
 #include "egg/egg-secure-memory.h"
 
+EGG_SECURE_DECLARE (aes_mechanism);
+
 static CK_RV
 retrieve_length (GkmSession *session, GkmObject *wrapped, gsize *length)
 {
diff --git a/pkcs11/gkm/gkm-data-der.c b/pkcs11/gkm/gkm-data-der.c
index 8e60651..0637202 100644
--- a/pkcs11/gkm/gkm-data-der.c
+++ b/pkcs11/gkm/gkm-data-der.c
@@ -36,6 +36,8 @@
 #include <glib.h>
 #include <gcrypt.h>
 
+EGG_SECURE_DECLARE (data_der);
+
 /* -----------------------------------------------------------------------------
  * QUARKS
  */
diff --git a/pkcs11/gkm/gkm-dh-mechanism.c b/pkcs11/gkm/gkm-dh-mechanism.c
index 446defb..3f80be1 100644
--- a/pkcs11/gkm/gkm-dh-mechanism.c
+++ b/pkcs11/gkm/gkm-dh-mechanism.c
@@ -32,6 +32,8 @@
 #include "egg/egg-libgcrypt.h"
 #include "egg/egg-secure-memory.h"
 
+EGG_SECURE_DECLARE (dh_mechanism);
+
 static GkmObject*
 create_dh_object (GkmSession *session, GkmTransaction *transaction, CK_OBJECT_CLASS klass,
                   CK_ATTRIBUTE_PTR value, CK_ATTRIBUTE_PTR prime, CK_ATTRIBUTE_PTR base,
diff --git a/pkcs11/gkm/gkm-generic-key.c b/pkcs11/gkm/gkm-generic-key.c
index 834f152..6ea5fdb 100644
--- a/pkcs11/gkm/gkm-generic-key.c
+++ b/pkcs11/gkm/gkm-generic-key.c
@@ -46,6 +46,8 @@ static const CK_MECHANISM_TYPE GKM_GENERIC_MECHANISMS[] = {
 	CKM_G_HKDF_SHA256_DERIVE
 };
 
+EGG_SECURE_DECLARE (generic_key);
+
 /* -----------------------------------------------------------------------------
  * INTERNAL
  */
diff --git a/pkcs11/gkm/gkm-hkdf-mechanism.c b/pkcs11/gkm/gkm-hkdf-mechanism.c
index bbca94f..be37d1c 100644
--- a/pkcs11/gkm/gkm-hkdf-mechanism.c
+++ b/pkcs11/gkm/gkm-hkdf-mechanism.c
@@ -32,6 +32,8 @@
 #include "egg/egg-libgcrypt.h"
 #include "egg/egg-secure-memory.h"
 
+EGG_SECURE_DECLARE (hkdf_mechanism);
+
 CK_RV
 gkm_hkdf_mechanism_derive (GkmSession *session, const char *algo,
                            CK_MECHANISM_PTR mech, GkmObject *base,
diff --git a/pkcs11/gkm/gkm-null-mechanism.c b/pkcs11/gkm/gkm-null-mechanism.c
index d1016bf..b08360c 100644
--- a/pkcs11/gkm/gkm-null-mechanism.c
+++ b/pkcs11/gkm/gkm-null-mechanism.c
@@ -30,6 +30,8 @@
 #include "egg/egg-libgcrypt.h"
 #include "egg/egg-secure-memory.h"
 
+EGG_SECURE_DECLARE (null_mechanism);
+
 static CK_RV
 retrieve_length (GkmSession *session, GkmObject *wrapped, gsize *length)
 {
diff --git a/pkcs11/gkm/gkm-secret.c b/pkcs11/gkm/gkm-secret.c
index ca9a36e..0951a55 100644
--- a/pkcs11/gkm/gkm-secret.c
+++ b/pkcs11/gkm/gkm-secret.c
@@ -35,6 +35,8 @@ struct _GkmSecret {
 
 G_DEFINE_TYPE (GkmSecret, gkm_secret, G_TYPE_OBJECT);
 
+EGG_SECURE_DECLARE (secret);
+
 /* -----------------------------------------------------------------------------
  * OBJECT
  */
diff --git a/pkcs11/gnome2-store/gkm-gnome2-file.c b/pkcs11/gnome2-store/gkm-gnome2-file.c
index 4dafd5d..f933b76 100644
--- a/pkcs11/gnome2-store/gkm-gnome2-file.c
+++ b/pkcs11/gnome2-store/gkm-gnome2-file.c
@@ -90,6 +90,8 @@ typedef GkmDataResult (*BlockFunc) (guint block, EggBuffer *buffer, GkmSecret *l
 
 #define UNUSED_VALUE  GUINT_TO_POINTER (1)
 
+EGG_SECURE_DECLARE (data_file);
+
 /* -----------------------------------------------------------------------------
  * HELPERS
  */
diff --git a/pkcs11/secret-store/gkm-secret-binary.c b/pkcs11/secret-store/gkm-secret-binary.c
index 29cd9cd..9468c86 100644
--- a/pkcs11/secret-store/gkm-secret-binary.c
+++ b/pkcs11/secret-store/gkm-secret-binary.c
@@ -52,6 +52,8 @@
 #include <string.h>
 #include <unistd.h>
 
+EGG_SECURE_DECLARE (secret_binary);
+
 /* -----------------------------------------------------------------------------
  * DECLARATIONS
  */
diff --git a/pkcs11/wrap-layer/gkm-wrap-login.c b/pkcs11/wrap-layer/gkm-wrap-login.c
index d17b7ff..5b563c5 100644
--- a/pkcs11/wrap-layer/gkm-wrap-login.c
+++ b/pkcs11/wrap-layer/gkm-wrap-login.c
@@ -39,6 +39,8 @@
 /* Holds failed unlock password, accessed atomically */
 static gpointer unlock_failure = NULL;
 
+EGG_SECURE_DECLARE (wrap_login);
+
 void
 gkm_wrap_layer_mark_login_unlock_success (void)
 {
diff --git a/ui/gku-prompt-tool.c b/ui/gku-prompt-tool.c
index c719b23..e8bf93a 100644
--- a/ui/gku-prompt-tool.c
+++ b/ui/gku-prompt-tool.c
@@ -67,6 +67,8 @@ static gsize n_the_key = 0;
 * @short_description: Displays a propmt for 3rd party programs (ssh, gnupg)
 **/
 
+EGG_SECURE_DECLARE (prompt_tool);
+
 /* ------------------------------------------------------------------------------ */
 
 /**
diff --git a/ui/gku-prompt-util.c b/ui/gku-prompt-util.c
index 2d16359..27aa1f2 100644
--- a/ui/gku-prompt-util.c
+++ b/ui/gku-prompt-util.c
@@ -29,6 +29,8 @@
 #include "egg/egg-padding.h"
 #include "egg/egg-secure-memory.h"
 
+EGG_SECURE_DECLARE (prompt);
+
 void
 gku_prompt_util_encode_mpi (GKeyFile *key_file, const gchar *section,
                             const gchar *field, gcry_mpi_t mpi)
diff --git a/ui/gku-prompt.c b/ui/gku-prompt.c
index abcb82f..dc1a610 100644
--- a/ui/gku-prompt.c
+++ b/ui/gku-prompt.c
@@ -96,6 +96,8 @@ const struct { const gchar *section; const gchar *name; } SOFT_RESET[] = {
 	{ "details", "expanded" },
 };
 
+EGG_SECURE_DECLARE (prompt);
+
 /* -----------------------------------------------------------------------------
  * INTERNAL
  */
diff --git a/ui/tests/test-util.c b/ui/tests/test-util.c
index 0cb9212..65e471d 100644
--- a/ui/tests/test-util.c
+++ b/ui/tests/test-util.c
@@ -35,6 +35,8 @@
 
 EGG_SECURE_GLIB_DEFINITIONS ();
 
+EGG_SECURE_DECLARE (test_util);
+
 typedef struct {
 	GKeyFile *key_file;
 } Test;



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]