Blame egg/egg-secure-memory.c

Packit b00eeb
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
Packit b00eeb
/* egg-secure-memory.h - library for allocating memory that is non-pageable
Packit b00eeb
Packit b00eeb
   Copyright (C) 2007 Stefan Walter
Packit b00eeb
Packit b00eeb
   The Gnome Keyring Library is free software; you can redistribute it and/or
Packit b00eeb
   modify it under the terms of the GNU Library General Public License as
Packit b00eeb
   published by the Free Software Foundation; either version 2 of the
Packit b00eeb
   License, or (at your option) any later version.
Packit b00eeb
Packit b00eeb
   The Gnome Keyring Library is distributed in the hope that it will be useful,
Packit b00eeb
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit b00eeb
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit b00eeb
   Library General Public License for more details.
Packit b00eeb
Packit b00eeb
   You should have received a copy of the GNU Library General Public
Packit b00eeb
   License along with the Gnome Library; see the file COPYING.LIB.  If not,
Packit b00eeb
   see <http://www.gnu.org/licenses/>.
Packit b00eeb
Packit b00eeb
   Author: Stef Walter <stef@memberwebs.com>
Packit b00eeb
*/
Packit b00eeb
Packit b00eeb
/*
Packit b00eeb
 * IMPORTANT: This is pure vanila standard C, no glib. We need this
Packit b00eeb
 * because certain consumers of this protocol need to be built
Packit b00eeb
 * without linking in any special libraries. ie: the PKCS#11 module.
Packit b00eeb
 */
Packit b00eeb
Packit b00eeb
#include "config.h"
Packit b00eeb
Packit b00eeb
#include "egg-secure-memory.h"
Packit b00eeb
Packit b00eeb
#include <sys/types.h>
Packit b00eeb
#include <sys/mman.h>
Packit b00eeb
#include <stddef.h>
Packit b00eeb
#include <string.h>
Packit b00eeb
#include <stdio.h>
Packit b00eeb
#include <stdlib.h>
Packit b00eeb
#include <errno.h>
Packit b00eeb
#include <unistd.h>
Packit b00eeb
#include <assert.h>
Packit b00eeb
Packit b00eeb
#ifdef WITH_VALGRIND
Packit b00eeb
#include <valgrind/valgrind.h>
Packit b00eeb
#include <valgrind/memcheck.h>
Packit b00eeb
#endif
Packit b00eeb
Packit b00eeb
#define DEBUG_SECURE_MEMORY 0
Packit b00eeb
Packit b00eeb
#if DEBUG_SECURE_MEMORY
Packit b00eeb
#define DEBUG_ALLOC(msg, n) 	fprintf(stderr, "%s %lu bytes\n", msg, n);
Packit b00eeb
#else
Packit b00eeb
#define DEBUG_ALLOC(msg, n)
Packit b00eeb
#endif
Packit b00eeb
Packit b00eeb
#define DEFAULT_BLOCK_SIZE 16384
Packit b00eeb
Packit b00eeb
/* Use our own assert to guarantee no glib allocations */
Packit b00eeb
#ifndef ASSERT
Packit b00eeb
#ifdef G_DISABLE_ASSERT
Packit b00eeb
#define ASSERT(x)
Packit b00eeb
#else
Packit b00eeb
#define ASSERT(x) assert(x)
Packit b00eeb
#endif
Packit b00eeb
#endif
Packit b00eeb
Packit b00eeb
#define DO_LOCK() \
Packit b00eeb
	EGG_SECURE_GLOBALS.lock ();
Packit b00eeb
Packit b00eeb
#define DO_UNLOCK() \
Packit b00eeb
	EGG_SECURE_GLOBALS.unlock ();
Packit b00eeb
Packit b00eeb
static int show_warning = 1;
Packit b00eeb
int egg_secure_warnings = 1;
Packit b00eeb
Packit b00eeb
/*
Packit b00eeb
 * We allocate all memory in units of sizeof(void*). This
Packit b00eeb
 * is our definition of 'word'.
Packit b00eeb
 */
Packit b00eeb
typedef void* word_t;
Packit b00eeb
Packit b00eeb
/* The amount of extra words we can allocate */
Packit b00eeb
#define WASTE   4
Packit b00eeb
Packit b00eeb
/*
Packit b00eeb
 * Track allocated memory or a free block. This structure is not stored
Packit b00eeb
 * in the secure memory area. It is allocated from a pool of other
Packit b00eeb
 * memory. See meta_pool_xxx ().
Packit b00eeb
 */
Packit b00eeb
typedef struct _Cell {
Packit b00eeb
	word_t *words;          /* Pointer to secure memory */
Packit b00eeb
	size_t n_words;         /* Amount of secure memory in words */
Packit b00eeb
	size_t requested;       /* Amount actually requested by app, in bytes, 0 if unused */
Packit b00eeb
	const char *tag;        /* Tag which describes the allocation */
Packit b00eeb
	struct _Cell *next;     /* Next in memory ring */
Packit b00eeb
	struct _Cell *prev;     /* Previous in memory ring */
Packit b00eeb
} Cell;
Packit b00eeb
Packit b00eeb
/*
Packit b00eeb
 * A block of secure memory. This structure is the header in that block.
Packit b00eeb
 */
Packit b00eeb
typedef struct _Block {
Packit b00eeb
	word_t *words;              /* Actual memory hangs off here */
Packit b00eeb
	size_t n_words;             /* Number of words in block */
Packit b00eeb
	size_t n_used;              /* Number of used allocations */
Packit b00eeb
	struct _Cell* used_cells;   /* Ring of used allocations */
Packit b00eeb
	struct _Cell* unused_cells; /* Ring of unused allocations */
Packit b00eeb
	struct _Block *next;        /* Next block in list */
Packit b00eeb
} Block;
Packit b00eeb
Packit b00eeb
/* -----------------------------------------------------------------------------
Packit b00eeb
 * UNUSED STACK
Packit b00eeb
 */
