gnome-keyring r1562 - in trunk: . egg



Author: nnielsen
Date: Fri Feb 13 04:21:00 2009
New Revision: 1562
URL: http://svn.gnome.org/viewvc/gnome-keyring?rev=1562&view=rev

Log:
Completely overhaul the secure memory allocator.

Don't store metadata inside the secure memory area. Add memory guards, so that we can see if/when memory is incorrectly overflown.

Modified:
   trunk/ChangeLog
   trunk/egg/egg-secure-memory.c

Modified: trunk/egg/egg-secure-memory.c
==============================================================================
--- trunk/egg/egg-secure-memory.c	(original)
+++ trunk/egg/egg-secure-memory.c	Fri Feb 13 04:21:00 2009
@@ -72,318 +72,552 @@
 #define DO_UNLOCK() \
 	egg_memory_unlock ();
 
-#define MEM_ALIGN	(sizeof(void*) > sizeof(long) ? sizeof(void*) : sizeof(long))
+static int lock_warning = 1;
 
-/* -----------------------------------------------------------------------------
- * BLOCK SUBALLOCATION
+
+/* 
+ * We allocate all memory in units of sizeof(void*). This 
+ * is our definition of 'word'.
  */
+typedef void* word_t;
 
-/* suba - sub-allocate memory from larger chunk of memory
- * Copyright (c) 2003 Michael B. Allen <mba2000 ioplex.com>
- *
- * The MIT License
- * 
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- * 
- * The above copyright notice and this permission notice shall be included
- * in all copies or substantial portions of the Software.
- * 
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
- * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- * OTHER DEALINGS IN THE SOFTWARE.
+/* The amount of extra words we can allocate */ 
+#define WASTE   4
+
+/* 
+ * Track allocated memory or a free block. This structure is not stored 
+ * in the secure memory area. It is allocated from a pool of other 
+ * memory. See meta_pool_xxx ().
  */
+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 */
+} Cell;
 
-typedef size_t ref_t;  /* suba offset from start of memory to object */
+/* 
+ * 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 */ 
+} Block;
 
-#define SUBA_MAGIC "\xFF\x15\x15\x15SUBA"
-#define CELL_MAGIC 0x7777CE11
-#define ALIGN(s)  ((((s) / MEM_ALIGN) + (((s) % MEM_ALIGN) ? 1 : 0)) * MEM_ALIGN)
-#define POFF (ALIGN(sizeof(size_t)) + ALIGN(sizeof(unsigned int)))
-#define C2P(c) ((char *)(c) + POFF)
-#define P2C(p) ((struct cell *)((char *)(p) - POFF))
-#define ISADJ(c1,c2) ((struct cell *)(C2P(c1) + (c1)->size) == (struct cell *)(c2))
-#define MINCELL 32
-#define HDRSIZ (ALIGN (sizeof *suba))
-
-struct allocator {
-	unsigned char magic[8];                /* suba header identifier */
-	ref_t tail;                 /* offset to first cell in free list */
-	size_t size;                        /* total size of memory area */
-	size_t alloc_total;  /* total bytes utilized from this allocator */
-	size_t free_total;   /* total bytes released from this allocator */
-	size_t size_total;  /* total bytes requested from this allocator */
-};
-
-struct cell {
-	size_t size;
-	unsigned int magic;
-	ref_t next; /* reference to next cell in free list */
-};
+/* -----------------------------------------------------------------------------
+ * UNUSED STACK
+ */
+
+static inline void
+unused_push (void **stack, void *ptr)
+{
+	ASSERT (ptr);
+	ASSERT (stack);
+	*((void**)ptr) = *stack;
+	*stack = ptr;
+}
 
 static inline void*
-suba_addr (const struct allocator *suba, const ref_t ref)
+unused_pop (void **stack)
 {
-	ASSERT (suba);
-	ASSERT (ref >= HDRSIZ);
-	ASSERT (ref < suba->size);
-	return (char*)suba + ref;
+	void *ptr;
+	ASSERT (stack);
+	ptr = *stack;
+	*stack = *(void**)ptr;
+	return ptr;
+	
 }
 
-static inline ref_t
-suba_ref (const struct allocator *suba, const void *ptr)
+static inline void*
+unused_peek (void **stack)
 {
-	ASSERT (suba);
-	ASSERT (ptr);
-	ASSERT ((char*)ptr >= (char*)suba + HDRSIZ);
-	ASSERT ((char*)ptr < (char*)suba + suba->size);
-	return (char*)ptr - (char*)suba;
+	ASSERT (stack);
+	return *stack; 
 }
 
