static size_t sec_allocated (Block *block, void *memory) { Cell *cell; 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; sec_check_guards (cell); ASSERT (cell->next == NULL); ASSERT (cell->prev == NULL); ASSERT (cell->allocated > 0); #ifdef WITH_VALGRIND VALGRIND_MAKE_MEM_NOACCESS (word, sizeof (word_t)); #endif return cell->allocated; }
static Cell* sec_neighbor_after (Block *block, Cell *cell) { word_t *word; ASSERT (cell); ASSERT (block); word = cell->words + cell->n_words; if (!sec_is_valid_word (block, word)) return NULL; #ifdef WITH_VALGRIND VALGRIND_MAKE_MEM_DEFINED (word, sizeof (word_t)); #endif cell = *word; sec_check_guards (cell); #ifdef WITH_VALGRIND VALGRIND_MAKE_MEM_NOACCESS (word, sizeof (word_t)); #endif return cell; }
static void sec_validate (Block *block) { Cell *cell; word_t *word, *last; #ifdef WITH_VALGRIND if (RUNNING_ON_VALGRIND) return; #endif word = block->words; last = word + block->n_words; for (;;) { ASSERT (word < last); ASSERT (sec_is_valid_word (block, word)); ASSERT (pool_valid (*word)); cell = *word; /* Validate that it's actually for real */ sec_check_guards (cell); /* Is it an allocated block? */ 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->tag == NULL); ASSERT (cell->next != NULL); ASSERT (cell->prev != NULL); ASSERT (cell->next->prev == cell); ASSERT (cell->prev->next == cell); } word += cell->n_words; if (word == last) break; } }
int egg_secure_check (const void *memory) { Block *block = NULL; DO_LOCK (); /* Find out where it belongs to */ for (block = all_blocks; block; block = block->next) { if (sec_is_valid_word (block, (word_t*)memory)) break; } DO_UNLOCK (); return block == NULL ? 0 : 1; }
void egg_secure_free_full (void *memory, int flags) { Block *block = NULL; if (memory == NULL) return; DO_LOCK (); /* Find out where it belongs to */ for (block = all_blocks; block; block = block->next) { if (sec_is_valid_word (block, memory)) break; } #ifdef WITH_VALGRIND /* We like valgrind's warnings, so give it a first whack at checking for errors */ if (block != NULL || !(flags & GKR_SECURE_USE_FALLBACK)) VALGRIND_FREELIKE_BLOCK (memory, sizeof (word_t)); #endif if (block != NULL) { sec_free (block, memory); if (block->used == 0) sec_block_destroy (block); } DO_UNLOCK (); if (!block) { if ((flags & GKR_SECURE_USE_FALLBACK)) { egg_memory_fallback (memory, 0); } else { if (egg_secure_warnings) fprintf (stderr, "memory does not belong to mate-keyring: 0x%08lx\n", (unsigned long)memory); ASSERT (0 && "memory does does not belong to mate-keyring"); } } }
static void sec_validate (Block *block) { Cell *cell; word_t *word, *last; word = block->words; last = word + block->n_words; for (;;) { ASSERT (word < last); ASSERT (sec_is_valid_word (block, word)); ASSERT (pool_valid (*word)); cell = *word; /* Validate that it's actually for real */ 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)); /* An unused block */ } else { ASSERT (cell->next); ASSERT (cell->prev); ASSERT (cell->next->prev == cell); ASSERT (cell->prev->next == cell); } word += cell->n_words; if (word == last) break; } }
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; }
void* egg_secure_realloc_full (void *memory, size_t length, int flags) { Block *block = NULL; size_t previous = 0; int donew = 0; void *alloc = NULL; if (length > 0xFFFFFFFF / 2) { if (egg_secure_warnings) fprintf (stderr, "tried to allocate an insane amount of memory: %lu\n", (unsigned long)length); return NULL; } 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 (block = all_blocks; block; block = block->next) { if (sec_is_valid_word (block, memory)) { previous = sec_allocated (block, memory); #ifdef WITH_VALGRIND /* Let valgrind think we are unallocating so that it'll validate */ VALGRIND_FREELIKE_BLOCK (memory, sizeof (word_t)); #endif alloc = sec_realloc (block, memory, length); #ifdef WITH_VALGRIND /* Now tell valgrind about either the new block or old one */ VALGRIND_MALLOCLIKE_BLOCK (alloc ? alloc : memory, alloc ? length : previous, sizeof (word_t), 1); #endif break; } } /* If it didn't work we may need to allocate a new block */ if (block && !alloc) donew = 1; if (block && block->used == 0) sec_block_destroy (block); DO_UNLOCK (); 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 (memory, length); } else { if (egg_secure_warnings) fprintf (stderr, "memory does not belong to mate-keyring: 0x%08lx\n", (unsigned long)memory); ASSERT (0 && "memory does does not belong to mate-keyring"); return NULL; } } if (donew) { alloc = egg_secure_alloc_full (length, flags); if (alloc) { memcpy (alloc, memory, previous); egg_secure_free_full (memory, flags); } } if (!alloc) errno = ENOMEM; return alloc; }