Example #1
0
static Block* 
sec_block_create (size_t size,
                  const char *during_tag)
{
	Block *block;
	Cell *cell;

	ASSERT (during_tag);

	/* We can force all all memory to be malloced */
	if (getenv ("SECMEM_FORCE_FALLBACK"))
		return NULL;

	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;
		
	block->words = sec_acquire_pages (&size, during_tag);
	block->n_words = size / sizeof (word_t);
	if (!block->words) {
		pool_free (block);
		pool_free (cell);
		return NULL;
	}
	
#ifdef WITH_VALGRIND
	VALGRIND_MAKE_MEM_DEFINED (block->words, size);
#endif
	
	/* The first cell to allocate from */
	cell->words = block->words;
	cell->n_words = block->n_words;
	cell->requested = 0;
	sec_write_guards (cell);
	sec_insert_cell_ring (&block->unused_cells, cell);

	block->next = all_blocks;
	all_blocks = block;
	
	return block;
}
static void*
sec_realloc (Block *block, 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);

	/* Dig out where the meta should be */
	word = memory;
	--word;

#ifdef WITH_VALGRIND
	VALGRIND_MAKE_MEM_DEFINED (word, sizeof (word_t));
#endif

	ASSERT (sec_is_valid_word (block, word));
	ASSERT (pool_valid (*word));
	cell = *word;

	/* 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 than is in the cell */
	if (n_words <= cell->n_words) {

		/* TODO: No shrinking behavior yet */
		cell->allocated = length;
		alloc = sec_cell_to_memory (cell);

#ifdef WITH_VALGRIND
		VALGRIND_MAKE_MEM_DEFINED (alloc, length);
#endif

		/*
		 * Even though we may be reusing the same cell, that doesn't
		 * mean that the allocation is shrinking. It could have shrunk
		 * and is now expanding back some.
		 */
		if (length < valid)
			return sec_clear_memory (alloc, length, valid);
		else
			return alloc;
	}

	/* 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 + WASTE >= other->n_words) {
			cell->n_words += other->n_words;
			sec_write_guards (cell);
			sec_remove_cell_ring (&block->unused, other);
			pool_free (other);

		/* 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);
		}
	}

	if (cell->n_words >= n_words) {
		cell->allocated = length;
		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);
	if (alloc) {
		memcpy (alloc, memory, valid);
		sec_free (block, memory);
	}

	return alloc;
}
static void*
sec_free (Block *block, void *memory)
{
	Cell *cell, *other;
	word_t *word;

	ASSERT (block);
	ASSERT (memory);

	word = memory;
	--word;

#ifdef WITH_VALGRIND
	VALGRIND_MAKE_MEM_DEFINED (word, sizeof (word_t));
#endif

	/* Lookup the meta for this memory block (using guard pointer) */
	ASSERT (sec_is_valid_word (block, word));
	ASSERT (pool_valid (*word));
	cell = *word;

#ifdef WITH_VALGRIND
	VALGRIND_MAKE_MEM_DEFINED (cell->words, cell->n_words * sizeof (word_t));
#endif

	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 void*
sec_alloc (Block *block, size_t length)
{
	Cell *cell, *other;
	size_t n_words;
	void *memory;

	ASSERT (block);
	ASSERT (length);

	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;
		}
	}

	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;
	}

	if (cell->next)
		sec_remove_cell_ring (&block->unused, cell);

	++block->used;
	cell->allocated = length;
	memory = sec_cell_to_memory (cell);

#ifdef WITH_VALGRIND
	VALGRIND_MAKE_MEM_UNDEFINED (memory, length);
#endif

	return memset (memory, 0, length);
}