/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */
/* @UNSAFE: whole file */
#include "lib.h"
#include "data-stack.h"
/* Initial stack size - this should be kept in a size that doesn't exceed
in a normal use to avoid extra malloc()ing. */
#ifdef DEBUG
#else
#endif
#ifdef DEBUG
#else
# define CLEAR_CHR 0
#endif
struct stack_block {
/* NULL or a poison value, just in case something accesses
the memory in front of an allocated area */
void *canary;
/* unsigned char data[]; */
};
((unsigned char *) (block) + SIZEOF_MEMBLOCK)
/* current_frame_block contains last t_push()ed frames. After that new
stack_frame_block is created and it's ->prev is set to
current_frame_block. */
struct stack_frame_block {
#ifdef DEBUG
/* Fairly arbitrary profiling data */
#endif
};
#ifdef STATIC_CHECKER
struct data_stack_frame {
unsigned int id;
};
#endif
unsigned int data_stack_frame_id = 0;
#ifdef DEBUG
#else
#endif
static union {
static inline
{
}
{
if (last_buffer_block != NULL) {
#ifdef DEBUG
#endif
/* reset t_buffer_get() mark - not really needed but makes it
easier to notice if t_malloc()/t_push()/t_pop() is called
between t_buffer_get() and t_buffer_alloc().
do this before we get to i_panic() to avoid recursive
panics. */
#ifdef DEBUG
while (p < pend)
if (*p++ != CLEAR_CHR)
i_panic("t_buffer_get(): buffer overflow");
if (!preserve_data) {
p = last_alloc_end;
}
#endif
}
}
{
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;
}
frame_pos = 0;
if (unused_frame_blocks == NULL) {
/* allocate new block */
if (frame_block == NULL) {
"t_push(): Out of memory");
}
} else {
/* use existing unused frame_block */
}
}
/* mark our current position */
#ifdef DEBUG
#endif
#ifndef STATIC_CHECKER
return data_stack_frame_id++;
#else
return frame;
#endif
}
{
#ifdef DEBUG
#else
(void)format; /* unused in non-DEBUG builds */
#endif
return ret;
}
#ifdef DEBUG
{
/* Make sure i_panic() won't try to allocate from the
same block by falling back onto our emergency block. */
i_panic("Corrupted data stack canary");
}
}
#endif
{
/* free all the blocks, except if any of them is bigger than
unused_block, replace it */
if (clean_after_pop)
} else {
}
}
}
#ifdef DEBUG
static void t_pop_verify(void)
{
unsigned char *p;
p = STACK_BLOCK_DATA(block);
i_panic("data stack[%s]: saved alloc size broken",
i_panic("data stack[%s]: buffer overflow",
}
}
/* 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. */
pos = 0;
}
}
#endif
void t_pop_last_unsafe(void)
{
i_panic("t_pop() called with empty stack");
#ifdef DEBUG
t_pop_verify();
#endif
/* update the current block */
if (clean_after_pop) {
}
/* free unused blocks */
}
if (frame_pos > 0)
frame_pos--;
else {
/* frame block is now unused, add it to unused list */
}
}
{
#ifndef STATIC_CHECKER
return FALSE;
*id = 0;
#else
return FALSE;
#endif
return TRUE;
}
{
/* nearest_power() returns 2^n values, so alloc_size can't be
anywhere close to SIZE_MAX */
if (outofmem) {
abort();
return &outofmem_area.block;
}
i_panic("data stack: Out of memory when allocating %"
}
#ifdef DEBUG
#endif
return block;
}
{
void *ret;
#ifdef DEBUG
#endif
if (unlikely(!data_stack_initialized)) {
/* kludgy, but allow this before initialization */
}
/* allocate only aligned amount of memory so alignment comes
always properly */
#ifdef DEBUG
if(permanent) {
}
#endif
/* used for t_try_realloc() */
/* current block is full, see if we can use the unused_block */
unused_block = NULL;
} else {
#ifdef DEBUG
#endif
}
}
/* enough space in current block, use it */
if (permanent)
#ifdef DEBUG
/* 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 %zu as "
"'%s' reaches %llu bytes from %u allocations.",
}
/* make sure the sentry contains CLEAR_CHRs. it might not if we
had used t_buffer_get(). */
/* we rely on errno not changing. it shouldn't. */
#endif
return ret;
}
{
}
{
void *mem;
return mem;
}
{
unsigned char *after_last_alloc;
/* see if we're trying to grow the memory we allocated last */
#ifdef DEBUG
#endif
/* yeah, see if we have space to grow */
#ifdef DEBUG
/* Only check one byte for over-run, that catches most
offenders who are likely to use t_try_realloc() */
#endif
/* just shrink the available size */
#ifdef DEBUG
/* All reallocs are permanent by definition
However, they don't count as a new allocation */
#endif
return TRUE;
}
}
return FALSE;
}
{
#ifndef DEBUG
#else
#endif
}
{
void *ret;
return ret;
}
{
void *new_buffer;
return buffer;
if (new_buffer != buffer)
return new_buffer;
}
{
/* we've already reserved the space, now we just mark it used */
}
void t_buffer_alloc_last_full(void)
{
if (last_buffer_block != NULL)
}
{
#ifndef DEBUG
#endif
}
void data_stack_init(void)
{
if (data_stack_initialized) {
/* already initialized (we did auto-initialization in
return;
}
data_stack_frame_id = 1;
last_buffer_size = 0;
}
void data_stack_deinit(void)
{
if (!t_pop(&root_frame_id) ||
i_panic("Missing t_pop() call");
while (unused_frame_blocks != NULL) {
}
unused_block = NULL;
}