data-stack.c revision 5517afec377fb7bf80e589b3efdd975f61dad5b3
76b43e4417bab52e913da39b5f5bc2a130d3f149Timo Sirainen/* Copyright (c) 2002-2014 Dovecot authors, see the included COPYING file */
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen/* @UNSAFE: whole file */
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen/* Initial stack size - this should be kept in a size that doesn't exceed
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen in a normal use to avoid extra malloc()ing. */
af1f4b17a92ca7b2661737e65c7849df289d3070Timo Sirainen /* always NULL and here just in case something accesses
af1f4b17a92ca7b2661737e65c7849df289d3070Timo Sirainen the memory in front of an allocated area */
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen /* unsigned char data[]; */
01cbf4ac5d44137ab434791be7f838d98d0fcf3bTimo Sirainen#define SIZEOF_MEMBLOCK MEM_ALIGN(sizeof(struct stack_block))
fe6c1556d3529a6376d4cbb3766c34aebde0de99Timo Sirainen/* current_frame_block contains last t_push()ed frames. After that new
fe6c1556d3529a6376d4cbb3766c34aebde0de99Timo Sirainen stack_frame_block is created and it's ->prev is set to
fe6c1556d3529a6376d4cbb3766c34aebde0de99Timo Sirainen current_frame_block. */
fe6c1556d3529a6376d4cbb3766c34aebde0de99Timo Sirainen /* Fairly arbitrary profiling data */
fe6c1556d3529a6376d4cbb3766c34aebde0de99Timo Sirainen unsigned long long alloc_bytes[BLOCK_FRAME_COUNT];
d30da25fb6be1f1c667d93767c9194000194b618Timo Sirainenunsigned int data_stack_frame = 0;
fe6c1556d3529a6376d4cbb3766c34aebde0de99Timo Sirainenstatic int frame_pos = BLOCK_FRAME_COUNT-1; /* in current_frame_block */
597dce34068d603fb759b4dff404b34049213e51Timo Sirainenstatic struct stack_frame_block *current_frame_block;
63a61b7a739ae0f3f520215137d9c50f94d0f34fTimo Sirainenstatic struct stack_frame_block *unused_frame_blocks;
fe6c1556d3529a6376d4cbb3766c34aebde0de99Timo Sirainenstatic struct stack_block *current_block; /* block now used for allocation */
fe6c1556d3529a6376d4cbb3766c34aebde0de99Timo Sirainenstatic struct stack_block *unused_block; /* largest unused block is kept here */
fe6c1556d3529a6376d4cbb3766c34aebde0de99Timo Sirainenstatic union {
fe6c1556d3529a6376d4cbb3766c34aebde0de99Timo Sirainenstatic void data_stack_last_buffer_reset(bool preserve_data ATTR_UNUSED)
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen unsigned char *p;
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen unsigned int i;
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen MEM_ALIGN(sizeof(size_t)) + MEM_ALIGN(last_buffer_size);
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen /* reset t_buffer_get() mark - not really needed but makes it
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen easier to notice if t_malloc()/t_push()/t_pop() is called
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen between t_buffer_get() and t_buffer_alloc().
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen do this before we get to i_panic() to avoid recursive
345212e8f61ebf14ff4f80df26df9e655eb5121eTimo Sirainen for (i = 0; i < SENTRY_COUNT; i++) {
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen /* frame block full */
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen /* kludgy, but allow this before initialization */
a0b0d629931773c17a236f6214adbe0e13b9b3fdTimo Sirainen /* allocate new block */
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen frame_block = calloc(sizeof(*frame_block), 1);
a0b0d629931773c17a236f6214adbe0e13b9b3fdTimo Sirainen frame_block = GC_malloc(sizeof(*frame_block));
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen "t_push(): Out of memory");
a0b0d629931773c17a236f6214adbe0e13b9b3fdTimo Sirainen /* use existing unused frame_block */
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen unused_frame_blocks = unused_frame_blocks->prev;
a0b0d629931773c17a236f6214adbe0e13b9b3fdTimo Sirainen /* mark our current position */
a0b0d629931773c17a236f6214adbe0e13b9b3fdTimo Sirainen current_frame_block->block[frame_pos] = current_block;
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen current_frame_block->block_space_used[frame_pos] = current_block->left;
a0b0d629931773c17a236f6214adbe0e13b9b3fdTimo Sirainen current_frame_block->last_alloc_size[frame_pos] = 0;
a0b0d629931773c17a236f6214adbe0e13b9b3fdTimo Sirainen current_frame_block->marker[frame_pos] = marker;
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen current_frame_block->alloc_bytes[frame_pos] = 0ULL;
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen current_frame_block->alloc_count[frame_pos] = 0;
64a67c0296b120b6e15169ac9d84dec964e55969Timo Sirainenunsigned int t_push_named(const char *format, ...)
64a67c0296b120b6e15169ac9d84dec964e55969Timo Sirainen current_frame_block->marker[frame_pos] = p_strdup_vprintf(unsafe_data_stack_pool, format, args);
64a67c0296b120b6e15169ac9d84dec964e55969Timo Sirainen (void)format; /* unused in non-DEBUG builds */
64a67c0296b120b6e15169ac9d84dec964e55969Timo Sirainenstatic void free_blocks(struct stack_block *block)
64a67c0296b120b6e15169ac9d84dec964e55969Timo Sirainen /* free all the blocks, except if any of them is bigger than
64a67c0296b120b6e15169ac9d84dec964e55969Timo Sirainen unused_block, replace it */
d67fde1a8ebc1d85704c5986d8f93aae97eccef3Timo Sirainen memset(STACK_BLOCK_DATA(block), CLEAR_CHR, block->size);
aa215ab623706463cea1d047f975ffe51d3f0c05Timo Sirainen if (unused_block == NULL || block->size > unused_block->size) {
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainenstatic void t_pop_verify(void)
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen unsigned char *p;
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen block = current_frame_block->block[frame_pos];
64a67c0296b120b6e15169ac9d84dec964e55969Timo Sirainen pos = block->size - current_frame_block->block_space_used[frame_pos];
aa215ab623706463cea1d047f975ffe51d3f0c05Timo Sirainen i_panic("data stack[%s]: saved alloc size broken",
a5c8dc283ef673fcdae158513b8032e74b45f59aTimo Sirainen max_pos = pos + MEM_ALIGN(alloc_size + SENTRY_COUNT);
64a67c0296b120b6e15169ac9d84dec964e55969Timo Sirainen /* if we had used t_buffer_get(), the rest of the buffer
64a67c0296b120b6e15169ac9d84dec964e55969Timo Sirainen may not contain CLEAR_CHRs. but we've already checked all
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen the allocations, so there's no need to check them anyway. */
64a67c0296b120b6e15169ac9d84dec964e55969Timo Sirainenunsigned int t_pop(void)
64a67c0296b120b6e15169ac9d84dec964e55969Timo Sirainen /* update the current block */
64a67c0296b120b6e15169ac9d84dec964e55969Timo Sirainen current_block = current_frame_block->block[frame_pos];
64a67c0296b120b6e15169ac9d84dec964e55969Timo Sirainen current_frame_block->block_space_used[frame_pos];
64a67c0296b120b6e15169ac9d84dec964e55969Timo Sirainen used_size = current_block->size - current_block->lowwater;
64a67c0296b120b6e15169ac9d84dec964e55969Timo Sirainen memset(STACK_BLOCK_DATA(current_block) + pos, CLEAR_CHR,
64a67c0296b120b6e15169ac9d84dec964e55969Timo Sirainen current_block->left = current_frame_block->block_space_used[frame_pos];
64a67c0296b120b6e15169ac9d84dec964e55969Timo Sirainen current_block->lowwater = current_block->left;
64a67c0296b120b6e15169ac9d84dec964e55969Timo Sirainen /* free unused blocks */
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen /* frame block is now unused, add it to unused list */
0d86aa0d47f7393c669c084b34c0537b193688adTimo Sirainenstatic struct stack_block *mem_block_alloc(size_t min_size)
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen prev_size = current_block == NULL ? 0 : current_block->size;
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen alloc_size = nearest_power(prev_size + min_size);
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen block = GC_malloc(SIZEOF_MEMBLOCK + alloc_size);
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen i_panic("data stack: Out of memory when allocating %"
838e367716bbd5e44b4a1691db9cbf72af53e9f0Timo Sirainen PRIuSIZE_T" bytes", alloc_size + SIZEOF_MEMBLOCK);
#ifdef DEBUG
return block;
void *ret;
#ifdef DEBUG
#ifndef DEBUG
if(permanent) {
if (permanent)
#ifdef DEBUG
if (permanent)
#ifdef DEBUG
#ifdef DEBUG
return ret;
void *mem;
return mem;
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_frame > 0) {
last_buffer_size = 0;
void data_stack_deinit(void)
(void)t_pop();
#ifndef USE_GC