-static struct allocator *
-suba_init (void *mem, size_t size)
+/* -----------------------------------------------------------------------------
+ * POOL META DATA ALLOCATION
+ * 
+ * A pool for memory meta data. We allocate fixed size blocks. There are actually 
+ * two different structures stored in this pool: Cell and Block. Cell is allocated
+ * way more often, and is bigger so we just allocate that size for both.
+ */
+
+/* Pool allocates this data type */
+typedef struct _Item {
+	union {
+		Cell cell;
+		Block block;
+	};
+} Item;
+
+typedef struct _Pool {
+	struct _Pool *next;    /* Next pool in list */
+	size_t length;         /* Length in bytes of the pool */
+	size_t used;           /* Number of cells used in pool */
+	void *unused;          /* Unused stack of unused stuff */
+	size_t n_items;        /* Total number of items in pool */
+	Item items[1];         /* Actual items hang off here */
+} Pool;
+
+static Pool *all_pools = NULL;
+
+static void*
+pool_alloc (void)
 {
-	struct allocator *suba = mem;
-	struct cell *c;
+	Pool *pool;
+	void *pages;
+	size_t len, i;
+	
+	/* A pool with an available item */
+	for (pool = all_pools; pool; pool = pool->next) {
+		if (unused_peek (&pool->unused))
+			break;
+	}
+	
+	/* Create a new pool */
+	if (pool == NULL) {
+		len = getpagesize () * 2;
+		pages = mmap (0, len, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
+		if (pages == MAP_FAILED)
+			return NULL;
 
-	ASSERT (mem != NULL);
-	ASSERT (size > (HDRSIZ + POFF));
-	ASSERT (ALIGN (sizeof (struct cell)) <= MINCELL);
+		/* Fill in the block header, and inlude in block list */
+		pool = pages;
+		pool->next = all_pools;
+		all_pools = pool;
+		pool->length = len;
+		pool->used = 0;
+		pool->unused = NULL;
+
+		/* Fill block with unused items */
+		pool->n_items = (len - sizeof (Pool)) / sizeof (Item);
+		for (i = 0; i < pool->n_items; ++i)
+			unused_push (&pool->unused, pool->items + i);
+	}
+
+	++pool->used;
+	ASSERT (unused_peek (&pool->unused));
+	return memset (unused_pop (&pool->unused), 0, sizeof (Item));
+}
+
+static void
+pool_free (void* item)
+{
+	Pool *pool, **at;
+	char *ptr, *beg, *end;
+	
+	ptr = item;
+	
+	/* Find which block this one belongs to */
+	for (at = &all_pools, pool = *at; pool; at = &pool->next, pool = *at) {
+		beg = (char*)pool->items;
+		end = (char*)pool + pool->length - sizeof (Item);
+		if (ptr >= beg && ptr <= end) {
+			ASSERT ((ptr - beg) % sizeof (Item) == 0);
+			break;
+		}
+	}
+	
+	/* Otherwise invalid meta */
+	ASSERT (pool && *at);
+	ASSERT (pool->used > 0);
+	
+	/* No more meta cells used in this block, remove from list, destroy */
+	if (pool->used == 1) {
+		*at = pool->next;
+		munmap (pool, pool->length);
+		return;
+	}
+	
+	--pool->used;
+	memset (item, 0xCD, sizeof (Item));
+	unused_push (&pool->unused, item);
+}
 
-	memset(suba, 0, HDRSIZ);
-	memcpy(suba->magic, SUBA_MAGIC, 8);
-	suba->tail = HDRSIZ;
-	suba->size = size;
+static int
+pool_valid (void* item)
+{
+	Pool *pool;
+	char *ptr, *beg, *end;
+	
+	ptr = item;
+	
+	/* Find which block this one belongs to */
+	for (pool = all_pools; pool; pool = pool->next) {
+		beg = (char*)pool->items;
+		end = (char*)pool + pool->length - sizeof (Item);
+		if (ptr >= beg && ptr <= end) 
+			return (pool->used && (ptr - beg) % sizeof (Item) == 0);
+	}
+	
+	return 0;
+}
 
-	c = suba_addr (suba, HDRSIZ);
-	c->size = size - (HDRSIZ + POFF);
-	c->next = suba->tail;
+/* -----------------------------------------------------------------------------
+ * SEC ALLOCATION
+ * 
+ * Each memory cell begins and ends with a pointer to its metadata. 
+ * 
+ */
 
-	return suba;
+static inline size_t
+sec_size_to_words (size_t length)
+{
+	return (length % sizeof (void*) ? 1 : 0) + (length / sizeof (void*));
 }
 
-static void *
-suba_alloc(struct allocator *suba, size_t size)
+static inline void
+sec_write_guards (Cell *cell)
 {
-	struct cell *c1, *c2, *c3;
-	size_t s = size;
+	((void**)cell->words)[0] = (void*)cell;
+	((void**)cell->words)[cell->n_words - 1] = (void*)cell;
+}
 
-	size = size < MINCELL ? MINCELL : ALIGN (size);
+static inline void
+sec_check_guards (Cell *cell)
+{
+	ASSERT(((void**)cell->words)[0] == (void*)cell);
+	ASSERT(((void**)cell->words)[cell->n_words - 1] == (void*)cell);	
+}
 
-	c2 = suba_addr (suba, suba->tail);
-	for ( ;; ) {
-		c1 = c2;
-		c2 = suba_addr (suba, c1->next);
-		if (c2->size >= size) 
-			break;       /* found a cell large enough */
-		if (c1->next == suba->tail) 
-			return NULL;
+static void
+sec_insert_cell_ring (Cell **ring, Cell *cell)
+{
+	ASSERT (ring);
+	ASSERT (cell);
+	ASSERT (cell != *ring);
+	ASSERT (cell->next == NULL);
+	ASSERT (cell->prev == NULL);
+	
+	/* Insert back into the mix of available memory */ 
+	if (*ring) { 
+		cell->next = (*ring)->next;
+		cell->prev = (*ring)->prev;
+		cell->next->prev = cell;
+		cell->prev->next = cell;
+	} else {
+		cell->next = cell;
+		cell->prev = cell;
 	}
+	
+	*ring = cell;
+}
+
+static void
+sec_remove_cell_ring (Cell **ring, Cell *cell)
+{
+	ASSERT (ring);
+	ASSERT (*ring);
+	ASSERT (cell->next);
+	ASSERT (cell->prev);
+	
+	if (cell == *ring) {
+		/* The last meta? */
+		if (cell->next == cell) {
+			ASSERT (cell->prev == cell);
+			*ring = NULL;
 
-	if ((c2->size - size) > MINCELL) {
-									/* split new cell */
-		c3 = (struct cell *)(C2P(c2) + size);
-		c3->size = c2->size - (size + POFF);
-		if (c1 == c2) {
-			c1 = c3;
+		/* Just pointing to this meta */
 		} else {
-			c3->next = c2->next;
+			ASSERT (cell->prev != cell);
+			*ring = cell->next;
 		}
-		c1->next = suba_ref (suba, c3);
-		c2->size = size;
-		if (c2 == suba_addr (suba, suba->tail))
-			suba->tail = suba_ref (suba, c3);
-	} else if (c1->next == suba->tail) {
-                /* never use the last cell! */
-	} else {                   
-		/* use the entire cell */
-		c1->next = c2->next;
 	}
 
-	suba->alloc_total += POFF + c2->size;
-	suba->size_total += s;
-	
-	c2->magic = CELL_MAGIC;
-	DEBUG_ALLOC ("gkr-secure-memory: allocated ", (unsigned long)size);
-
-	/* TODO: Fix suba, so always allocates zero */
-	memset (C2P(c2), 0, c2->size);
+	cell->next->prev = cell->prev;
+	cell->prev->next = cell->next;
+	cell->next = cell->prev = NULL;
 	
-	return C2P(c2);
+	ASSERT (*ring != cell);
 }
 
-static void
-suba_free(void *suba0, void *ptr)
+static inline void*
+sec_cell_to_memory (Cell *cell)
 {
-	struct allocator *suba = suba0;
-	struct cell *c1, *c2, *c3;
-	volatile char *vp;
-	size_t len;
-	ref_t ref;
-	int j1, j2;
+	return cell->words + 1;
+}
 
-	if (!ptr) 
-		return;
+static inline int
+sec_is_valid_word (Block *block, word_t *word)
+{
+	return (word >= block->words && word < block->words + block->n_words);
+}
 
-       	c1 = suba_addr (suba, suba->tail);
+static inline void*
+sec_clear_memory (void *memory, size_t from, size_t to)
+{
+	ASSERT (from <= to);
+	memset ((char*)memory + from, 0, to - from);
+	return memory;
+}
 
-	/* Find out what cell we're talking about */
-	c2 = P2C(ptr);
-	ref = suba_ref (suba, c2);
-	if (c2->magic != CELL_MAGIC) {
-		ASSERT(0 && "invalid memory pointer passed to gkr-secure-memory");
-		return;
-	}
-		 
-	/* Clear out memory */
-        vp = (volatile char*)ptr;
-       	len = c2->size;
-        while (len) { 
-        	*vp = 0xaa;
-        	vp++;
-        	len--; 
-        } 
+static Cell*
+sec_neighbor_before (Block *block, Cell *cell)
+{
+	word_t *word;
+	
+	ASSERT (cell);
+	ASSERT (block);
+	
+	word = cell->words - 1;
+	if (!sec_is_valid_word (block, word))
+		return NULL;
 
-	suba->free_total += POFF + c2->size;
-	suba->alloc_total -= (POFF + c2->size);
+	cell = *word;
+	sec_check_guards (cell);
+	return cell;
+}
 
-	c2->magic = 0;
-	DEBUG_ALLOC ("gkr-secure-memory: freed ", (unsigned long)c2->size);
+static Cell* 
+sec_neighbor_after (Block *block, Cell *cell)
+{
+	word_t *word;
 	
-	/* splice the cell back into the list */
-	if (c2 > c1) {           /* append to end of list */
-		if (ISADJ(c1,c2)) {    /* join with last cell */
-			c1->size += POFF + c2->size;
-			return;
-		}
-		c2->next = c1->next;
-		suba->tail = c1->next = ref;
-		return;
-	}
+	ASSERT (cell);
+	ASSERT (block);
+	
+	word = cell->words + cell->n_words;
+	if (!sec_is_valid_word (block, word))
+		return NULL;
 
-	while (c1->next < ref) /* find insertion point */
-		c1 = suba_addr (suba, c1->next);
-	c3 = suba_addr (suba, c1->next);
-
-	j1 = ISADJ(c1,c2); /* c1 and c2 need to be joined */
-	j2 = ISADJ(c2,c3); /* c2 and c3 need to be joined */
-
-	if (j1) {
-		if (j2) {  /* splice all three cells together */
-			if (suba_ref (suba, c3) == suba->tail)
-				suba->tail = suba_ref (suba, c1);
-			c1->next = c3->next;
-			c1->size += POFF + c3->size;
-		}
-		c1->size += POFF + c2->size;
-	} else {
-		if (j2) {
-			if (suba_ref (suba, c3) == suba->tail)
-				suba->tail = ref;
-			c2->next = c3->next == suba_ref (suba, c3) ? ref : c3->next;
-			c2->size += POFF + c3->size;
-		} else {
-			c2->next = c1->next;
-		}
-		c1->next = ref;
-	}
+	cell = *word;
+	sec_check_guards (cell);
+	return cell;
 }
 
-static void *
-suba_realloc(struct allocator *suba, void *ptr, size_t size)
+static void*
+sec_alloc (Block *block, size_t length)
 {
-	struct cell *c;
-	void *p;
+	Cell *cell, *other;
+	size_t n_words;
+	
+	ASSERT (block);
+	ASSERT (length);
 
-	if (ptr == NULL)
-		return suba_alloc(suba, size);
-	if (size == 0) {
-		suba_free(suba, ptr);
+	if (!block->unused)
 		return NULL;
+
+	/* 
+	 * Each memory allocation is aligned to a pointer size, and 
+	 * then, sandwidched between two pointers to its meta data.
+	 * These pointers also act as guards.
+	 *
+	 * We allocate memory in units of sizeof (void*) 
+	 */
+	
+	n_words = sec_size_to_words (length) + 2;
+	
+	/* Look for a cell of at least our required size */
+	cell = block->unused;
+	while (cell->n_words < n_words) {
+		cell = cell->next;
+		if (cell == block->unused) {
+			cell = NULL;
+			break;
+		}
 	}
-	c = P2C(ptr);
-	if (c->size < size || (c->size - ALIGN(size)) > MINCELL) {
-		p = suba_alloc(suba, size);
-	} else {
-		return ptr;
-	}
-	if (p) {
-		memcpy(p, ptr, c->size);
-		suba_free(suba, ptr);
+	
+	if (!cell)
+		return NULL;
+	
+	ASSERT (cell->allocated == 0);
+	ASSERT (cell->prev);
+	ASSERT (cell->words);
+	sec_check_guards (cell);
+	
+	/* Steal from the cell if it's too long */
+	if (cell->n_words > n_words + WASTE) {
+		other = pool_alloc ();
+		if (!other)
+			return NULL;
+		other->n_words = n_words;
+		other->words = cell->words;
+		cell->n_words -= n_words;
+		cell->words += n_words;
+		
+		sec_write_guards (other);
+		sec_write_guards (cell);
+		
+		cell = other;
 	}
-
-	return p;
+	
+	if (cell->next)
+		sec_remove_cell_ring (&block->unused, cell);
+	
+	++block->used;
+	cell->allocated = length;
+	return memset (sec_cell_to_memory (cell), 0, cell->allocated);
 }
 
-static int
-suba_print_cell(struct allocator *suba, const char *msg, struct cell *c)
+static void*
+sec_free (Block *block, void *memory)
 {
-	ref_t ref = suba_ref (suba, c);
-	if (ref >= ALIGN(sizeof *suba) && (ref + POFF + c->size) <= 10000000) {
-		fprintf(stderr, "%s: %8u-%-8u %8u %-8u\n", msg,
-			(unsigned int)ref, (unsigned int)(ref + POFF + c->size),
-			(unsigned int)c->size, (unsigned int)c->next);
-	} else {
-		fprintf(stderr, "%s: %8u-err %8u %-8u\n", msg,
-			(unsigned int)ref, (unsigned int)c->size,
-			(unsigned int)c->next);
-		return 0;
-	}
-	return 1;
+	Cell *cell, *other;
+	word_t *word;
+	
+	ASSERT (block);
+	ASSERT (memory);
+
+	/* Lookup the meta for this memory block (using guard pointer) */
+	word = memory;
+	ASSERT (sec_is_valid_word (block, word - 1));
+	ASSERT (pool_valid (*(word - 1)));
+	cell = *(word - 1);
+
+	sec_check_guards (cell);
+	sec_clear_memory (memory, 0, cell->allocated);
+
+	sec_check_guards (cell);
+	ASSERT (cell->next == NULL);
+	ASSERT (cell->prev == NULL);
+	ASSERT (cell->allocated > 0);
+
+        /* Find previous unallocated neighbor, and merge if possible */
+        other = sec_neighbor_before (block, cell);
+        if (other && other->allocated == 0) {
+        	ASSERT (other->next && other->prev);
+        	other->n_words += cell->n_words;
+        	sec_write_guards (other);
+        	pool_free (cell);
+        	cell = other;
+        } 
+        
+        /* Find next unallocated neighbor, and merge if possible */
+        other = sec_neighbor_after (block, cell);
+        if (other && other->allocated == 0) {
+        	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_write_guards (other);
+        	pool_free (cell);
+        	cell = other;
+        }
+
+        /* Add to the unused list if not already there */
+        if (!cell->next)
+        	sec_insert_cell_ring (&block->unused, cell);
+        
+        cell->allocated = 0;
+        --block->used;
+        return NULL;
 }
 
-static int
-suba_print_free_list(struct allocator *suba)
+static void*
+sec_realloc (Block *block, void *memory, size_t length) 
 {
-	struct cell *c;
-	char buf[10];
-	int count = 0;
-	int ret = 1;
+	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);
+
+	/* Dig out where the meta should be */
+	word = memory;
+	ASSERT (sec_is_valid_word (block, word - 1));
+	ASSERT (pool_valid (*(word - 1)));
+	cell = *(word - 1);
+	
+	/* Validate that it's actually for real */
+	sec_check_guards (cell);
+	ASSERT (cell->allocated > 0);
+	ASSERT (cell->next == NULL);
+	ASSERT (cell->prev == NULL);
+	
+	/* The amount of valid data */
+	valid = cell->allocated;
+	
+	/* How many words we actually want */
+	n_words = sec_size_to_words (length) + 2;
+
+	/* Less memory is required */
+	if (n_words <= cell->n_words) {
+
+		/* TODO: No shrinking behavior yet */
+		cell->allocated = length;
+		return sec_clear_memory (sec_cell_to_memory (cell), valid, length);
+	}
+	
+	/* Need braaaaaiiiiiinsss... */
+	while (cell->n_words < n_words) {
+
+		/* See if we have a neighbor who can give us some memory */
+		other = sec_neighbor_after (block, cell);
+		if (!other || other->allocated != 0)
+			break;
+		
+		/* Eat the whole neighbor if not too big */
+		if (n_words - cell->n_words >= other->n_words + WASTE) {
+			cell->n_words += other->n_words;
+			sec_write_guards (cell);
+			sec_remove_cell_ring (&block->unused, other);
+			pool_free (other);
 
-	c = suba_addr (suba, suba->tail);
-	while (c->next < suba->tail) {
-		c = suba_addr (suba, c->next);
-		sprintf(buf, "%d", count++);
-		if (!suba_print_cell(suba, buf, c)) {
-			ret = 0;
+		/* Steal from the neighbor */
+		} else {
+			other->words += n_words - cell->n_words;
+			other->n_words -= n_words - cell->n_words;
+			sec_write_guards (other);
+			cell->n_words = n_words;
+			sec_write_guards (cell);
 		}
 	}
-	c = suba_addr (suba, c->next);
-	sprintf(buf, "%d", count++);
-	if (!suba_print_cell(suba, buf, c)) {
-		ret = 0;
+	
+	if (cell->n_words >= n_words) {
+		cell->allocated = length;
+		return sec_clear_memory (sec_cell_to_memory (cell), valid, length);
 	}
-
-	return ret;
+	
+	/* That didn't work, try alloc/free */
+	alloc = sec_alloc (block, length);
+	if (alloc) {
+		memcpy (alloc, memory, valid);
+		sec_free (block, memory);
+	}
+	
+	return alloc;
 }
 
+
 static size_t
-suba_allocation_size (struct allocator *suba, void *ptr)
+sec_allocated (Block *block, void *memory)
 {
-	struct cell *c = P2C(ptr);
-	if (c->magic != CELL_MAGIC) {
-		ASSERT(0 && "invalid memory pointer passed to gkr-secure-memory");
-		return 0;
-	}
-	return c->size;
+	Cell *cell;
+	word_t *word;
+	
+	ASSERT (block);
+	ASSERT (memory);
+
+	/* Lookup the meta for this memory block (using guard pointer) */
+	word = memory;
+	ASSERT (sec_is_valid_word (block, word - 1));
+	ASSERT (pool_valid (*(word - 1)));
+	cell = *(word - 1);
+	
+	sec_check_guards (cell);
+	ASSERT (cell->next == NULL);
+	ASSERT (cell->prev == NULL);
+	ASSERT (cell->allocated > 0);
+	
+	return cell->allocated;
 }
 
 /* -----------------------------------------------------------------------------
- * PAGE SOURCE -- Where blocks of locked memory pages come from.
+ * LOCKED MEMORY
  */
- 
-static int lock_warning = 1;
 
 static void*
-get_locked_pages (unsigned long *sz)
+sec_acquire_pages (unsigned long *sz)
 {
 	void *pages;
 	unsigned long pgsize;
@@ -430,7 +664,7 @@
 }
 
 static void 
-rel_locked_pages (void *pages, unsigned long sz)
+sec_release_pages (void *pages, unsigned long sz)
 {
 	ASSERT (pages);
 	ASSERT (sz % getpagesize () == 0);
@@ -453,276 +687,282 @@
  * MANAGE DIFFERENT BLOCKS
  */
 
-typedef struct _MemBlock {
-	unsigned long size;
-	struct allocator *suba;
-	struct _MemBlock *next;
-} MemBlock;
-
-static MemBlock *most_recent_block = NULL;
+static Block *all_blocks = NULL;
 
-static MemBlock* 
-block_create (unsigned long size)
+static Block* 
+sec_block_create (unsigned long size)
 {
-	MemBlock *bl;
-	void *blmem;
+	Block *block;
+	Cell *cell;
 
 #if FORCE_FALLBACK_MEMORY
 	/* We can force all all memory to be malloced */
 	return NULL;
 #endif
 	
-	size += sizeof (MemBlock);
-	
+	block = pool_alloc ();
+	if (!block)
+		return NULL;
+
+	cell = pool_alloc ();
+	if (!cell) {
+		pool_free (block);
+		return NULL;
+	}
+
 	/* The size above is a minimum, we're free to go bigger */
 	if (size < DEFAULT_BLOCK_SIZE)
 		size = DEFAULT_BLOCK_SIZE;
 		
-	blmem = get_locked_pages (&size);
-	if (!blmem)
+	block->n_words = size / sizeof (word_t);
+	block->words = sec_acquire_pages (&size);
+	if (!block->words) {
+		pool_free (block);
+		pool_free (cell);
 		return NULL;
-		
-	bl = (MemBlock*)blmem;
-	bl->size = size;
-	bl->suba = suba_init (((unsigned char*)blmem) + sizeof (MemBlock), 
-			      size - sizeof (MemBlock));
-	ASSERT (bl->suba);
+	}
 	
-	bl->next = most_recent_block;
-	most_recent_block = bl;
+	/* The first cell to allocate from */
+	cell->words = block->words;
+	cell->n_words = block->n_words;
+	cell->allocated = 0;
+	sec_write_guards (cell);
+	sec_insert_cell_ring (&block->unused, cell);
 	
-	return bl;
+	block->next = all_blocks;
+	all_blocks = block;
+	
+	return block;
 }
 
 static void
-block_destroy (MemBlock *bl)
+sec_block_destroy (Block *block)
 {
-	MemBlock *b;
-	
-	ASSERT (bl && bl->suba);
-	ASSERT (bl->size > 0);
-	ASSERT (bl->suba->alloc_total == 0);
+	Block *bl, **at;
+	Cell *cell;
+
+	ASSERT (block);
+	ASSERT (block->words);
+	ASSERT (block->used == 0);
 	
-	/* Is the most recent block, simple */
-	if (bl == most_recent_block) {
-		most_recent_block = bl->next;
-		
-	/* Take it out of our list */
-	} else {
-		for (b = most_recent_block; b; b = b->next) {
-			if (b->next == bl) {
-				b->next = bl->next;
-				break;
-			}
+	/* Remove from the list */
+	for (at = &all_blocks, bl = *at; bl; at = &bl->next, bl = *at) {
+		if (bl == block) {
+			*at = block->next;
+			break;
 		}
-		ASSERT (b != NULL && "couldn't find memory block in list");
 	}
 	
-	/* Memory is all in one block, nothing fancy to free */
-	rel_locked_pages(bl, bl->size);	
-}
+	/* Must have been found */
+	ASSERT (bl == block);
 
-static int
-block_belongs (MemBlock *bl, const void *p)
-{
-	ASSERT (bl);
-	ASSERT (bl->size > 0);
+	/* Release all the meta data cells */
+	while (block->unused) {
+		cell = block->unused;
+		sec_remove_cell_ring (&block->unused, cell);
+		pool_free (cell);
+	}
 	
-	/* This does not check for invalid memory */
-	return ((char*)p) >= ((char*)bl) && 
-	       ((char*)p) < (((char*)bl) + bl->size);
+	/* Release all pages of secure memory */
+	sec_release_pages (block->words, block->n_words * sizeof (word_t));
+
+	pool_free (block);
 }
 
+/* ------------------------------------------------------------------------
+ * PUBLIC FUNCTIONALITY
+ */
+
 void*
-egg_secure_alloc (unsigned long sz)
+egg_secure_alloc (size_t length)
 {
-	return egg_secure_alloc_full (sz, GKR_SECURE_USE_FALLBACK);
+	return egg_secure_alloc_full (length, GKR_SECURE_USE_FALLBACK);
 }
 
 void*
-egg_secure_alloc_full (unsigned long sz, int flags)
+egg_secure_alloc_full (size_t length, int flags)
 {
-	MemBlock *bl;
-	void *p = NULL;
+	Block *block;
+	void *memory = NULL;
 		
-	if (sz > 0xFFFFFFFF / 2) {
-		fprintf (stderr, "tried to allocate an insane amount of memory: %lu\n", sz);   
+	if (length > 0xFFFFFFFF / 2) {
+		fprintf (stderr, "tried to allocate an insane amount of memory: %lu\n", length);   
 		return NULL;
 	}
 	
 	DO_LOCK ();
 	
-		for (bl = most_recent_block; bl; bl = bl->next) {
-			p = suba_alloc (bl->suba, sz);
-			if (p)
+		for (block = all_blocks; block; block = block->next) {
+			memory = sec_alloc (block, length);
+			if (memory)
 				break;	
 		}
 	
 		/* None of the current blocks have space, allocate new */
-		if (!p) {
-			bl = block_create (sz);
-			if (bl) {
-				p = suba_alloc (bl->suba, sz);
-				ASSERT (p);
-			}
+		if (!memory) {
+			block = sec_block_create (length);
+			if (block)
+				memory = sec_alloc (block, length);
 		}
 	
 	DO_UNLOCK ();
 	
-	if (!p && (flags & GKR_SECURE_USE_FALLBACK)) {
-		p = egg_memory_fallback (NULL, sz);
-		if (p) /* Our returned memory is always zeroed */
-			memset (p, 0, sz);
+	if (!memory && (flags & GKR_SECURE_USE_FALLBACK)) {
+		memory = egg_memory_fallback (NULL, length);
+		if (memory) /* Our returned memory is always zeroed */
+			memset (memory, 0, length);
 	}
 	
-	if (!p)
+	if (!memory)
 		errno = ENOMEM;
 	
-	return p;
+	return memory;
 }
 
 void*
-egg_secure_realloc (void *p, unsigned long sz)
+egg_secure_realloc (void *memory, size_t length)
 {
-	return egg_secure_realloc_full (p, sz, GKR_SECURE_USE_FALLBACK);
+	return egg_secure_realloc_full (memory, length, GKR_SECURE_USE_FALLBACK);
 }
 
 void*
-egg_secure_realloc_full (void *p, unsigned long sz, int flags)
+egg_secure_realloc_full (void *memory, unsigned long length, int flags)
 {
-	MemBlock *bl = NULL;
-	unsigned long oldsz = 0;
+	Block *block = NULL;
+	size_t previous = 0;
 	int donew = 0;
-	void *n = NULL;	
+	void *alloc = NULL;
 	
-	if (sz > 0xFFFFFFFF / 2) {
-		fprintf (stderr, "tried to allocate an insane amount of memory: %lu\n", sz);
+	if (length > 0xFFFFFFFF / 2) {
+		fprintf (stderr, "tried to allocate an insane amount of memory: %lu\n", length);
 		ASSERT (0 && "tried to allocate an insane amount of memory");
 		return NULL;
 	}
 	
-	if (p == NULL)
-		return egg_secure_alloc_full (sz, flags);
-	if (!sz) {
-		egg_secure_free_full (p, flags);
+	if (memory == NULL)
+		return egg_secure_alloc_full (length, flags);
+	if (!length) {
+		egg_secure_free_full (memory, flags);
 		return NULL;
 	}
 	
 	DO_LOCK ();
 	
 		/* Find out where it belongs to */
-		for (bl = most_recent_block; bl; bl = bl->next) {
-			if (block_belongs (bl, p)) {
-				oldsz = suba_allocation_size (bl->suba, p); 
-				n = suba_realloc (bl->suba, p, sz);
+		for (block = all_blocks; block; block = block->next) {
+			if (sec_is_valid_word (block, memory)) {
+				previous = sec_allocated (block, memory);
+				alloc = sec_realloc (block, memory, length);
 				break;
 			}
 		}
 
 		/* If it didn't work we may need to allocate a new block */
-		if (bl && !n)
+		if (block && !alloc)
 			donew = 1;
 
-		if (bl && bl->suba->alloc_total == 0)
-			block_destroy (bl);
+		if (block && block->used == 0)
+			sec_block_destroy (block);
 		
 	DO_UNLOCK ();		
 	
-	if (!bl) {
+	if (!block) {
 		if ((flags & GKR_SECURE_USE_FALLBACK)) {
 			/* 
 			 * In this case we can't zero the returned memory, 
 			 * because we don't know what the block size was.
 			 */
-			return egg_memory_fallback (p, sz);
+			return egg_memory_fallback (memory, length);
 		} else {
-			fprintf (stderr, "memory does not belong to gnome-keyring: 0x%08lx\n", (unsigned long)p);
+			fprintf (stderr, "memory does not belong to gnome-keyring: 0x%08lx\n", (unsigned long)memory);
 			ASSERT (0 && "memory does does not belong to gnome-keyring");
 			return NULL;
 		}
 	}
 		
 	if (donew) {
-		n = egg_secure_alloc_full (sz, flags);
-		if (n) {
-			memcpy (n, p, oldsz);
-			egg_secure_free_full (p, flags);
+		alloc = egg_secure_alloc_full (length, flags);
+		if (alloc) {
+			memcpy (alloc, memory, previous);
+			egg_secure_free_full (memory, flags);
 		}
 	}
 	
-	if (!n)
+	if (!alloc)
 		errno = ENOMEM;
 
-	return n;
+	return alloc;
 }
 
 void
-egg_secure_free (void *p)
+egg_secure_free (void *memory)
 {
-	egg_secure_free_full (p, GKR_SECURE_USE_FALLBACK);
+	egg_secure_free_full (memory, GKR_SECURE_USE_FALLBACK);
 }
 
 void
-egg_secure_free_full (void *p, int flags)
+egg_secure_free_full (void *memory, int flags)
 {
-	MemBlock *bl = NULL;
+	Block *block = NULL;
+	
+	if (memory == NULL)
+		return;
 	
 	DO_LOCK ();
 	
 		/* Find out where it belongs to */
-		for (bl = most_recent_block; bl; bl = bl->next) {
-			if (block_belongs (bl, p)) {
-				suba_free (bl->suba, p);
+		for (block = all_blocks; block; block = block->next) {
+			if (sec_is_valid_word (block, memory)) {
+				sec_free (block, memory);
 				break;
 			}
 		}
 
-		if (bl && bl->suba->alloc_total == 0)
-			block_destroy (bl);
+		if (block && block->used == 0)
+			sec_block_destroy (block);
 			
 	DO_UNLOCK ();
 	
-	if (!bl) {
+	if (!block) {
 		if ((flags & GKR_SECURE_USE_FALLBACK)) {
-			egg_memory_fallback (p, 0);
+			egg_memory_fallback (memory, 0);
 		} else {
-			fprintf (stderr, "memory does not belong to gnome-keyring: 0x%08lx\n", (unsigned long)p);
+			fprintf (stderr, "memory does not belong to gnome-keyring: 0x%08lx\n", (unsigned long)memory);
 			ASSERT (0 && "memory does does not belong to gnome-keyring");
 		}
 	}
 } 
 
 int  
-egg_secure_check (const void *p)
+egg_secure_check (const void *memory)
 {
-	MemBlock *bl = NULL;
+	Block *block = NULL;
 
 	DO_LOCK ();
 	
 		/* Find out where it belongs to */
-		for (bl = most_recent_block; bl; bl = bl->next) {
-			if (block_belongs (bl, p))
+		for (block = all_blocks; block; block = block->next) {
+			if (sec_is_valid_word (block, (word_t*)memory))
 				break;
 		}
 		
 	DO_UNLOCK ();
 	
-	return bl == NULL ? 0 : 1;
+	return block == NULL ? 0 : 1;
 } 
 
 void
 egg_secure_dump_blocks (void)
 {
-	MemBlock *bl = NULL;
+	Block *block = NULL;
 
 	DO_LOCK ();
 	
 		/* Find out where it belongs to */
-		for (bl = most_recent_block; bl; bl = bl->next) {
+		for (block = all_blocks; block; block = block->next) {
 			fprintf (stderr, "----------------------------------------------------\n");
-			fprintf (stderr, "  BLOCK at: 0x%08lx  len: %lu\n", (unsigned long)bl, bl->size);
+			fprintf (stderr, "  BLOCK at: 0x%08lx  len: %lu\n", (unsigned long)block, block->n_words * sizeof (word_t));
 			fprintf (stderr, "\n");
-			suba_print_free_list (bl->suba);
 		}
 		
 	DO_UNLOCK ();



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