data-stack.c revision c438c40313ec4ccd348e5c3d199c2927cdce8906
e59faf65ce864fe95dc00f5d52b8323cdbd0608aTimo Sirainen/* Copyright (c) 2002-2008 Dovecot authors, see the included COPYING file */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen/* @UNSAFE: whole file */
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen/* Initial stack size - this should be kept in a size that doesn't exceed
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen in a normal use to avoid extra malloc()ing. */
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen /* unsigned char data[]; */
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen#define SIZEOF_MEMBLOCK MEM_ALIGN(sizeof(struct stack_block))
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen/* current_frame_block contains last t_push()ed frames. After that new
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen stack_frame_block is created and it's ->prev is set to
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen current_frame_block. */
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainenunsigned int data_stack_frame = 0;
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainenstatic int frame_pos = BLOCK_FRAME_COUNT-1; /* in current_frame_block */
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainenstatic struct stack_frame_block *current_frame_block;
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainenstatic struct stack_frame_block *unused_frame_blocks;
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainenstatic struct stack_block *current_block; /* block now used for allocation */
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainenstatic struct stack_block *unused_block; /* largest unused block is kept here */
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen const unsigned char *p;
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen unsigned int i;
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen MEM_ALIGN(sizeof(size_t)) + MEM_ALIGN(last_buffer_size);
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen /* reset t_buffer_get() mark - not really needed but makes it
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen easier to notice if t_malloc()/t_push()/t_pop() is called
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen between t_buffer_get() and t_buffer_alloc().
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen do this before we get to i_panic() to avoid recursive
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen for (i = 0; i < SENTRY_COUNT; i++) {
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainenunsigned int t_push(void)
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen /* frame block full */
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen /* kludgy, but allow this before initialization */
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen /* allocate new block */
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen frame_block = calloc(sizeof(*frame_block), 1);
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen frame_block = GC_malloc(sizeof(*frame_block));
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen "t_push(): Out of memory");
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen /* use existing unused frame_block */
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen unused_frame_blocks = unused_frame_blocks->prev;
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen /* mark our current position */
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen current_frame_block->block[frame_pos] = current_block;
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen current_frame_block->block_space_used[frame_pos] = current_block->left;
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen current_frame_block->last_alloc_size[frame_pos] = 0;
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainenstatic void free_blocks(struct stack_block *block)
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen /* free all the blocks, except if any of them is bigger than
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen unused_block, replace it */
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen memset(STACK_BLOCK_DATA(block), CLEAR_CHR, block->size);
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen if (unused_block == NULL || block->size > unused_block->size) {
16f816d3f3c32ae3351834253f52ddd0212bcbf3Timo Sirainenstatic void t_pop_verify(void)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen unsigned char *p;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen block = current_frame_block->block[frame_pos];
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen pos = block->size - current_frame_block->block_space_used[frame_pos];
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_panic("data stack: saved alloc size broken");
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen max_pos = pos + MEM_ALIGN(alloc_size + SENTRY_COUNT);
b66a7b7ab0db2c9ad425912d3f21a36fcf76d876Timo Sirainen /* we could verify here that the rest of the buffer contains
b66a7b7ab0db2c9ad425912d3f21a36fcf76d876Timo Sirainen CLEAR_CHRs, but it would slow us down a bit too much. */
44dc970b18c4e2d06f34cb908924152156e4a45bTimo Sirainenunsigned int t_pop(void)
6bf1543bb7af03324c04e8f9ac8e430f395989aeTimo Sirainen /* update the current block */
6bf1543bb7af03324c04e8f9ac8e430f395989aeTimo Sirainen current_block = current_frame_block->block[frame_pos];
44dc970b18c4e2d06f34cb908924152156e4a45bTimo Sirainen current_frame_block->block_space_used[frame_pos];
b00787191c3c31bebb939c3d00f1fcdb67356c69Timo Sirainen used_size = current_block->size - current_block->lowwater;
44dc970b18c4e2d06f34cb908924152156e4a45bTimo Sirainen memset(STACK_BLOCK_DATA(current_block) + pos, CLEAR_CHR,
44dc970b18c4e2d06f34cb908924152156e4a45bTimo Sirainen current_block->left = current_frame_block->block_space_used[frame_pos];
d22301419109ed4a38351715e6760011421dadecTimo Sirainen current_block->lowwater = current_block->left;
ee116df08d0fdab703483e18fe8076b2ef9fd9d7Timo Sirainen /* free unused blocks */
e30b748edcef3cf3352478bf21fa8f785bdc773aTimo Sirainen /* frame block is now unused, add it to unused list */
ea9fd7f876643e985946a2563140359064819b8eTimo Sirainenstatic struct stack_block *mem_block_alloc(size_t min_size)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen prev_size = current_block == NULL ? 0 : current_block->size;
6925fd9cd70c30884406d50f1d85efb6561e776cTimo Sirainen alloc_size = nearest_power(prev_size + min_size);
6925fd9cd70c30884406d50f1d85efb6561e776cTimo Sirainen block = GC_malloc(SIZEOF_MEMBLOCK + alloc_size);
4b43f50117630aa12b3cfd0cbd05ae22ba27fec1Timo Sirainen i_panic("data stack: Out of memory when allocating %"
4b43f50117630aa12b3cfd0cbd05ae22ba27fec1Timo Sirainen PRIuSIZE_T" bytes", alloc_size + SIZEOF_MEMBLOCK);
992a9e2d6c6ee45d87089ac54267e0198a7802c3Timo Sirainen memset(STACK_BLOCK_DATA(block), CLEAR_CHR, alloc_size);
992a9e2d6c6ee45d87089ac54267e0198a7802c3Timo Sirainenstatic void *t_malloc_real(size_t size, bool permanent)
6925fd9cd70c30884406d50f1d85efb6561e776cTimo Sirainen if (unlikely(size == 0 || size > SSIZE_T_MAX))
6925fd9cd70c30884406d50f1d85efb6561e776cTimo Sirainen i_panic("Trying to allocate %"PRIuSIZE_T" bytes", size);
4b43f50117630aa12b3cfd0cbd05ae22ba27fec1Timo Sirainen /* kludgy, but allow this before initialization */
6925fd9cd70c30884406d50f1d85efb6561e776cTimo Sirainen /* allocate only aligned amount of memory so alignment comes
6925fd9cd70c30884406d50f1d85efb6561e776cTimo Sirainen always properly */
6925fd9cd70c30884406d50f1d85efb6561e776cTimo Sirainen alloc_size = MEM_ALIGN(sizeof(size)) + MEM_ALIGN(size + SENTRY_COUNT);
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen /* used for t_try_realloc() */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen current_frame_block->last_alloc_size[frame_pos] = alloc_size;
8e5fedd9ada47735be8ac0f8af2a66e8528bd776Timo Sirainen /* enough space in current block, use it */
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen /* current block is full, see if we can use the unused_block */
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen if (unused_block != NULL && unused_block->size >= alloc_size) {
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen if (block->left - alloc_size < block->lowwater)
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen /* warn after allocation, so if i_warning() wants to
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen allocate more memory we don't go to infinite loop */
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen i_warning("Growing data stack with: %"PRIuSIZE_T,
4dc8837ab37c1a606add1067e21ed868754db4e3Timo Sirainen ret = PTR_OFFSET(ret, MEM_ALIGN(sizeof(size)));
4dc8837ab37c1a606add1067e21ed868754db4e3Timo Sirainen /* make sure the sentry contains CLEAR_CHRs. it might not if we
4dc8837ab37c1a606add1067e21ed868754db4e3Timo Sirainen had used t_buffer_get(). */
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen if (unlikely(size == 0 || size > SSIZE_T_MAX))
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen i_panic("Trying to allocate %"PRIuSIZE_T" bytes", size);
b3bb775c6b735a7f6021dea799601fbfdb656e58Timo Sirainen last_alloc_size = current_frame_block->last_alloc_size[frame_pos];
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen /* see if we're trying to grow the memory we allocated last */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* yeah, see if we have space to grow */
d77c309fccbc6a7594f8cb08fb01009fa613c568Timo Sirainen if (current_block->left >= size - last_alloc_size) {
d77c309fccbc6a7594f8cb08fb01009fa613c568Timo Sirainen /* just shrink the available size */
d22301419109ed4a38351715e6760011421dadecTimo Sirainen current_block->left -= size - last_alloc_size;
d22301419109ed4a38351715e6760011421dadecTimo Sirainen current_frame_block->last_alloc_size[frame_pos] = size;
641f0c0900ee6e7cf9667f4b40ed95cec7d0cdcaTimo Sirainenvoid *t_buffer_reget(void *buffer, size_t size)
641f0c0900ee6e7cf9667f4b40ed95cec7d0cdcaTimo Sirainen /* we've already reserved the space, now we just mark it used */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenvoid data_stack_set_clean_after_pop(bool enable ATTR_UNUSED)
38df0cacce475112991e60d796f8f2105c616f01Timo Sirainen outofmem_area.block.size = outofmem_area.block.left =
38df0cacce475112991e60d796f8f2105c616f01Timo Sirainen sizeof(outofmem_area) - sizeof(outofmem_area.block);
eae1d6e75713d3d658908ac39b719992e2f8a456Timo Sirainen current_block = mem_block_alloc(INITIAL_STACK_SIZE);
785d9cca224d33ca3938e9166784f6483e8a27d7Timo Sirainen struct stack_frame_block *frame_block = unused_frame_blocks;