Packit b00eeb
Packit b00eeb
static inline void
Packit b00eeb
unused_push (void **stack, void *ptr)
Packit b00eeb
{
Packit b00eeb
	ASSERT (ptr);
Packit b00eeb
	ASSERT (stack);
Packit b00eeb
	*((void**)ptr) = *stack;
Packit b00eeb
	*stack = ptr;
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static inline void*
Packit b00eeb
unused_pop (void **stack)
Packit b00eeb
{
Packit b00eeb
	void *ptr;
Packit b00eeb
	ASSERT (stack);
Packit b00eeb
	ptr = *stack;
Packit b00eeb
	*stack = *(void**)ptr;
Packit b00eeb
	return ptr;
Packit b00eeb
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static inline void*
Packit b00eeb
unused_peek (void **stack)
Packit b00eeb
{
Packit b00eeb
	ASSERT (stack);
Packit b00eeb
	return *stack;
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
/* -----------------------------------------------------------------------------
Packit b00eeb
 * POOL META DATA ALLOCATION
Packit b00eeb
 *
Packit b00eeb
 * A pool for memory meta data. We allocate fixed size blocks. There are actually
Packit b00eeb
 * two different structures stored in this pool: Cell and Block. Cell is allocated
Packit b00eeb
 * way more often, and is bigger so we just allocate that size for both.
Packit b00eeb
 */
Packit b00eeb
Packit b00eeb
/* Pool allocates this data type */
Packit b00eeb
typedef union _Item {
Packit b00eeb
		Cell cell;
Packit b00eeb
		Block block;
Packit b00eeb
} Item;
Packit b00eeb
Packit b00eeb
typedef struct _Pool {
Packit b00eeb
	struct _Pool *next;    /* Next pool in list */
Packit b00eeb
	size_t length;         /* Length in bytes of the pool */
Packit b00eeb
	size_t used;           /* Number of cells used in pool */
Packit b00eeb
	void *unused;          /* Unused stack of unused stuff */
Packit b00eeb
	size_t n_items;        /* Total number of items in pool */
Packit b00eeb
	Item items[1];         /* Actual items hang off here */
Packit b00eeb
} Pool;
Packit b00eeb
Packit b00eeb
static void *
Packit b00eeb
pool_alloc (void)
Packit b00eeb
{
Packit b00eeb
	Pool *pool;
Packit b00eeb
	void *pages, *item;
Packit b00eeb
	size_t len, i;
Packit b00eeb
Packit b00eeb
	if (!EGG_SECURE_GLOBALS.pool_version ||
Packit b00eeb
	    strcmp (EGG_SECURE_GLOBALS.pool_version, EGG_SECURE_POOL_VER_STR) != 0) {
Packit b00eeb
		if (show_warning && egg_secure_warnings)
Packit b00eeb
			fprintf (stderr, "the secure memory pool version does not match the code '%s' != '%s'\n",
Packit b00eeb
			         EGG_SECURE_GLOBALS.pool_version ? EGG_SECURE_GLOBALS.pool_version : "(null)",
Packit b00eeb
			         EGG_SECURE_POOL_VER_STR);
Packit b00eeb
		show_warning = 0;
Packit b00eeb
		return NULL;
Packit b00eeb
	}
Packit b00eeb
Packit b00eeb
	/* A pool with an available item */
Packit b00eeb
	for (pool = EGG_SECURE_GLOBALS.pool_data; pool; pool = pool->next) {
Packit b00eeb
		if (unused_peek (&pool->unused))
Packit b00eeb
			break;
Packit b00eeb
	}
Packit b00eeb
Packit b00eeb
	/* Create a new pool */
Packit b00eeb
	if (pool == NULL) {
Packit b00eeb
		len = getpagesize () * 2;
Packit b00eeb
		pages = mmap (0, len, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
Packit b00eeb
		if (pages == MAP_FAILED)
Packit b00eeb
			return NULL;
Packit b00eeb
Packit b00eeb
		/* Fill in the block header, and inlude in block list */
Packit b00eeb
		pool = pages;
Packit b00eeb
		pool->next = EGG_SECURE_GLOBALS.pool_data;
Packit b00eeb
		EGG_SECURE_GLOBALS.pool_data = pool;
Packit b00eeb
		pool->length = len;
Packit b00eeb
		pool->used = 0;
Packit b00eeb
		pool->unused = NULL;
Packit b00eeb
Packit b00eeb
		/* Fill block with unused items */
Packit b00eeb
		pool->n_items = (len - sizeof (Pool)) / sizeof (Item);
Packit b00eeb
		for (i = 0; i < pool->n_items; ++i)
Packit b00eeb
			unused_push (&pool->unused, pool->items + i);
Packit b00eeb
Packit b00eeb
#ifdef WITH_VALGRIND
Packit b00eeb
		VALGRIND_CREATE_MEMPOOL(pool, 0, 0);
Packit b00eeb
#endif
Packit b00eeb
	}
Packit b00eeb
Packit b00eeb
	++pool->used;
Packit b00eeb
	ASSERT (unused_peek (&pool->unused));
Packit b00eeb
	item = unused_pop (&pool->unused);
Packit b00eeb
Packit b00eeb
#ifdef WITH_VALGRIND
Packit b00eeb
	VALGRIND_MEMPOOL_ALLOC (pool, item, sizeof (Item));
Packit b00eeb
#endif
Packit b00eeb
Packit b00eeb
	return memset (item, 0, sizeof (Item));
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static void
Packit b00eeb
pool_free (void* item)
Packit b00eeb
{
Packit b00eeb
	Pool *pool, **at;
Packit b00eeb
	char *ptr, *beg, *end;
Packit b00eeb
Packit b00eeb
	ptr = item;
Packit b00eeb
Packit b00eeb
	/* Find which block this one belongs to */
Packit b00eeb
	for (at = (Pool **)&EGG_SECURE_GLOBALS.pool_data, pool = *at;
Packit b00eeb
	     pool != NULL; at = &pool->next, pool = *at) {
Packit b00eeb
		beg = (char*)pool->items;
Packit b00eeb
		end = (char*)pool + pool->length - sizeof (Item);
Packit b00eeb
		if (ptr >= beg && ptr <= end) {
Packit b00eeb
			ASSERT ((ptr - beg) % sizeof (Item) == 0);
Packit b00eeb
			break;
Packit b00eeb
		}
Packit b00eeb
	}
Packit b00eeb
Packit b00eeb
	/* Otherwise invalid meta */
Packit b00eeb
	ASSERT (at);
Packit b00eeb
	ASSERT (pool);
Packit b00eeb
	ASSERT (pool->used > 0);
Packit b00eeb
Packit b00eeb
	/* No more meta cells used in this block, remove from list, destroy */
Packit b00eeb
	if (pool->used == 1) {
Packit b00eeb
		*at = pool->next;
Packit b00eeb
Packit b00eeb
#ifdef WITH_VALGRIND
Packit b00eeb
		VALGRIND_DESTROY_MEMPOOL (pool);
Packit b00eeb
#endif
Packit b00eeb
Packit b00eeb
		munmap (pool, pool->length);
Packit b00eeb
		return;
Packit b00eeb
	}
Packit b00eeb
Packit b00eeb
#ifdef WITH_VALGRIND
Packit b00eeb
	VALGRIND_MEMPOOL_FREE (pool, item);
Packit b00eeb
	VALGRIND_MAKE_MEM_UNDEFINED (item, sizeof (Item));
Packit b00eeb
#endif
Packit b00eeb
Packit b00eeb
	--pool->used;
Packit b00eeb
	memset (item, 0xCD, sizeof (Item));
Packit b00eeb
	unused_push (&pool->unused, item);
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
#ifndef G_DISABLE_ASSERT
Packit b00eeb
Packit b00eeb
static int
Packit b00eeb
pool_valid (void* item)
Packit b00eeb
{
Packit b00eeb
	Pool *pool;
Packit b00eeb
	char *ptr, *beg, *end;
Packit b00eeb
Packit b00eeb
	ptr = item;
Packit b00eeb
Packit b00eeb
	/* Find which block this one belongs to */
Packit b00eeb
	for (pool = EGG_SECURE_GLOBALS.pool_data; pool; pool = pool->next) {
Packit b00eeb
		beg = (char*)pool->items;
Packit b00eeb
		end = (char*)pool + pool->length - sizeof (Item);
Packit b00eeb
		if (ptr >= beg && ptr <= end)
Packit b00eeb
			return (pool->used && (ptr - beg) % sizeof (Item) == 0);
Packit b00eeb
	}
Packit b00eeb
Packit b00eeb
	return 0;
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
#endif /* G_DISABLE_ASSERT */
Packit b00eeb
Packit b00eeb
/* -----------------------------------------------------------------------------
Packit b00eeb
 * SEC ALLOCATION
Packit b00eeb
 *
Packit b00eeb
 * Each memory cell begins and ends with a pointer to its metadata. These are also
Packit b00eeb
 * used as guards or red zones. Since they're treated as redzones by valgrind we
Packit b00eeb
 * have to jump through a few hoops before reading and/or writing them.
Packit b00eeb
 */
Packit b00eeb
Packit b00eeb
static inline size_t
Packit b00eeb
sec_size_to_words (size_t length)
Packit b00eeb
{
Packit b00eeb
	return (length % sizeof (void*) ? 1 : 0) + (length / sizeof (void*));
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static inline void
Packit b00eeb
sec_write_guards (Cell *cell)
Packit b00eeb
{
Packit b00eeb
#ifdef WITH_VALGRIND
Packit b00eeb
	VALGRIND_MAKE_MEM_UNDEFINED (cell->words, sizeof (word_t));
Packit b00eeb
	VALGRIND_MAKE_MEM_UNDEFINED (cell->words + cell->n_words - 1, sizeof (word_t));
Packit b00eeb
#endif
Packit b00eeb
Packit b00eeb
	((void**)cell->words)[0] = (void*)cell;
Packit b00eeb
	((void**)cell->words)[cell->n_words - 1] = (void*)cell;
Packit b00eeb
Packit b00eeb
#ifdef WITH_VALGRIND
Packit b00eeb
	VALGRIND_MAKE_MEM_NOACCESS (cell->words, sizeof (word_t));
Packit b00eeb
	VALGRIND_MAKE_MEM_NOACCESS (cell->words + cell->n_words - 1, sizeof (word_t));
Packit b00eeb
#endif
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static inline void
Packit b00eeb
sec_check_guards (Cell *cell)
Packit b00eeb
{
Packit b00eeb
#ifdef WITH_VALGRIND
Packit b00eeb
	VALGRIND_MAKE_MEM_DEFINED (cell->words, sizeof (word_t));
Packit b00eeb
	VALGRIND_MAKE_MEM_DEFINED (cell->words + cell->n_words - 1, sizeof (word_t));
Packit b00eeb
#endif
Packit b00eeb
Packit b00eeb
	ASSERT(((void**)cell->words)[0] == (void*)cell);
Packit b00eeb
	ASSERT(((void**)cell->words)[cell->n_words - 1] == (void*)cell);
Packit b00eeb
Packit b00eeb
#ifdef WITH_VALGRIND
Packit b00eeb
	VALGRIND_MAKE_MEM_NOACCESS (cell->words, sizeof (word_t));
Packit b00eeb
	VALGRIND_MAKE_MEM_NOACCESS (cell->words + cell->n_words - 1, sizeof (word_t));
Packit b00eeb
#endif
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static void
Packit b00eeb
sec_insert_cell_ring (Cell **ring, Cell *cell)
Packit b00eeb
{
Packit b00eeb
	ASSERT (ring);
Packit b00eeb
	ASSERT (cell);
Packit b00eeb
	ASSERT (cell != *ring);
Packit b00eeb
	ASSERT (cell->next == NULL);
Packit b00eeb
	ASSERT (cell->prev == NULL);
Packit b00eeb
Packit b00eeb
	/* Insert back into the mix of available memory */
Packit b00eeb
	if (*ring) {
Packit b00eeb
		cell->next = (*ring)->next;
Packit b00eeb
		cell->prev = *ring;
Packit b00eeb
		cell->next->prev = cell;
Packit b00eeb
		cell->prev->next = cell;
Packit b00eeb
	} else {
Packit b00eeb
		cell->next = cell;
Packit b00eeb
		cell->prev = cell;
Packit b00eeb
	}
Packit b00eeb
Packit b00eeb
	*ring = cell;
Packit b00eeb
	ASSERT (cell->next->prev == cell);
Packit b00eeb
	ASSERT (cell->prev->next == cell);
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static void
Packit b00eeb
sec_remove_cell_ring (Cell **ring, Cell *cell)
Packit b00eeb
{
Packit b00eeb
	ASSERT (ring);
Packit b00eeb
	ASSERT (*ring);
Packit b00eeb
	ASSERT (cell->next);
Packit b00eeb
	ASSERT (cell->prev);
Packit b00eeb
Packit b00eeb
	ASSERT (cell->next->prev == cell);
Packit b00eeb
	ASSERT (cell->prev->next == cell);
Packit b00eeb
Packit b00eeb
	if (cell == *ring) {
Packit b00eeb
		/* The last meta? */
Packit b00eeb
		if (cell->next == cell) {
Packit b00eeb
			ASSERT (cell->prev == cell);
Packit b00eeb
			*ring = NULL;
Packit b00eeb
Packit b00eeb
		/* Just pointing to this meta */
Packit b00eeb
		} else {
Packit b00eeb
			ASSERT (cell->prev != cell);
Packit b00eeb
			*ring = cell->next;
Packit b00eeb
		}
Packit b00eeb
	}
Packit b00eeb
Packit b00eeb
	cell->next->prev = cell->prev;
Packit b00eeb
	cell->prev->next = cell->next;
Packit b00eeb
	cell->next = cell->prev = NULL;
Packit b00eeb
Packit b00eeb
	ASSERT (*ring != cell);
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static inline void*
Packit b00eeb
sec_cell_to_memory (Cell *cell)
Packit b00eeb
{
Packit b00eeb
	return cell->words + 1;
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static inline int
Packit b00eeb
sec_is_valid_word (Block *block, word_t *word)
Packit b00eeb
{
Packit b00eeb
	return (word >= block->words && word < block->words + block->n_words);
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static inline void
Packit b00eeb
sec_clear_undefined (void *memory,
Packit b00eeb
                     size_t from,
Packit b00eeb
                     size_t to)
Packit b00eeb
{
Packit b00eeb
	char *ptr = memory;
Packit b00eeb
	ASSERT (from <= to);
Packit b00eeb
#ifdef WITH_VALGRIND
Packit b00eeb
	VALGRIND_MAKE_MEM_UNDEFINED (ptr + from, to - from);
Packit b00eeb
#endif
Packit b00eeb
	memset (ptr + from, 0, to - from);
Packit b00eeb
#ifdef WITH_VALGRIND
Packit b00eeb
	VALGRIND_MAKE_MEM_UNDEFINED (ptr + from, to - from);
Packit b00eeb
#endif
Packit b00eeb
}
Packit b00eeb
static inline void
Packit b00eeb
sec_clear_noaccess (void *memory, size_t from, size_t to)
Packit b00eeb
{
Packit b00eeb
	char *ptr = memory;
Packit b00eeb
	ASSERT (from <= to);
Packit b00eeb
#ifdef WITH_VALGRIND
Packit b00eeb
	VALGRIND_MAKE_MEM_UNDEFINED (ptr + from, to - from);
Packit b00eeb
#endif
Packit b00eeb
	memset (ptr + from, 0, to - from);
Packit b00eeb
#ifdef WITH_VALGRIND
Packit b00eeb
	VALGRIND_MAKE_MEM_NOACCESS (ptr + from, to - from);
Packit b00eeb
#endif
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static Cell*
Packit b00eeb
sec_neighbor_before (Block *block, Cell *cell)
Packit b00eeb
{
Packit b00eeb
	word_t *word;
Packit b00eeb
Packit b00eeb
	ASSERT (cell);
Packit b00eeb
	ASSERT (block);
Packit b00eeb
Packit b00eeb
	word = cell->words - 1;
Packit b00eeb
	if (!sec_is_valid_word (block, word))
Packit b00eeb
		return NULL;
Packit b00eeb
Packit b00eeb
#ifdef WITH_VALGRIND
Packit b00eeb
	VALGRIND_MAKE_MEM_DEFINED (word, sizeof (word_t));
Packit b00eeb
#endif
Packit b00eeb
Packit b00eeb
	cell = *word;
Packit b00eeb
	sec_check_guards (cell);
Packit b00eeb
Packit b00eeb
#ifdef WITH_VALGRIND
Packit b00eeb
	VALGRIND_MAKE_MEM_NOACCESS (word, sizeof (word_t));
Packit b00eeb
#endif
Packit b00eeb
Packit b00eeb
	return cell;
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static Cell*
Packit b00eeb
sec_neighbor_after (Block *block, Cell *cell)
Packit b00eeb
{
Packit b00eeb
	word_t *word;
Packit b00eeb
Packit b00eeb
	ASSERT (cell);
Packit b00eeb
	ASSERT (block);
Packit b00eeb
Packit b00eeb
	word = cell->words + cell->n_words;
Packit b00eeb
	if (!sec_is_valid_word (block, word))
Packit b00eeb
		return NULL;
Packit b00eeb
Packit b00eeb
#ifdef WITH_VALGRIND
Packit b00eeb
	VALGRIND_MAKE_MEM_DEFINED (word, sizeof (word_t));
Packit b00eeb
#endif
Packit b00eeb
Packit b00eeb
	cell = *word;
Packit b00eeb
	sec_check_guards (cell);
Packit b00eeb
Packit b00eeb
#ifdef WITH_VALGRIND
Packit b00eeb
	VALGRIND_MAKE_MEM_NOACCESS (word, sizeof (word_t));
Packit b00eeb
#endif
Packit b00eeb
Packit b00eeb
	return cell;
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static void*
Packit b00eeb
sec_alloc (Block *block,
Packit b00eeb
           const char *tag,
Packit b00eeb
           size_t length)
Packit b00eeb
{
Packit b00eeb
	Cell *cell, *other;
Packit b00eeb
	size_t n_words;
Packit b00eeb
	void *memory;
Packit b00eeb
Packit b00eeb
	ASSERT (block);
Packit b00eeb
	ASSERT (length);
Packit b00eeb
	ASSERT (tag);
Packit b00eeb
Packit b00eeb
	if (!block->unused_cells)
Packit b00eeb
		return NULL;
Packit b00eeb
Packit b00eeb
	/*
Packit b00eeb
	 * Each memory allocation is aligned to a pointer size, and
Packit b00eeb
	 * then, sandwidched between two pointers to its meta data.
Packit b00eeb
	 * These pointers also act as guards.
Packit b00eeb
	 *
Packit b00eeb
	 * We allocate memory in units of sizeof (void*)
Packit b00eeb
	 */
Packit b00eeb
Packit b00eeb
	n_words = sec_size_to_words (length) + 2;
Packit b00eeb
Packit b00eeb
	/* Look for a cell of at least our required size */
Packit b00eeb
	cell = block->unused_cells;
Packit b00eeb
	while (cell->n_words < n_words) {
Packit b00eeb
		cell = cell->next;
Packit b00eeb
		if (cell == block->unused_cells) {
Packit b00eeb
			cell = NULL;
Packit b00eeb
			break;
Packit b00eeb
		}
Packit b00eeb
	}
Packit b00eeb
Packit b00eeb
	if (!cell)
Packit b00eeb
		return NULL;
Packit b00eeb
Packit b00eeb
	ASSERT (cell->tag == NULL);
Packit b00eeb
	ASSERT (cell->requested == 0);
Packit b00eeb
	ASSERT (cell->prev);
Packit b00eeb
	ASSERT (cell->words);
Packit b00eeb
	sec_check_guards (cell);
Packit b00eeb
Packit b00eeb
	/* Steal from the cell if it's too long */
Packit b00eeb
	if (cell->n_words > n_words + WASTE) {
Packit b00eeb
		other = pool_alloc ();
Packit b00eeb
		if (!other)
Packit b00eeb
			return NULL;
Packit b00eeb
		other->n_words = n_words;
Packit b00eeb
		other->words = cell->words;
Packit b00eeb
		cell->n_words -= n_words;
Packit b00eeb
		cell->words += n_words;
Packit b00eeb
Packit b00eeb
		sec_write_guards (other);
Packit b00eeb
		sec_write_guards (cell);
Packit b00eeb
Packit b00eeb
		cell = other;
Packit b00eeb
	}
Packit b00eeb
Packit b00eeb
	if (cell->next)
Packit b00eeb
		sec_remove_cell_ring (&block->unused_cells, cell);
Packit b00eeb
Packit b00eeb
	++block->n_used;
Packit b00eeb
	cell->tag = tag;
Packit b00eeb
	cell->requested = length;
Packit b00eeb
	sec_insert_cell_ring (&block->used_cells, cell);
Packit b00eeb
	memory = sec_cell_to_memory (cell);
Packit b00eeb
Packit b00eeb
#ifdef WITH_VALGRIND
Packit b00eeb
	VALGRIND_MAKE_MEM_UNDEFINED (memory, length);
Packit b00eeb
#endif
Packit b00eeb
Packit b00eeb
	return memset (memory, 0, length);
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static void*
Packit b00eeb
sec_free (Block *block, void *memory)
Packit b00eeb
{
Packit b00eeb
	Cell *cell, *other;
Packit b00eeb
	word_t *word;
Packit b00eeb
Packit b00eeb
	ASSERT (block);
Packit b00eeb
	ASSERT (memory);
Packit b00eeb
Packit b00eeb
	word = memory;
Packit b00eeb
	--word;
Packit b00eeb
Packit b00eeb
#ifdef WITH_VALGRIND
Packit b00eeb
	VALGRIND_MAKE_MEM_DEFINED (word, sizeof (word_t));
Packit b00eeb
#endif
Packit b00eeb
Packit b00eeb
	/* Lookup the meta for this memory block (using guard pointer) */
Packit b00eeb
	ASSERT (sec_is_valid_word (block, word));
Packit b00eeb
	ASSERT (pool_valid (*word));
Packit b00eeb
	cell = *word;
Packit b00eeb
Packit b00eeb
#ifdef WITH_VALGRIND
Packit b00eeb
	VALGRIND_MAKE_MEM_DEFINED (cell->words, cell->n_words * sizeof (word_t));
Packit b00eeb
#endif
Packit b00eeb
Packit b00eeb
	sec_check_guards (cell);
Packit b00eeb
	sec_clear_noaccess (memory, 0, cell->requested);
Packit b00eeb
Packit b00eeb
	sec_check_guards (cell);
Packit b00eeb
	ASSERT (cell->requested > 0);
Packit b00eeb
	ASSERT (cell->tag != NULL);
Packit b00eeb
Packit b00eeb
	/* Remove from the used cell ring */
Packit b00eeb
	sec_remove_cell_ring (&block->used_cells, cell);
Packit b00eeb
Packit b00eeb
	/* Find previous unallocated neighbor, and merge if possible */
Packit b00eeb
	other = sec_neighbor_before (block, cell);
Packit b00eeb
	if (other && other->requested == 0) {
Packit b00eeb
		ASSERT (other->tag == NULL);
Packit b00eeb
		ASSERT (other->next && other->prev);
Packit b00eeb
		other->n_words += cell->n_words;
Packit b00eeb
		sec_write_guards (other);
Packit b00eeb
		pool_free (cell);
Packit b00eeb
		cell = other;
Packit b00eeb
	}
Packit b00eeb
Packit b00eeb
	/* Find next unallocated neighbor, and merge if possible */
Packit b00eeb
	other = sec_neighbor_after (block, cell);
Packit b00eeb
	if (other && other->requested == 0) {
Packit b00eeb
		ASSERT (other->tag == NULL);
Packit b00eeb
		ASSERT (other->next && other->prev);
Packit b00eeb
		other->n_words += cell->n_words;
Packit b00eeb
		other->words = cell->words;
Packit b00eeb
		if (cell->next)
Packit b00eeb
			sec_remove_cell_ring (&block->unused_cells, cell);
Packit b00eeb
		sec_write_guards (other);
Packit b00eeb
		pool_free (cell);
Packit b00eeb
		cell = other;
Packit b00eeb
	}
Packit b00eeb
Packit b00eeb
	/* Add to the unused list if not already there */
Packit b00eeb
	if (!cell->next)
Packit b00eeb
		sec_insert_cell_ring (&block->unused_cells, cell);
Packit b00eeb
Packit b00eeb
	cell->tag = NULL;
Packit b00eeb
	cell->requested = 0;
Packit b00eeb
	--block->n_used;
Packit b00eeb
	return NULL;
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static void
Packit b00eeb
memcpy_with_vbits (void *dest,
Packit b00eeb
                   void *src,
Packit b00eeb
                   size_t length)
Packit b00eeb
{
Packit b00eeb
#ifdef WITH_VALGRIND
Packit b00eeb
	int vbits_setup = 0;
Packit b00eeb
	void *vbits = NULL;
Packit b00eeb
Packit b00eeb
	if (RUNNING_ON_VALGRIND) {
Packit b00eeb
		vbits = malloc (length);
Packit b00eeb
		if (vbits != NULL)
Packit b00eeb
			vbits_setup = VALGRIND_GET_VBITS (src, vbits, length);
Packit b00eeb
		VALGRIND_MAKE_MEM_DEFINED (src, length);
Packit b00eeb
	}
Packit b00eeb
#endif
Packit b00eeb
Packit b00eeb
	memcpy (dest, src, length);
Packit b00eeb
Packit b00eeb
#ifdef WITH_VALGRIND
Packit b00eeb
	if (vbits_setup == 1) {
Packit b00eeb
		VALGRIND_SET_VBITS (dest, vbits, length);
Packit b00eeb
		VALGRIND_SET_VBITS (src, vbits, length);
Packit b00eeb
	}
Packit b00eeb
	free (vbits);
Packit b00eeb
#endif
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static void*
Packit b00eeb
sec_realloc (Block *block,
Packit b00eeb
             const char *tag,
Packit b00eeb
             void *memory,
Packit b00eeb
             size_t length)
Packit b00eeb
{
Packit b00eeb
	Cell *cell, *other;
Packit b00eeb
	word_t *word;
Packit b00eeb
	size_t n_words;
Packit b00eeb
	size_t valid;
Packit b00eeb
	void *alloc;
Packit b00eeb
Packit b00eeb
	/* Standard realloc behavior, should have been handled elsewhere */
Packit b00eeb
	ASSERT (memory != NULL);
Packit b00eeb
	ASSERT (length > 0);
Packit b00eeb
	ASSERT (tag != NULL);
Packit b00eeb
Packit b00eeb
	/* Dig out where the meta should be */
Packit b00eeb
	word = memory;
Packit b00eeb
	--word;
Packit b00eeb
Packit b00eeb
#ifdef WITH_VALGRIND
Packit b00eeb
	VALGRIND_MAKE_MEM_DEFINED (word, sizeof (word_t));
Packit b00eeb
#endif
Packit b00eeb
Packit b00eeb
	ASSERT (sec_is_valid_word (block, word));
Packit b00eeb
	ASSERT (pool_valid (*word));
Packit b00eeb
	cell = *word;
Packit b00eeb
Packit b00eeb
	/* Validate that it's actually for real */
Packit b00eeb
	sec_check_guards (cell);
Packit b00eeb
	ASSERT (cell->requested > 0);
Packit b00eeb
	ASSERT (cell->tag != NULL);
Packit b00eeb
Packit b00eeb
	/* The amount of valid data */
Packit b00eeb
	valid = cell->requested;
Packit b00eeb
Packit b00eeb
	/* How many words we actually want */
Packit b00eeb
	n_words = sec_size_to_words (length) + 2;
Packit b00eeb
Packit b00eeb
	/* Less memory is required than is in the cell */
Packit b00eeb
	if (n_words <= cell->n_words) {
Packit b00eeb
Packit b00eeb
		/* TODO: No shrinking behavior yet */
Packit b00eeb
		cell->requested = length;
Packit b00eeb
		alloc = sec_cell_to_memory (cell);
Packit b00eeb
Packit b00eeb
		/*
Packit b00eeb
		 * Even though we may be reusing the same cell, that doesn't
Packit b00eeb
		 * mean that the allocation is shrinking. It could have shrunk
Packit b00eeb
		 * and is now expanding back some.
Packit b00eeb
		 */
Packit b00eeb
		if (length < valid)
Packit b00eeb
			sec_clear_undefined (alloc, length, valid);
Packit b00eeb
Packit b00eeb
		return alloc;
Packit b00eeb
	}
Packit b00eeb
Packit b00eeb
	/* Need braaaaaiiiiiinsss... */
Packit b00eeb
	while (cell->n_words < n_words) {
Packit b00eeb
Packit b00eeb
		/* See if we have a neighbor who can give us some memory */
Packit b00eeb
		other = sec_neighbor_after (block, cell);
Packit b00eeb
		if (!other || other->requested != 0)
Packit b00eeb
			break;
Packit b00eeb
Packit b00eeb
		/* Eat the whole neighbor if not too big */
Packit b00eeb
		if (n_words - cell->n_words + WASTE >= other->n_words) {
Packit b00eeb
			cell->n_words += other->n_words;
Packit b00eeb
			sec_write_guards (cell);
Packit b00eeb
			sec_remove_cell_ring (&block->unused_cells, other);
Packit b00eeb
			pool_free (other);
Packit b00eeb
Packit b00eeb
		/* Steal from the neighbor */
Packit b00eeb
		} else {
Packit b00eeb
			other->words += n_words - cell->n_words;
Packit b00eeb
			other->n_words -= n_words - cell->n_words;
Packit b00eeb
			sec_write_guards (other);
Packit b00eeb
			cell->n_words = n_words;
Packit b00eeb
			sec_write_guards (cell);
Packit b00eeb
		}
Packit b00eeb
	}
Packit b00eeb
Packit b00eeb
	if (cell->n_words >= n_words) {
Packit b00eeb
		cell->requested = length;
Packit b00eeb
		cell->tag = tag;
Packit b00eeb
		alloc = sec_cell_to_memory (cell);
Packit b00eeb
		sec_clear_undefined (alloc, valid, length);
Packit b00eeb
		return alloc;
Packit b00eeb
	}
Packit b00eeb
Packit b00eeb
	/* That didn't work, try alloc/free */
Packit b00eeb
	alloc = sec_alloc (block, tag, length);
Packit b00eeb
	if (alloc) {
Packit b00eeb
		memcpy_with_vbits (alloc, memory, valid);
Packit b00eeb
		sec_free (block, memory);
Packit b00eeb
	}
Packit b00eeb
Packit b00eeb
	return alloc;
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
Packit b00eeb
static size_t
Packit b00eeb
sec_allocated (Block *block, void *memory)
Packit b00eeb
{
Packit b00eeb
	Cell *cell;
Packit b00eeb
	word_t *word;
Packit b00eeb
Packit b00eeb
	ASSERT (block);
Packit b00eeb
	ASSERT (memory);
Packit b00eeb
Packit b00eeb
	word = memory;
Packit b00eeb
	--word;
Packit b00eeb
Packit b00eeb
#ifdef WITH_VALGRIND
Packit b00eeb
	VALGRIND_MAKE_MEM_DEFINED (word, sizeof (word_t));
Packit b00eeb
#endif
Packit b00eeb
Packit b00eeb
	/* Lookup the meta for this memory block (using guard pointer) */
Packit b00eeb
	ASSERT (sec_is_valid_word (block, word));
Packit b00eeb
	ASSERT (pool_valid (*word));
Packit b00eeb
	cell = *word;
Packit b00eeb
Packit b00eeb
	sec_check_guards (cell);
Packit b00eeb
	ASSERT (cell->requested > 0);
Packit b00eeb
	ASSERT (cell->tag != NULL);
Packit b00eeb
Packit b00eeb
#ifdef WITH_VALGRIND
Packit b00eeb
	VALGRIND_MAKE_MEM_NOACCESS (word, sizeof (word_t));
Packit b00eeb
#endif
Packit b00eeb
Packit b00eeb
	return cell->requested;
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static void
Packit b00eeb
sec_validate (Block *block)
Packit b00eeb
{
Packit b00eeb
	Cell *cell;
Packit b00eeb
	word_t *word, *last;
Packit b00eeb
Packit b00eeb
#ifdef WITH_VALGRIND
Packit b00eeb
	if (RUNNING_ON_VALGRIND)
Packit b00eeb
		return;
Packit b00eeb
#endif
Packit b00eeb
Packit b00eeb
	word = block->words;
Packit b00eeb
	last = word + block->n_words;
Packit b00eeb
Packit b00eeb
	for (;;) {
Packit b00eeb
		ASSERT (word < last);
Packit b00eeb
Packit b00eeb
		ASSERT (sec_is_valid_word (block, word));
Packit b00eeb
		ASSERT (pool_valid (*word));
Packit b00eeb
		cell = *word;
Packit b00eeb
Packit b00eeb
		/* Validate that it's actually for real */
Packit b00eeb
		sec_check_guards (cell);
Packit b00eeb
Packit b00eeb
		/* Is it an allocated block? */
Packit b00eeb
		if (cell->requested > 0) {
Packit b00eeb
			ASSERT (cell->tag != NULL);
Packit b00eeb
			ASSERT (cell->next != NULL);
Packit b00eeb
			ASSERT (cell->prev != NULL);
Packit b00eeb
			ASSERT (cell->next->prev == cell);
Packit b00eeb
			ASSERT (cell->prev->next == cell);
Packit b00eeb
			ASSERT (cell->requested <= (cell->n_words - 2) * sizeof (word_t));
Packit b00eeb
Packit b00eeb
		/* An unused block */
Packit b00eeb
		} else {
Packit b00eeb
			ASSERT (cell->tag == NULL);
Packit b00eeb
			ASSERT (cell->next != NULL);
Packit b00eeb
			ASSERT (cell->prev != NULL);
Packit b00eeb
			ASSERT (cell->next->prev == cell);
Packit b00eeb
			ASSERT (cell->prev->next == cell);
Packit b00eeb
		}
Packit b00eeb
Packit b00eeb
		word += cell->n_words;
Packit b00eeb
		if (word == last)
Packit b00eeb
			break;
Packit b00eeb
	}
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
/* -----------------------------------------------------------------------------
Packit b00eeb
 * LOCKED MEMORY
Packit b00eeb
 */
Packit b00eeb
Packit b00eeb
static void*
Packit b00eeb
sec_acquire_pages (size_t *sz,
Packit b00eeb
                   const char *during_tag)
Packit b00eeb
{
Packit b00eeb
	void *pages;
Packit b00eeb
	unsigned long pgsize;
Packit b00eeb
Packit b00eeb
	ASSERT (sz);
Packit b00eeb
	ASSERT (*sz);
Packit b00eeb
	ASSERT (during_tag);
Packit b00eeb
Packit b00eeb
	/* Make sure sz is a multiple of the page size */
Packit b00eeb
	pgsize = getpagesize ();
Packit b00eeb
	*sz = (*sz + pgsize -1) & ~(pgsize - 1);
Packit b00eeb
Packit b00eeb
#if defined(HAVE_MLOCK)
Packit b00eeb
	pages = mmap (0, *sz, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
Packit b00eeb
	if (pages == MAP_FAILED) {
Packit b00eeb
		if (show_warning && egg_secure_warnings)
Packit b00eeb
			fprintf (stderr, "couldn't map %lu bytes of memory (%s): %s\n",
Packit b00eeb
			         (unsigned long)*sz, during_tag, strerror (errno));
Packit b00eeb
		show_warning = 0;
Packit b00eeb
		return NULL;
Packit b00eeb
	}
Packit b00eeb
Packit b00eeb
	if (mlock (pages, *sz) < 0) {
Packit b00eeb
		if (show_warning && egg_secure_warnings && errno != EPERM) {
Packit b00eeb
			fprintf (stderr, "couldn't lock %lu bytes of memory (%s): %s\n",
Packit b00eeb
			         (unsigned long)*sz, during_tag, strerror (errno));
Packit b00eeb
			show_warning = 0;
Packit b00eeb
		}
Packit b00eeb
		munmap (pages, *sz);
Packit b00eeb
		return NULL;
Packit b00eeb
	}
Packit b00eeb
Packit b00eeb
	DEBUG_ALLOC ("gkr-secure-memory: new block ", *sz);
Packit b00eeb
Packit b00eeb
	show_warning = 1;
Packit b00eeb
	return pages;
Packit b00eeb
Packit b00eeb
#else
Packit b00eeb
	if (show_warning && egg_secure_warnings)
Packit b00eeb
		fprintf (stderr, "your system does not support private memory");
Packit b00eeb
	show_warning = 0;
Packit b00eeb
	return NULL;
Packit b00eeb
#endif
Packit b00eeb
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static void
Packit b00eeb
sec_release_pages (void *pages, size_t sz)
Packit b00eeb
{
Packit b00eeb
	ASSERT (pages);
Packit b00eeb
	ASSERT (sz % getpagesize () == 0);
Packit b00eeb
Packit b00eeb
#if defined(HAVE_MLOCK)
Packit b00eeb
	if (munlock (pages, sz) < 0 && egg_secure_warnings)
Packit b00eeb
		fprintf (stderr, "couldn't unlock private memory: %s\n", strerror (errno));
Packit b00eeb
Packit b00eeb
	if (munmap (pages, sz) < 0 && egg_secure_warnings)
Packit b00eeb
		fprintf (stderr, "couldn't unmap private anonymous memory: %s\n", strerror (errno));
Packit b00eeb
Packit b00eeb
	DEBUG_ALLOC ("gkr-secure-memory: freed block ", sz);
Packit b00eeb
Packit b00eeb
#else
Packit b00eeb
	ASSERT (FALSE);
Packit b00eeb
#endif
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
/* -----------------------------------------------------------------------------
Packit b00eeb
 * MANAGE DIFFERENT BLOCKS
Packit b00eeb
 */
Packit b00eeb
Packit b00eeb
static Block *all_blocks = NULL;
Packit b00eeb
Packit b00eeb
static Block*
Packit b00eeb
sec_block_create (size_t size,
Packit b00eeb
                  const char *during_tag)
Packit b00eeb
{
Packit b00eeb
	Block *block;
Packit b00eeb
	Cell *cell;
Packit b00eeb
Packit b00eeb
	ASSERT (during_tag);
Packit b00eeb
Packit b00eeb
	/* We can force all all memory to be malloced */
Packit b00eeb
	if (getenv ("SECMEM_FORCE_FALLBACK"))
Packit b00eeb
		return NULL;
Packit b00eeb
Packit b00eeb
	block = pool_alloc ();
Packit b00eeb
	if (!block)
Packit b00eeb
		return NULL;
Packit b00eeb
Packit b00eeb
	cell = pool_alloc ();
Packit b00eeb
	if (!cell) {
Packit b00eeb
		pool_free (block);
Packit b00eeb
		return NULL;
Packit b00eeb
	}
Packit b00eeb
Packit b00eeb
	/* The size above is a minimum, we're free to go bigger */
Packit b00eeb
	if (size < DEFAULT_BLOCK_SIZE)
Packit b00eeb
		size = DEFAULT_BLOCK_SIZE;
Packit b00eeb
Packit b00eeb
	block->words = sec_acquire_pages (&size, during_tag);
Packit b00eeb
	block->n_words = size / sizeof (word_t);
Packit b00eeb
	if (!block->words) {
Packit b00eeb
		pool_free (block);
Packit b00eeb
		pool_free (cell);
Packit b00eeb
		return NULL;
Packit b00eeb
	}
Packit b00eeb
Packit b00eeb
#ifdef WITH_VALGRIND
Packit b00eeb
	VALGRIND_MAKE_MEM_DEFINED (block->words, size);
Packit b00eeb
#endif
Packit b00eeb
Packit b00eeb
	/* The first cell to allocate from */
Packit b00eeb
	cell->words = block->words;
Packit b00eeb
	cell->n_words = block->n_words;
Packit b00eeb
	cell->requested = 0;
Packit b00eeb
	sec_write_guards (cell);
Packit b00eeb
	sec_insert_cell_ring (&block->unused_cells, cell);
Packit b00eeb
Packit b00eeb
	block->next = all_blocks;
Packit b00eeb
	all_blocks = block;
Packit b00eeb
Packit b00eeb
	return block;
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static void
Packit b00eeb
sec_block_destroy (Block *block)
Packit b00eeb
{
Packit b00eeb
	Block *bl, **at;
Packit b00eeb
	Cell *cell;
Packit b00eeb
Packit b00eeb
	ASSERT (block);
Packit b00eeb
	ASSERT (block->words);
Packit b00eeb
	ASSERT (block->n_used == 0);
Packit b00eeb
Packit b00eeb
	/* Remove from the list */
Packit b00eeb
	for (at = &all_blocks, bl = *at; bl; at = &bl->next, bl = *at) {
Packit b00eeb
		if (bl == block) {
Packit b00eeb
			*at = block->next;
Packit b00eeb
			break;
Packit b00eeb
		}
Packit b00eeb
	}
Packit b00eeb
Packit b00eeb
	/* Must have been found */
Packit b00eeb
	ASSERT (bl == block);
Packit b00eeb
	ASSERT (block->used_cells == NULL);
Packit b00eeb
Packit b00eeb
	/* Release all the meta data cells */
Packit b00eeb
	while (block->unused_cells) {
Packit b00eeb
		cell = block->unused_cells;
Packit b00eeb
		sec_remove_cell_ring (&block->unused_cells, cell);
Packit b00eeb
		pool_free (cell);
Packit b00eeb
	}
Packit b00eeb
Packit b00eeb
	/* Release all pages of secure memory */
Packit b00eeb
	sec_release_pages (block->words, block->n_words * sizeof (word_t));
Packit b00eeb
Packit b00eeb
	pool_free (block);
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
/* ------------------------------------------------------------------------
Packit b00eeb
 * PUBLIC FUNCTIONALITY
Packit b00eeb
 */
Packit b00eeb
Packit b00eeb
void*
Packit b00eeb
egg_secure_alloc_full (const char *tag,
Packit b00eeb
                       size_t length,
Packit b00eeb
                       int flags)
Packit b00eeb
{
Packit b00eeb
	Block *block;
Packit b00eeb
	void *memory = NULL;
Packit b00eeb
Packit b00eeb
	if (tag == NULL)
Packit b00eeb
		tag = "?";
Packit b00eeb
Packit b00eeb
	if (length > 0xFFFFFFFF / 2) {
Packit b00eeb
		if (egg_secure_warnings)
Packit b00eeb
			fprintf (stderr, "tried to allocate an insane amount of memory: %lu\n",
Packit b00eeb
			         (unsigned long)length);
Packit b00eeb
		return NULL;
Packit b00eeb
	}
Packit b00eeb
Packit b00eeb
	/* Can't allocate zero bytes */
Packit b00eeb
	if (length == 0)
Packit b00eeb
		return NULL;
Packit b00eeb
Packit b00eeb
	DO_LOCK ();
Packit b00eeb
Packit b00eeb
		for (block = all_blocks; block; block = block->next) {
Packit b00eeb
			memory = sec_alloc (block, tag, length);
Packit b00eeb
			if (memory)
Packit b00eeb
				break;
Packit b00eeb
		}
Packit b00eeb
Packit b00eeb
		/* None of the current blocks have space, allocate new */
Packit b00eeb
		if (!memory) {
Packit b00eeb
			block = sec_block_create (length, tag);
Packit b00eeb
			if (block)
Packit b00eeb
				memory = sec_alloc (block, tag, length);
Packit b00eeb
		}
Packit b00eeb
Packit b00eeb
#ifdef WITH_VALGRIND
Packit b00eeb
		if (memory != NULL)
Packit b00eeb
			VALGRIND_MALLOCLIKE_BLOCK (memory, length, sizeof (void*), 1);
Packit b00eeb
#endif
Packit b00eeb
Packit b00eeb
	DO_UNLOCK ();
Packit b00eeb
Packit b00eeb
	if (!memory && (flags & EGG_SECURE_USE_FALLBACK) && EGG_SECURE_GLOBALS.fallback != NULL) {
Packit b00eeb
		memory = EGG_SECURE_GLOBALS.fallback (NULL, length);
Packit b00eeb
		if (memory) /* Our returned memory is always zeroed */
Packit b00eeb
			memset (memory, 0, length);
Packit b00eeb
	}
Packit b00eeb
Packit b00eeb
	if (!memory)
Packit b00eeb
		errno = ENOMEM;
Packit b00eeb
Packit b00eeb
	return memory;
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
void*
Packit b00eeb
egg_secure_realloc_full (const char *tag,
Packit b00eeb
                         void *memory,
Packit b00eeb
                         size_t length,
Packit b00eeb
                         int flags)
Packit b00eeb
{
Packit b00eeb
	Block *block = NULL;
Packit b00eeb
	size_t previous = 0;
Packit b00eeb
	int donew = 0;
Packit b00eeb
	void *alloc = NULL;
Packit b00eeb
Packit b00eeb
	if (tag == NULL)
Packit b00eeb
		tag = "?";
Packit b00eeb
Packit b00eeb
	if (length > 0xFFFFFFFF / 2) {
Packit b00eeb
		if (egg_secure_warnings)
Packit b00eeb
			fprintf (stderr, "tried to allocate an insane amount of memory: %lu\n",
Packit b00eeb
			         (unsigned long)length);
Packit b00eeb
		return NULL;
Packit b00eeb
	}
Packit b00eeb
Packit b00eeb
	if (memory == NULL)
Packit b00eeb
		return egg_secure_alloc_full (tag, length, flags);
Packit b00eeb
	if (!length) {
Packit b00eeb
		egg_secure_free_full (memory, flags);
Packit b00eeb
		return NULL;
Packit b00eeb
	}
Packit b00eeb
Packit b00eeb
	DO_LOCK ();
Packit b00eeb
Packit b00eeb
		/* Find out where it belongs to */
Packit b00eeb
		for (block = all_blocks; block; block = block->next) {
Packit b00eeb
			if (sec_is_valid_word (block, memory)) {
Packit b00eeb
				previous = sec_allocated (block, memory);
Packit b00eeb
Packit b00eeb
#ifdef WITH_VALGRIND
Packit b00eeb
				/* Let valgrind think we are unallocating so that it'll validate */
Packit b00eeb
				VALGRIND_FREELIKE_BLOCK (memory, sizeof (word_t));
Packit b00eeb
#endif
Packit b00eeb
Packit b00eeb
				alloc = sec_realloc (block, tag, memory, length);
Packit b00eeb
Packit b00eeb
#ifdef WITH_VALGRIND
Packit b00eeb
				/* Now tell valgrind about either the new block or old one */
Packit b00eeb
				VALGRIND_MALLOCLIKE_BLOCK (alloc ? alloc : memory,
Packit b00eeb
				                           alloc ? length : previous,
Packit b00eeb
				                           sizeof (word_t), 1);
Packit b00eeb
#endif
Packit b00eeb
				break;
Packit b00eeb
			}
Packit b00eeb
		}
Packit b00eeb
Packit b00eeb
		/* If it didn't work we may need to allocate a new block */
Packit b00eeb
		if (block && !alloc)
Packit b00eeb
			donew = 1;
Packit b00eeb
Packit b00eeb
		if (block && block->n_used == 0)
Packit b00eeb
			sec_block_destroy (block);
Packit b00eeb
Packit b00eeb
	DO_UNLOCK ();
Packit b00eeb
Packit b00eeb
	if (!block) {
Packit b00eeb
		if ((flags & EGG_SECURE_USE_FALLBACK) && EGG_SECURE_GLOBALS.fallback) {
Packit b00eeb
			/*
Packit b00eeb
			 * In this case we can't zero the returned memory,
Packit b00eeb
			 * because we don't know what the block size was.
Packit b00eeb
			 */
Packit b00eeb
			return EGG_SECURE_GLOBALS.fallback (memory, length);
Packit b00eeb
		} else {
Packit b00eeb
			if (egg_secure_warnings)
Packit b00eeb
				fprintf (stderr, "memory does not belong to secure memory pool: 0x%08lx\n",
Packit b00eeb
				         (unsigned long)memory);
Packit b00eeb
			ASSERT (0 && "memory does does not belong to secure memory pool");
Packit b00eeb
			return NULL;
Packit b00eeb
		}
Packit b00eeb
	}
Packit b00eeb
Packit b00eeb
	if (donew) {
Packit b00eeb
		alloc = egg_secure_alloc_full (tag, length, flags);
Packit b00eeb
		if (alloc) {
Packit b00eeb
			memcpy_with_vbits (alloc, memory, previous);
Packit b00eeb
			egg_secure_free_full (memory, flags);
Packit b00eeb
		}
Packit b00eeb
	}
Packit b00eeb
Packit b00eeb
	if (!alloc)
Packit b00eeb
		errno = ENOMEM;
Packit b00eeb
Packit b00eeb
	return alloc;
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
void
Packit b00eeb
egg_secure_free (void *memory)
Packit b00eeb
{
Packit b00eeb
	egg_secure_free_full (memory, EGG_SECURE_USE_FALLBACK);
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
void
Packit b00eeb
egg_secure_free_full (void *memory, int flags)
Packit b00eeb
{
Packit b00eeb
	Block *block = NULL;
Packit b00eeb
Packit b00eeb
	if (memory == NULL)
Packit b00eeb
		return;
Packit b00eeb
Packit b00eeb
	DO_LOCK ();
Packit b00eeb
Packit b00eeb
		/* Find out where it belongs to */
Packit b00eeb
		for (block = all_blocks; block; block = block->next) {
Packit b00eeb
			if (sec_is_valid_word (block, memory))
Packit b00eeb
				break;
Packit b00eeb
		}
Packit b00eeb
Packit b00eeb
#ifdef WITH_VALGRIND
Packit b00eeb
		/* We like valgrind's warnings, so give it a first whack at checking for errors */
Packit b00eeb
		if (block != NULL || !(flags & EGG_SECURE_USE_FALLBACK))
Packit b00eeb
			VALGRIND_FREELIKE_BLOCK (memory, sizeof (word_t));
Packit b00eeb
#endif
Packit b00eeb
Packit b00eeb
		if (block != NULL) {
Packit b00eeb
			sec_free (block, memory);
Packit b00eeb
			if (block->n_used == 0)
Packit b00eeb
				sec_block_destroy (block);
Packit b00eeb
		}
Packit b00eeb
Packit b00eeb
	DO_UNLOCK ();
Packit b00eeb
Packit b00eeb
	if (!block) {
Packit b00eeb
		if ((flags & EGG_SECURE_USE_FALLBACK) && EGG_SECURE_GLOBALS.fallback) {
Packit b00eeb
			EGG_SECURE_GLOBALS.fallback (memory, 0);
Packit b00eeb
		} else {
Packit b00eeb
			if (egg_secure_warnings)
Packit b00eeb
				fprintf (stderr, "memory does not belong to secure memory pool: 0x%08lx\n",
Packit b00eeb
				         (unsigned long)memory);
Packit b00eeb
			ASSERT (0 && "memory does does not belong to secure memory pool");
Packit b00eeb
		}
Packit b00eeb
	}
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
int
Packit b00eeb
egg_secure_check (const void *memory)
Packit b00eeb
{
Packit b00eeb
	Block *block = NULL;
Packit b00eeb
Packit b00eeb
	DO_LOCK ();
Packit b00eeb
Packit b00eeb
		/* Find out where it belongs to */
Packit b00eeb
		for (block = all_blocks; block; block = block->next) {
Packit b00eeb
			if (sec_is_valid_word (block, (word_t*)memory))
Packit b00eeb
				break;
Packit b00eeb
		}
Packit b00eeb
Packit b00eeb
	DO_UNLOCK ();
Packit b00eeb
Packit b00eeb
	return block == NULL ? 0 : 1;
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
void
Packit b00eeb
egg_secure_validate (void)
Packit b00eeb
{
Packit b00eeb
	Block *block = NULL;
Packit b00eeb
Packit b00eeb
	DO_LOCK ();
Packit b00eeb
Packit b00eeb
		for (block = all_blocks; block; block = block->next)
Packit b00eeb
			sec_validate (block);
Packit b00eeb
Packit b00eeb
	DO_UNLOCK ();
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
Packit b00eeb
static egg_secure_rec *
Packit b00eeb
records_for_ring (Cell *cell_ring,
Packit b00eeb
                  egg_secure_rec *records,
Packit b00eeb
                  unsigned int *count,
Packit b00eeb
                  unsigned int *total)
Packit b00eeb
{
Packit b00eeb
	egg_secure_rec *new_rec;
Packit b00eeb
	unsigned int allocated = *count;
Packit b00eeb
	Cell *cell;
Packit b00eeb
Packit b00eeb
	cell = cell_ring;
Packit b00eeb
	do {
Packit b00eeb
		if (*count >= allocated) {
Packit b00eeb
			new_rec = realloc (records, sizeof (egg_secure_rec) * (allocated + 32));
Packit b00eeb
			if (new_rec == NULL) {
Packit b00eeb
				*count = 0;
Packit b00eeb
				free (records);
Packit b00eeb
				return NULL;
Packit b00eeb
			} else {
Packit b00eeb
				records = new_rec;
Packit b00eeb
				allocated += 32;
Packit b00eeb
			}
Packit b00eeb
		}
Packit b00eeb
Packit b00eeb
		if (cell != NULL) {
Packit b00eeb
			records[*count].request_length = cell->requested;
Packit b00eeb
			records[*count].block_length = cell->n_words * sizeof (word_t);
Packit b00eeb
			records[*count].tag = cell->tag;
Packit b00eeb
			(*count)++;
Packit b00eeb
			(*total) += cell->n_words;
Packit b00eeb
			cell = cell->next;
Packit b00eeb
		}
Packit b00eeb
	} while (cell != NULL && cell != cell_ring);
Packit b00eeb
Packit b00eeb
	return records;
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
egg_secure_rec *
Packit b00eeb
egg_secure_records (unsigned int *count)
Packit b00eeb
{
Packit b00eeb
	egg_secure_rec *records = NULL;
Packit b00eeb
	Block *block = NULL;
Packit b00eeb
	unsigned int total;
Packit b00eeb
Packit b00eeb
	*count = 0;
Packit b00eeb
Packit b00eeb
	DO_LOCK ();
Packit b00eeb
Packit b00eeb
		for (block = all_blocks; block != NULL; block = block->next) {
Packit b00eeb
			total = 0;
Packit b00eeb
Packit b00eeb
			records = records_for_ring (block->unused_cells, records, count, &total);
Packit b00eeb
			if (records == NULL)
Packit b00eeb
				break;
Packit b00eeb
			records = records_for_ring (block->used_cells, records, count, &total);
Packit b00eeb
			if (records == NULL)
Packit b00eeb
				break;
Packit b00eeb
Packit b00eeb
			/* Make sure this actualy accounts for all memory */
Packit b00eeb
			ASSERT (total == block->n_words);
Packit b00eeb
		}
Packit b00eeb
Packit b00eeb
	DO_UNLOCK ();
Packit b00eeb
Packit b00eeb
	return records;
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
char*
Packit b00eeb
egg_secure_strdup_full (const char *tag,
Packit b00eeb
                        const char *str,
Packit b00eeb
                        int options)
Packit b00eeb
{
Packit b00eeb
	size_t len;
Packit b00eeb
	char *res;
Packit b00eeb
Packit b00eeb
	if (!str)
Packit b00eeb
		return NULL;
Packit b00eeb
Packit b00eeb
	len = strlen (str) + 1;
Packit b00eeb
	res = (char *)egg_secure_alloc_full (tag, len, options);
Packit b00eeb
	strcpy (res, str);
Packit b00eeb
	return res;
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
char *
Packit b00eeb
egg_secure_strndup_full (const char *tag,
Packit b00eeb
                         const char *str,
Packit b00eeb
                         size_t length,
Packit b00eeb
                         int options)
Packit b00eeb
{
Packit b00eeb
	size_t len;
Packit b00eeb
	char *res;
Packit b00eeb
	const char *end;
Packit b00eeb
Packit b00eeb
	if (!str)
Packit b00eeb
		return NULL;
Packit b00eeb
Packit b00eeb
	end = memchr (str, '\0', length);
Packit b00eeb
	if (end != NULL)
Packit b00eeb
		length = (end - str);
Packit b00eeb
	len = length + 1;
Packit b00eeb
	res = (char *)egg_secure_alloc_full (tag, len, options);
Packit b00eeb
	memcpy (res, str, len);
Packit b00eeb
	return res;
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
void
Packit b00eeb
egg_secure_clear (void *p, size_t length)
Packit b00eeb
{
Packit b00eeb
	volatile char *vp;
Packit b00eeb
Packit b00eeb
	if (p == NULL)
Packit b00eeb
		return;
Packit b00eeb
Packit b00eeb
        vp = (volatile char*)p;
Packit b00eeb
        while (length) {
Packit b00eeb
	        *vp = 0xAA;
Packit b00eeb
	        vp++;
Packit b00eeb
	        length--;
Packit b00eeb
	}
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
void
Packit b00eeb
egg_secure_strclear (char *str)
Packit b00eeb
{
Packit b00eeb
	if (!str)
Packit b00eeb
		return;
Packit b00eeb
	egg_secure_clear ((unsigned char*)str, strlen (str));
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
void
Packit b00eeb
egg_secure_strfree (char *str)
Packit b00eeb
{
Packit b00eeb
	/*
Packit b00eeb
	 * If we're using unpageable 'secure' memory, then the free call
Packit b00eeb
	 * should zero out the memory, but because on certain platforms
Packit b00eeb
	 * we may be using normal memory, zero it out here just in case.
Packit b00eeb
	 */
Packit b00eeb
Packit b00eeb
	egg_secure_strclear (str);
Packit b00eeb
	egg_secure_free_full (str, EGG_SECURE_USE_FALLBACK);
Packit b00eeb
}