data-stack.c revision 32c3ba3ba51c80d3fa2b4e17c34a621a8532cca0
2454dfa32c93c20a8522c6ed42fe057baaac9f9aStephan Bosch/* Copyright (c) 2002-2016 Dovecot authors, see the included COPYING file */
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen/* @UNSAFE: whole file */
0536ccb51d41e3078c3a9fa33e509fb4b2420f95Timo Sirainen
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen#include "lib.h"
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen#include "data-stack.h"
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen
9511a40d933181045343110c8101b75887062aaeTimo Sirainen#ifdef HAVE_GC_GC_H
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen# include <gc/gc.h>
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen#elif defined (HAVE_GC_H)
4aa7fe81503a20bc972ae625da4dd9e6996fbdbfTimo Sirainen# include <gc.h>
4aa7fe81503a20bc972ae625da4dd9e6996fbdbfTimo Sirainen#endif
4aa7fe81503a20bc972ae625da4dd9e6996fbdbfTimo Sirainen
4aa7fe81503a20bc972ae625da4dd9e6996fbdbfTimo Sirainen/* Initial stack size - this should be kept in a size that doesn't exceed
4aa7fe81503a20bc972ae625da4dd9e6996fbdbfTimo Sirainen in a normal use to avoid extra malloc()ing. */
4aa7fe81503a20bc972ae625da4dd9e6996fbdbfTimo Sirainen#ifdef DEBUG
4aa7fe81503a20bc972ae625da4dd9e6996fbdbfTimo Sirainen# define INITIAL_STACK_SIZE (1024*10)
4aa7fe81503a20bc972ae625da4dd9e6996fbdbfTimo Sirainen#else
4aa7fe81503a20bc972ae625da4dd9e6996fbdbfTimo Sirainen# define INITIAL_STACK_SIZE (1024*32)
4aa7fe81503a20bc972ae625da4dd9e6996fbdbfTimo Sirainen#endif
4aa7fe81503a20bc972ae625da4dd9e6996fbdbfTimo Sirainen
4aa7fe81503a20bc972ae625da4dd9e6996fbdbfTimo Sirainen#ifdef DEBUG
4aa7fe81503a20bc972ae625da4dd9e6996fbdbfTimo Sirainen# define CLEAR_CHR 0xD5 /* D5 is mnemonic for "Data 5tack" */
4aa7fe81503a20bc972ae625da4dd9e6996fbdbfTimo Sirainen# define SENTRY_COUNT (4*8)
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen# define BLOCK_CANARY ((void *)0xBADBADD5BADBADD5) /* contains 'D5' */
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen# define BLOCK_CANARY_CHECK(block) i_assert((block)->canary == BLOCK_CANARY)
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen# define ALLOC_SIZE(size) (MEM_ALIGN(sizeof(size_t)) + MEM_ALIGN(size + SENTRY_COUNT))
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen#else
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen# define CLEAR_CHR 0
8887a9bb6d2e3f664cf741b763643a0e5610fa4dTimo Sirainen# define BLOCK_CANARY NULL
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen# define BLOCK_CANARY_CHECK(block) do { ; } while(0)
3f8303bae0f70df6db9337ad1d1476d290f9b1a3Timo Sirainen# define ALLOC_SIZE(size) MEM_ALIGN(size)
3f8303bae0f70df6db9337ad1d1476d290f9b1a3Timo Sirainen#endif
3f8303bae0f70df6db9337ad1d1476d290f9b1a3Timo Sirainen
8887a9bb6d2e3f664cf741b763643a0e5610fa4dTimo Sirainenstruct stack_block {
8887a9bb6d2e3f664cf741b763643a0e5610fa4dTimo Sirainen struct stack_block *next;
859cc94211b759825db5e15b0c88754da902ca14Timo Sirainen
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen size_t size, left, lowwater;
859cc94211b759825db5e15b0c88754da902ca14Timo Sirainen /* NULL or a poison value, just in case something accesses
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen the memory in front of an allocated area */
8887a9bb6d2e3f664cf741b763643a0e5610fa4dTimo Sirainen void *canary;
8887a9bb6d2e3f664cf741b763643a0e5610fa4dTimo Sirainen /* unsigned char data[]; */
8887a9bb6d2e3f664cf741b763643a0e5610fa4dTimo Sirainen};
8887a9bb6d2e3f664cf741b763643a0e5610fa4dTimo Sirainen
8887a9bb6d2e3f664cf741b763643a0e5610fa4dTimo Sirainen#define SIZEOF_MEMBLOCK MEM_ALIGN(sizeof(struct stack_block))
8887a9bb6d2e3f664cf741b763643a0e5610fa4dTimo Sirainen
8887a9bb6d2e3f664cf741b763643a0e5610fa4dTimo Sirainen#define STACK_BLOCK_DATA(block) \
8887a9bb6d2e3f664cf741b763643a0e5610fa4dTimo Sirainen ((unsigned char *) (block) + SIZEOF_MEMBLOCK)
8887a9bb6d2e3f664cf741b763643a0e5610fa4dTimo Sirainen
8887a9bb6d2e3f664cf741b763643a0e5610fa4dTimo Sirainen/* current_frame_block contains last t_push()ed frames. After that new
8887a9bb6d2e3f664cf741b763643a0e5610fa4dTimo Sirainen stack_frame_block is created and it's ->prev is set to
8887a9bb6d2e3f664cf741b763643a0e5610fa4dTimo Sirainen current_frame_block. */
8887a9bb6d2e3f664cf741b763643a0e5610fa4dTimo Sirainen#define BLOCK_FRAME_COUNT 32
8887a9bb6d2e3f664cf741b763643a0e5610fa4dTimo Sirainen
8887a9bb6d2e3f664cf741b763643a0e5610fa4dTimo Sirainenstruct stack_frame_block {
8887a9bb6d2e3f664cf741b763643a0e5610fa4dTimo Sirainen struct stack_frame_block *prev;
8887a9bb6d2e3f664cf741b763643a0e5610fa4dTimo Sirainen
8887a9bb6d2e3f664cf741b763643a0e5610fa4dTimo Sirainen struct stack_block *block[BLOCK_FRAME_COUNT];
8887a9bb6d2e3f664cf741b763643a0e5610fa4dTimo Sirainen size_t block_space_used[BLOCK_FRAME_COUNT];
8887a9bb6d2e3f664cf741b763643a0e5610fa4dTimo Sirainen size_t last_alloc_size[BLOCK_FRAME_COUNT];
8887a9bb6d2e3f664cf741b763643a0e5610fa4dTimo Sirainen#ifdef DEBUG
1117aa7adc2909c750031fd7551a58a486d100d8Timo Sirainen const char *marker[BLOCK_FRAME_COUNT];
8887a9bb6d2e3f664cf741b763643a0e5610fa4dTimo Sirainen /* Fairly arbitrary profiling data */
8887a9bb6d2e3f664cf741b763643a0e5610fa4dTimo Sirainen unsigned long long alloc_bytes[BLOCK_FRAME_COUNT];
8887a9bb6d2e3f664cf741b763643a0e5610fa4dTimo Sirainen unsigned int alloc_count[BLOCK_FRAME_COUNT];
8887a9bb6d2e3f664cf741b763643a0e5610fa4dTimo Sirainen#endif
8887a9bb6d2e3f664cf741b763643a0e5610fa4dTimo Sirainen};
8887a9bb6d2e3f664cf741b763643a0e5610fa4dTimo Sirainen
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen#ifdef DEBUG
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainenstruct data_stack_frame {
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen int dummy;
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen};
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen#endif
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainendata_stack_frame_t data_stack_frame = 0;
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainenstatic bool data_stack_initialized = FALSE;
2c0f1cb7a0564d48ec43c7315ea46ea38d2abd19Timo Sirainenstatic data_stack_frame_t root_frame_id;
2c0f1cb7a0564d48ec43c7315ea46ea38d2abd19Timo Sirainen
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainenstatic int frame_pos = BLOCK_FRAME_COUNT-1; /* in current_frame_block */
2c0f1cb7a0564d48ec43c7315ea46ea38d2abd19Timo Sirainenstatic struct stack_frame_block *current_frame_block;
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainenstatic struct stack_frame_block *unused_frame_blocks;
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainenstatic struct stack_block *current_block; /* block now used for allocation */
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainenstatic struct stack_block *unused_block; /* largest unused block is kept here */
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainenstatic struct stack_block *last_buffer_block;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainenstatic size_t last_buffer_size;
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen#ifdef DEBUG
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainenstatic bool clean_after_pop = TRUE;
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen#else
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainenstatic bool clean_after_pop = FALSE;
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen#endif
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainenstatic bool outofmem = FALSE;
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainenstatic union {
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen struct stack_block block;
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen unsigned char data[512];
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen} outofmem_area;
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainenstatic inline
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainenunsigned char *data_stack_after_last_alloc(struct stack_block *block)
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen{
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen return STACK_BLOCK_DATA(block) + (block->size - block->left);
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen}
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainenstatic void data_stack_last_buffer_reset(bool preserve_data ATTR_UNUSED)
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen{
10c96a244935de4add8213ba0b894178dfb889a5Timo Sirainen if (last_buffer_block != NULL) {
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen#ifdef DEBUG
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen unsigned char *last_alloc_end, *p, *pend;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen
fe363b433b8038a69b55169da9dca27892ad7d18Timo Sirainen last_alloc_end = data_stack_after_last_alloc(current_block);
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen p = last_alloc_end + MEM_ALIGN(sizeof(size_t)) + last_buffer_size;
597dba3488c648ffb375ee4a552bd52ac4346979Timo Sirainen pend = last_alloc_end + ALLOC_SIZE(last_buffer_size);
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen#endif
4aa7fe81503a20bc972ae625da4dd9e6996fbdbfTimo Sirainen /* reset t_buffer_get() mark - not really needed but makes it
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen easier to notice if t_malloc()/t_push()/t_pop() is called
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen between t_buffer_get() and t_buffer_alloc().
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen do this before we get to i_panic() to avoid recursive
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen panics. */
da2aa032ccfa8e7e4a4380ef738014549f4d2c2dTimo Sirainen last_buffer_block = NULL;
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen
411d6baa37f31d90730e90c4a28c43e1974bbe58Timo Sirainen#ifdef DEBUG
9511a40d933181045343110c8101b75887062aaeTimo Sirainen while (p < pend)
2974dca6be5120e49279f06c8aa952e5fac56048Timo Sirainen if (*p++ != CLEAR_CHR)
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen i_panic("t_buffer_get(): buffer overflow");
bdcb00145ad87765e3fd22d4ebc4d2c029a326b9Timo Sirainen
bdcb00145ad87765e3fd22d4ebc4d2c029a326b9Timo Sirainen if (!preserve_data) {
bdcb00145ad87765e3fd22d4ebc4d2c029a326b9Timo Sirainen p = last_alloc_end;
bdcb00145ad87765e3fd22d4ebc4d2c029a326b9Timo Sirainen memset(p, CLEAR_CHR, SENTRY_COUNT);
bdcb00145ad87765e3fd22d4ebc4d2c029a326b9Timo Sirainen }
bdcb00145ad87765e3fd22d4ebc4d2c029a326b9Timo Sirainen#endif
bdcb00145ad87765e3fd22d4ebc4d2c029a326b9Timo Sirainen }
bdcb00145ad87765e3fd22d4ebc4d2c029a326b9Timo Sirainen}
bdcb00145ad87765e3fd22d4ebc4d2c029a326b9Timo Sirainen
bdcb00145ad87765e3fd22d4ebc4d2c029a326b9Timo Sirainendata_stack_frame_t t_push(const char *marker)
bdcb00145ad87765e3fd22d4ebc4d2c029a326b9Timo Sirainen{
bdcb00145ad87765e3fd22d4ebc4d2c029a326b9Timo Sirainen struct stack_frame_block *frame_block;
frame_pos++;
if (frame_pos == BLOCK_FRAME_COUNT) {
/* frame block full */
if (unlikely(!data_stack_initialized)) {
/* kludgy, but allow this before initialization */
frame_pos = 0;
data_stack_init();
return t_push(marker);
}
frame_pos = 0;
if (unused_frame_blocks == NULL) {
/* allocate new block */
#ifndef USE_GC
frame_block = calloc(sizeof(*frame_block), 1);
#else
frame_block = GC_malloc(sizeof(*frame_block));
#endif
if (frame_block == NULL) {
i_fatal_status(FATAL_OUTOFMEM,
"t_push(): Out of memory");
}
} else {
/* use existing unused frame_block */
frame_block = unused_frame_blocks;
unused_frame_blocks = unused_frame_blocks->prev;
}
frame_block->prev = current_frame_block;
current_frame_block = frame_block;
}
data_stack_last_buffer_reset(FALSE);
/* mark our current position */
current_frame_block->block[frame_pos] = current_block;
current_frame_block->block_space_used[frame_pos] = current_block->left;
current_frame_block->last_alloc_size[frame_pos] = 0;
#ifdef DEBUG
current_frame_block->marker[frame_pos] = marker;
current_frame_block->alloc_bytes[frame_pos] = 0ULL;
current_frame_block->alloc_count[frame_pos] = 0;
#else
(void)marker; /* only used for debugging */
#endif
return data_stack_frame++;
}
data_stack_frame_t t_push_named(const char *format, ...)
{
data_stack_frame_t ret = t_push(NULL);
#ifdef DEBUG
va_list args;
va_start(args, format);
current_frame_block->marker[frame_pos] = p_strdup_vprintf(unsafe_data_stack_pool, format, args);
va_end(args);
#else
(void)format; /* unused in non-DEBUG builds */
#endif
return ret;
}
static void free_blocks(struct stack_block *block)
{
struct stack_block *next;
/* free all the blocks, except if any of them is bigger than
unused_block, replace it */
while (block != NULL) {
BLOCK_CANARY_CHECK(block);
next = block->next;
if (clean_after_pop)
memset(STACK_BLOCK_DATA(block), CLEAR_CHR, block->size);
if (unused_block == NULL || block->size > unused_block->size) {
#ifndef USE_GC
free(unused_block);
#endif
unused_block = block;
} else {
#ifndef USE_GC
if (block != &outofmem_area.block)
free(block);
#endif
}
block = next;
}
}
#ifdef DEBUG
static void t_pop_verify(void)
{
struct stack_block *block;
unsigned char *p;
size_t pos, max_pos, used_size;
block = current_frame_block->block[frame_pos];
pos = block->size - current_frame_block->block_space_used[frame_pos];
while (block != NULL) {
BLOCK_CANARY_CHECK(block);
used_size = block->size - block->left;
p = STACK_BLOCK_DATA(block);
while (pos < used_size) {
size_t requested_size = *(size_t *)(p + pos);
if (used_size - pos < requested_size)
i_panic("data stack[%s]: saved alloc size broken",
current_frame_block->marker[frame_pos]);
max_pos = pos + ALLOC_SIZE(requested_size);
pos += MEM_ALIGN(sizeof(size_t)) + requested_size;
for (; pos < max_pos; pos++) {
if (p[pos] != CLEAR_CHR)
i_panic("data stack[%s]: buffer overflow",
current_frame_block->marker[frame_pos]);
}
}
/* if we had used t_buffer_get(), the rest of the buffer
may not contain CLEAR_CHRs. but we've already checked all
the allocations, so there's no need to check them anyway. */
block = block->next;
pos = 0;
}
}
#endif
bool t_pop(data_stack_frame_t *id)
{
struct stack_frame_block *frame_block;
if (unlikely(frame_pos < 0))
i_panic("t_pop() called with empty stack");
data_stack_last_buffer_reset(FALSE);
#ifdef DEBUG
t_pop_verify();
#endif
/* update the current block */
current_block = current_frame_block->block[frame_pos];
BLOCK_CANARY_CHECK(current_block);
if (clean_after_pop) {
size_t pos, used_size;
pos = current_block->size -
current_frame_block->block_space_used[frame_pos];
used_size = current_block->size - current_block->lowwater;
i_assert(used_size >= pos);
memset(STACK_BLOCK_DATA(current_block) + pos, CLEAR_CHR,
used_size - pos);
}
current_block->left = current_frame_block->block_space_used[frame_pos];
current_block->lowwater = current_block->left;
if (current_block->next != NULL) {
/* free unused blocks */
free_blocks(current_block->next);
current_block->next = NULL;
}
if (frame_pos > 0)
frame_pos--;
else {
/* frame block is now unused, add it to unused list */
frame_pos = BLOCK_FRAME_COUNT-1;
frame_block = current_frame_block;
current_frame_block = frame_block->prev;
frame_block->prev = unused_frame_blocks;
unused_frame_blocks = frame_block;
}
if (unlikely(--data_stack_frame != *id))
return FALSE;
*id = 0;
return TRUE;
}
static struct stack_block *mem_block_alloc(size_t min_size)
{
struct stack_block *block;
size_t prev_size, alloc_size;
prev_size = current_block == NULL ? 0 : current_block->size;
alloc_size = nearest_power(prev_size + min_size);
#ifndef USE_GC
block = malloc(SIZEOF_MEMBLOCK + alloc_size);
#else
block = GC_malloc(SIZEOF_MEMBLOCK + alloc_size);
#endif
if (unlikely(block == NULL)) {
if (outofmem) {
if (min_size > outofmem_area.block.left)
abort();
return &outofmem_area.block;
}
outofmem = TRUE;
i_panic("data stack: Out of memory when allocating %"
PRIuSIZE_T" bytes", alloc_size + SIZEOF_MEMBLOCK);
}
block->size = alloc_size;
block->left = 0;
block->lowwater = block->size;
block->next = NULL;
block->canary = BLOCK_CANARY;
#ifdef DEBUG
memset(STACK_BLOCK_DATA(block), CLEAR_CHR, alloc_size);
#endif
return block;
}
static void *t_malloc_real(size_t size, bool permanent)
{
void *ret;
size_t alloc_size;
#ifdef DEBUG
bool warn = FALSE;
int old_errno = errno;
#endif
if (unlikely(size == 0 || size > SSIZE_T_MAX))
i_panic("Trying to allocate %"PRIuSIZE_T" bytes", size);
if (unlikely(!data_stack_initialized)) {
/* kludgy, but allow this before initialization */
data_stack_init();
}
BLOCK_CANARY_CHECK(current_block);
/* allocate only aligned amount of memory so alignment comes
always properly */
alloc_size = ALLOC_SIZE(size);
#ifdef DEBUG
if(permanent) {
current_frame_block->alloc_bytes[frame_pos] += alloc_size;
current_frame_block->alloc_count[frame_pos]++;
}
#endif
data_stack_last_buffer_reset(TRUE);
/* used for t_try_realloc() */
current_frame_block->last_alloc_size[frame_pos] = alloc_size;
if (current_block->left < alloc_size) {
struct stack_block *block;
/* current block is full, see if we can use the unused_block */
if (unused_block != NULL && unused_block->size >= alloc_size) {
block = unused_block;
unused_block = NULL;
} else {
block = mem_block_alloc(alloc_size);
#ifdef DEBUG
warn = TRUE;
#endif
}
block->left = block->size;
block->next = NULL;
current_block->next = block;
current_block = block;
}
/* enough space in current block, use it */
ret = data_stack_after_last_alloc(current_block);
if (current_block->left - alloc_size < current_block->lowwater)
current_block->lowwater = current_block->left - alloc_size;
if (permanent)
current_block->left -= alloc_size;
#ifdef DEBUG
if (warn && getenv("DEBUG_SILENT") == NULL) {
/* warn after allocation, so if i_debug() wants to
allocate more memory we don't go to infinite loop */
i_debug("Growing data stack by %"PRIuSIZE_T" as "
"'%s' reaches %llu bytes from %u allocations.",
current_block->size,
current_frame_block->marker[frame_pos],
current_frame_block->alloc_bytes[frame_pos],
current_frame_block->alloc_count[frame_pos]);
}
memcpy(ret, &size, sizeof(size));
ret = PTR_OFFSET(ret, MEM_ALIGN(sizeof(size)));
/* make sure the sentry contains CLEAR_CHRs. it might not if we
had used t_buffer_get(). */
memset(PTR_OFFSET(ret, size), CLEAR_CHR,
MEM_ALIGN(size + SENTRY_COUNT) - size);
/* we rely on errno not changing. it shouldn't. */
i_assert(errno == old_errno);
#endif
return ret;
}
void *t_malloc_no0(size_t size)
{
return t_malloc_real(size, TRUE);
}
void *t_malloc0(size_t size)
{
void *mem;
mem = t_malloc_real(size, TRUE);
memset(mem, 0, size);
return mem;
}
bool t_try_realloc(void *mem, size_t size)
{
size_t debug_adjust = 0, last_alloc_size;
unsigned char *after_last_alloc;
if (unlikely(size == 0 || size > SSIZE_T_MAX))
i_panic("Trying to allocate %"PRIuSIZE_T" bytes", size);
BLOCK_CANARY_CHECK(current_block);
last_alloc_size = current_frame_block->last_alloc_size[frame_pos];
/* see if we're trying to grow the memory we allocated last */
after_last_alloc = data_stack_after_last_alloc(current_block);
#ifdef DEBUG
debug_adjust = MEM_ALIGN(sizeof(size_t));
#endif
if (after_last_alloc - last_alloc_size + debug_adjust == mem) {
/* yeah, see if we have space to grow */
size_t new_alloc_size, alloc_growth;
new_alloc_size = ALLOC_SIZE(size);
alloc_growth = (new_alloc_size - last_alloc_size);
#ifdef DEBUG
size_t old_raw_size; /* sorry, non-C99 users - add braces if you need them */
old_raw_size = *(size_t *)PTR_OFFSET(mem, -(ptrdiff_t)MEM_ALIGN(sizeof(size_t)));
i_assert(ALLOC_SIZE(old_raw_size) == last_alloc_size);
/* Only check one byte for over-run, that catches most
offenders who are likely to use t_try_realloc() */
i_assert(((unsigned char*)mem)[old_raw_size] == CLEAR_CHR);
#endif
if (current_block->left >= alloc_growth) {
/* just shrink the available size */
current_block->left -= alloc_growth;
if (current_block->left < current_block->lowwater)
current_block->lowwater = current_block->left;
current_frame_block->last_alloc_size[frame_pos] =
new_alloc_size;
#ifdef DEBUG
/* All reallocs are permanent by definition
However, they don't count as a new allocation */
current_frame_block->alloc_bytes[frame_pos] += alloc_growth;
*(size_t *)PTR_OFFSET(mem, -(ptrdiff_t)MEM_ALIGN(sizeof(size_t))) = size;
memset(PTR_OFFSET(mem, size), CLEAR_CHR,
new_alloc_size - size - MEM_ALIGN(sizeof(size_t)));
#endif
return TRUE;
}
}
return FALSE;
}
size_t t_get_bytes_available(void)
{
#ifndef DEBUG
const unsigned int extra = MEM_ALIGN_SIZE-1;
#else
const unsigned int extra = MEM_ALIGN_SIZE-1 + SENTRY_COUNT +
MEM_ALIGN(sizeof(size_t));
#endif
BLOCK_CANARY_CHECK(current_block);
return current_block->left < extra ? current_block->left :
current_block->left - extra;
}
void *t_buffer_get(size_t size)
{
void *ret;
ret = t_malloc_real(size, FALSE);
last_buffer_size = size;
last_buffer_block = current_block;
return ret;
}
void *t_buffer_reget(void *buffer, size_t size)
{
size_t old_size;
void *new_buffer;
old_size = last_buffer_size;
if (size <= old_size)
return buffer;
new_buffer = t_buffer_get(size);
if (new_buffer != buffer)
memcpy(new_buffer, buffer, old_size);
return new_buffer;
}
void t_buffer_alloc(size_t size)
{
i_assert(last_buffer_block != NULL);
i_assert(last_buffer_size >= size);
i_assert(current_block->left >= size);
/* we've already reserved the space, now we just mark it used */
(void)t_malloc_real(size, TRUE);
}
void t_buffer_alloc_last_full(void)
{
if (last_buffer_block != NULL)
(void)t_malloc_real(last_buffer_size, TRUE);
}
void data_stack_set_clean_after_pop(bool enable ATTR_UNUSED)
{
#ifndef DEBUG
clean_after_pop = enable;
#endif
}
void data_stack_init(void)
{
if (data_stack_initialized) {
/* already initialized (we did auto-initialization in
t_malloc/t_push) */
return;
}
data_stack_initialized = TRUE;
data_stack_frame = (data_stack_frame_t)1;
outofmem_area.block.size = outofmem_area.block.left =
sizeof(outofmem_area) - sizeof(outofmem_area.block);
current_block = mem_block_alloc(INITIAL_STACK_SIZE);
current_block->left = current_block->size;
current_block->next = NULL;
current_frame_block = NULL;
unused_frame_blocks = NULL;
frame_pos = BLOCK_FRAME_COUNT-1;
last_buffer_block = NULL;
last_buffer_size = 0;
root_frame_id = t_push("data_stack_init");
}
void data_stack_deinit(void)
{
if (!t_pop(&root_frame_id) ||
frame_pos != BLOCK_FRAME_COUNT-1)
i_panic("Missing t_pop() call");
#ifndef USE_GC
while (unused_frame_blocks != NULL) {
struct stack_frame_block *frame_block = unused_frame_blocks;
unused_frame_blocks = unused_frame_blocks->prev;
free(frame_block);
}
free(current_block);
free(unused_block);
#endif
unused_frame_blocks = NULL;
current_block = NULL;
unused_block = NULL;
}