data-stack.c revision e2588872c1fe79642589b805aaab9fbb6750771b
45312f52ff3a3d4c137447be4c7556500c2f8bf2Timo Sirainen/* Copyright (c) 2002-2016 Dovecot authors, see the included COPYING file */
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen/* @UNSAFE: whole file */
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen/* Initial stack size - this should be kept in a size that doesn't exceed
f6c1297c26b355c4aec2a08978f51ec3efecb351Timo Sirainen in a normal use to avoid extra malloc()ing. */
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen# define CLEAR_CHR 0xD5 /* D5 is mnemonic for "Data 5tack" */
ef2b2ef2e6a6eb5e4667f2e63faae8a3b646e8baTimo Sirainen# define BLOCK_CANARY ((void *)0xBADBADD5BADBADD5) /* contains 'D5' */
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen# define BLOCK_CANARY_CHECK(block) i_assert((block)->canary == BLOCK_CANARY)
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen# define ALLOC_SIZE(size) (MEM_ALIGN(sizeof(size_t)) + MEM_ALIGN(size + SENTRY_COUNT))
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen# define BLOCK_CANARY_CHECK(block) do { ; } while(0)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen /* NULL or a poison value, just in case something accesses
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen the memory in front of an allocated area */
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen /* unsigned char data[]; */
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#define SIZEOF_MEMBLOCK MEM_ALIGN(sizeof(struct stack_block))
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen/* current_frame_block contains last t_push()ed frames. After that new
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen stack_frame_block is created and it's ->prev is set to
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen current_frame_block. */
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen /* Fairly arbitrary profiling data */
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen unsigned long long alloc_bytes[BLOCK_FRAME_COUNT];
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenstatic int frame_pos = BLOCK_FRAME_COUNT-1; /* in current_frame_block */
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenstatic struct stack_frame_block *current_frame_block;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenstatic struct stack_frame_block *unused_frame_blocks;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenstatic struct stack_block *current_block; /* block now used for allocation */
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenstatic struct stack_block *unused_block; /* largest unused block is kept here */
63110c906fdb5b4e8c870e76fa3f244dac4b043dTimo Sirainenstatic union {
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenunsigned char *data_stack_after_last_alloc(struct stack_block *block)
ef2b2ef2e6a6eb5e4667f2e63faae8a3b646e8baTimo Sirainen return STACK_BLOCK_DATA(block) + (block->size - block->left);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenstatic void data_stack_last_buffer_reset(bool preserve_data ATTR_UNUSED)
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen last_alloc_end = data_stack_after_last_alloc(current_block);
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen p = last_alloc_end + MEM_ALIGN(sizeof(size_t)) + last_buffer_size;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen pend = last_alloc_end + ALLOC_SIZE(last_buffer_size);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen /* reset t_buffer_get() mark - not really needed but makes it
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen easier to notice if t_malloc()/t_push()/t_pop() is called
2769eecf814bd243033dcbf5bdc38f4162d3202dTimo Sirainen between t_buffer_get() and t_buffer_alloc().
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen do this before we get to i_panic() to avoid recursive
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen /* frame block full */
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen /* kludgy, but allow this before initialization */
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen /* allocate new block */
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen frame_block = calloc(sizeof(*frame_block), 1);
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen frame_block = GC_malloc(sizeof(*frame_block));
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen "t_push(): Out of memory");
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen /* use existing unused frame_block */
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen unused_frame_blocks = unused_frame_blocks->prev;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen /* mark our current position */
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen current_frame_block->block[frame_pos] = current_block;
f1e9611e93dcb3b745c1904029084fa81644e1b3Timo Sirainen current_frame_block->block_space_used[frame_pos] = current_block->left;
bf9ea5404a0094a8fb8199b677d81f803512c44eTimo Sirainen current_frame_block->last_alloc_size[frame_pos] = 0;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen current_frame_block->marker[frame_pos] = marker;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen current_frame_block->alloc_bytes[frame_pos] = 0ULL;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen current_frame_block->alloc_count[frame_pos] = 0;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainendata_stack_frame_t t_push_named(const char *format, ...)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen current_frame_block->marker[frame_pos] = p_strdup_vprintf(unsafe_data_stack_pool, format, args);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen (void)format; /* unused in non-DEBUG builds */
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenstatic void free_blocks(struct stack_block *block)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen /* free all the blocks, except if any of them is bigger than
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen unused_block, replace it */
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen memset(STACK_BLOCK_DATA(block), CLEAR_CHR, block->size);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen if (unused_block == NULL || block->size > unused_block->size) {
f8da06de93e28b5d3e039a427cdde7e1e15daec8Timo Sirainenstatic void t_pop_verify(void)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen unsigned char *p;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen block = current_frame_block->block[frame_pos];
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen pos = block->size - current_frame_block->block_space_used[frame_pos];
pos = 0;
#ifdef DEBUG
t_pop_verify();
if (clean_after_pop) {
if (frame_pos > 0)
frame_pos--;
return --data_stack_frame;
*id = 0;
#ifndef USE_GC
if (outofmem) {
abort();
#ifdef DEBUG
return block;
void *ret;
#ifdef DEBUG
#ifdef DEBUG
if(permanent) {
#ifdef DEBUG
if (permanent)
#ifdef DEBUG
return ret;
void *mem;
return mem;
unsigned char *after_last_alloc;
#ifdef DEBUG
#ifdef DEBUG
#ifdef DEBUG
return TRUE;
return FALSE;
#ifndef DEBUG
void *ret;
return ret;
void *new_buffer;
return buffer;
return new_buffer;
void t_buffer_alloc_last_full(void)
#ifndef DEBUG
void data_stack_init(void)
if (data_stack_initialized) {
last_buffer_size = 0;
void data_stack_deinit(void)
(void)t_pop();
#ifndef USE_GC