Skip to content

Commit

Permalink
lib: mempool - Define a global max allocation size supported by all p…
Browse files Browse the repository at this point in the history
…ools

Instead of relying on each mempool implementation to define its own limits
and checks, this commit defines a generic one that's sufficient to make any
mempool present or future happy.
  • Loading branch information
Josef 'Jeff' Sipek authored and villesavolainen committed Feb 6, 2019
1 parent 0a3e10c commit f4cac7d
Show file tree
Hide file tree
Showing 10 changed files with 47 additions and 23 deletions.
8 changes: 6 additions & 2 deletions src/lib/mempool-allocfree.c
Expand Up @@ -146,6 +146,10 @@ static const struct pool static_allocfree_pool = {
pool_t pool_allocfree_create(const char *name ATTR_UNUSED)
{
struct allocfree_pool *pool;

if (SIZEOF_POOLBLOCK > (SSIZE_T_MAX - POOL_MAX_ALLOC_SIZE))
i_panic("POOL_MAX_ALLOC_SIZE is too large");

pool = calloc(1, SIZEOF_ALLOCFREE_POOL);
if (pool == NULL)
i_fatal_status(FATAL_OUTOFMEM, "calloc(1, %"PRIuSIZE_T"): Out of memory",
Expand Down Expand Up @@ -251,7 +255,7 @@ static void *pool_allocfree_malloc(pool_t pool, size_t size)
struct allocfree_pool *apool =
container_of(pool, struct allocfree_pool, pool);

if (unlikely(size == 0 || size > SSIZE_T_MAX - SIZEOF_POOLBLOCK))
if (unlikely(size == 0 || size > POOL_MAX_ALLOC_SIZE))
i_panic("Trying to allocate %"PRIuSIZE_T" bytes", size);

struct pool_block *block = calloc(1, SIZEOF_POOLBLOCK + size);
Expand Down Expand Up @@ -281,7 +285,7 @@ static void *pool_allocfree_realloc(pool_t pool, void *mem,
container_of(pool, struct allocfree_pool, pool);
unsigned char *new_mem;

if (unlikely(new_size == 0 || new_size > SSIZE_T_MAX - SIZEOF_POOLBLOCK))
if (unlikely(new_size == 0 || new_size > POOL_MAX_ALLOC_SIZE))
i_panic("Trying to allocate %"PRIuSIZE_T" bytes", new_size);

if (mem == NULL)
Expand Down
7 changes: 5 additions & 2 deletions src/lib/mempool-alloconly.c
Expand Up @@ -231,6 +231,9 @@ pool_t pool_alloconly_create(const char *name ATTR_UNUSED, size_t size)
size_t min_alloc = SIZEOF_POOLBLOCK +
MEM_ALIGN(sizeof(struct alloconly_pool) + SENTRY_COUNT);

if (POOL_ALLOCONLY_MAX_EXTRA > (SSIZE_T_MAX - POOL_MAX_ALLOC_SIZE))
i_panic("POOL_MAX_ALLOC_SIZE is too large");

#ifdef DEBUG
min_alloc += MEM_ALIGN(strlen(name) + 1 + SENTRY_COUNT) +
sizeof(size_t)*2;
Expand Down Expand Up @@ -381,7 +384,7 @@ static void *pool_alloconly_malloc(pool_t pool, size_t size)
void *mem;
size_t alloc_size;

if (unlikely(size == 0 || size > SSIZE_T_MAX - POOL_ALLOCONLY_MAX_EXTRA))
if (unlikely(size == 0 || size > POOL_MAX_ALLOC_SIZE))
i_panic("Trying to allocate %"PRIuSIZE_T" bytes", size);

#ifndef DEBUG
Expand Down Expand Up @@ -451,7 +454,7 @@ static void *pool_alloconly_realloc(pool_t pool, void *mem,
container_of(pool, struct alloconly_pool, pool);
unsigned char *new_mem;

if (unlikely(new_size == 0 || new_size > SSIZE_T_MAX - POOL_ALLOCONLY_MAX_EXTRA))
if (unlikely(new_size == 0 || new_size > POOL_MAX_ALLOC_SIZE))
i_panic("Trying to allocate %"PRIuSIZE_T" bytes", new_size);

if (mem == NULL)
Expand Down
4 changes: 2 additions & 2 deletions src/lib/mempool-datastack.c
Expand Up @@ -140,7 +140,7 @@ static void *pool_data_stack_malloc(pool_t pool ATTR_UNUSED, size_t size)
struct datastack_pool *dpool =
container_of(pool, struct datastack_pool, pool);

if (unlikely(size == 0 || size > SSIZE_T_MAX))
if (unlikely(size == 0 || size > POOL_MAX_ALLOC_SIZE))
i_panic("Trying to allocate %"PRIuSIZE_T" bytes", size);

if (unlikely(dpool->data_stack_frame != data_stack_frame_id))
Expand All @@ -166,7 +166,7 @@ static void *pool_data_stack_realloc(pool_t pool, void *mem,
void *new_mem;

/* @UNSAFE */
if (unlikely(new_size == 0 || new_size > SSIZE_T_MAX))
if (unlikely(new_size == 0 || new_size > POOL_MAX_ALLOC_SIZE))
i_panic("Trying to allocate %"PRIuSIZE_T" bytes", new_size);

if (unlikely(dpool->data_stack_frame != data_stack_frame_id))
Expand Down
4 changes: 2 additions & 2 deletions src/lib/mempool-system.c
Expand Up @@ -102,7 +102,7 @@ static void *pool_system_malloc(pool_t pool ATTR_UNUSED, size_t size)
int old_errno = errno;
#endif

if (unlikely(size == 0 || size > SSIZE_T_MAX))
if (unlikely(size == 0 || size > POOL_MAX_ALLOC_SIZE))
i_panic("Trying to allocate %"PRIuSIZE_T" bytes", size);

mem = calloc(size, 1);
Expand Down Expand Up @@ -135,7 +135,7 @@ void pool_system_free(pool_t pool ATTR_UNUSED, void *mem ATTR_UNUSED)
static void *pool_system_realloc(pool_t pool ATTR_UNUSED, void *mem,
size_t old_size, size_t new_size)
{
if (unlikely(new_size == 0 || new_size > SSIZE_T_MAX))
if (unlikely(new_size == 0 || new_size > POOL_MAX_ALLOC_SIZE))
i_panic("Trying to allocate %"PRIuSIZE_T" bytes", new_size);

if (mem == NULL) {
Expand Down
4 changes: 2 additions & 2 deletions src/lib/mempool-unsafe-datastack.c
Expand Up @@ -96,7 +96,7 @@ static void pool_unsafe_data_stack_unref(pool_t *pool ATTR_UNUSED)
static void *pool_unsafe_data_stack_malloc(pool_t pool ATTR_UNUSED,
size_t size)
{
if (unlikely(size == 0 || size > SSIZE_T_MAX))
if (unlikely(size == 0 || size > POOL_MAX_ALLOC_SIZE))
i_panic("Trying to allocate %"PRIuSIZE_T" bytes", size);

return t_malloc0(size);
Expand All @@ -114,7 +114,7 @@ static void *pool_unsafe_data_stack_realloc(pool_t pool ATTR_UNUSED,
void *new_mem;

/* @UNSAFE */
if (new_size == 0 || new_size > SSIZE_T_MAX)
if (new_size == 0 || new_size > POOL_MAX_ALLOC_SIZE)
i_panic("Trying to allocate %"PRIuSIZE_T" bytes", new_size);

if (mem == NULL)
Expand Down
7 changes: 7 additions & 0 deletions src/lib/mempool.c
Expand Up @@ -2,6 +2,13 @@

#include "lib.h"

/* The various implementations of pools API assume that they'll never be
asked for more than SSIZE_T_MAX bytes. This is a sanity check to make
sure nobody accidentally bumped the define beyond what's expected. */
#if POOL_MAX_ALLOC_SIZE > SSIZE_T_MAX
#error "POOL_MAX_ALLOC_SIZE is too large"
#endif

size_t pool_get_exp_grown_size(pool_t pool, size_t old_size, size_t min_size)
{
size_t exp_size, easy_size;
Expand Down
5 changes: 5 additions & 0 deletions src/lib/mempool.h
Expand Up @@ -10,6 +10,11 @@
pools to disable the warning. */
#define MEMPOOL_GROWING "GROWING-"

/* The maximum allocation size that's allowed. Anything larger than that
will panic. No pool ever should need more than 4kB of overhead per
allocation. */
#define POOL_MAX_ALLOC_SIZE (SSIZE_T_MAX - 4096)

/* Memory allocated and reallocated (the new data in it) in pools is always
zeroed, it will cost only a few CPU cycles and may well save some debug
time. */
Expand Down
6 changes: 3 additions & 3 deletions src/lib/test-mempool-allocfree.c
Expand Up @@ -111,13 +111,13 @@ enum fatal_test_state fatal_mempool_allocfree(unsigned int stage)

case 1: /* logically impossible size */
test_expect_fatal_string("Trying to allocate");
(void)p_malloc(pool, SSIZE_T_MAX + 1ULL);
(void)p_malloc(pool, POOL_MAX_ALLOC_SIZE + 1ULL);
return FATAL_TEST_FAILURE;

#if SSIZE_T_MAX > 2147483648 /* malloc(SSIZE_T_MAX) may succeed with 32bit */
#ifdef _LP64 /* malloc(POOL_MAX_ALLOC_SIZE) may succeed with 32bit */
case 2: /* physically impossible size */
test_expect_fatal_string("Out of memory");
(void)p_malloc(pool, SSIZE_T_MAX - 1024);
(void)p_malloc(pool, POOL_MAX_ALLOC_SIZE);
return FATAL_TEST_FAILURE;
#endif

Expand Down
6 changes: 3 additions & 3 deletions src/lib/test-mempool-alloconly.c
Expand Up @@ -70,13 +70,13 @@ enum fatal_test_state fatal_mempool_alloconly(unsigned int stage)

case 1: /* logically impossible size */
test_expect_fatal_string("Trying to allocate");
(void)p_malloc(pool, SSIZE_T_MAX + 1ULL);
(void)p_malloc(pool, POOL_MAX_ALLOC_SIZE + 1ULL);
return FATAL_TEST_FAILURE;

#if SSIZE_T_MAX > 2147483648 /* malloc(SSIZE_T_MAX) may succeed with 32bit */
#ifdef _LP64 /* malloc(POOL_MAX_ALLOC_SIZE) may succeed with 32bit */
case 2: /* physically impossible size */
test_expect_fatal_string("Out of memory");
(void)p_malloc(pool, SSIZE_T_MAX - 1024);
(void)p_malloc(pool, POOL_MAX_ALLOC_SIZE);
return FATAL_TEST_FAILURE;
#endif

Expand Down
19 changes: 12 additions & 7 deletions src/lib/test-mempool.c
Expand Up @@ -8,12 +8,12 @@ typedef char uint32max_array_t[4294967295];
typedef char uint32max_array_t[65535];
#endif

#define BIG_MAX POOL_MAX_ALLOC_SIZE

#if defined(_LP64)
#define LITTLE_MAX ((unsigned long long) UINT32_MAX)
#define BIG_MAX ((unsigned long long) UINT64_MAX)
#define LITTLE_MAX ((unsigned long long) INT32_MAX)
#elif defined(_ILP32)
#define LITTLE_MAX ((unsigned long long) UINT16_MAX)
#define BIG_MAX ((unsigned long long) UINT32_MAX)
#define LITTLE_MAX ((unsigned long long) INT16_MAX)
#else
#error unsupported pointer size
#endif
Expand Down Expand Up @@ -46,32 +46,37 @@ enum fatal_test_state fatal_mempool(unsigned int stage)
static uint32max_array_t *m1;
static uint32_t *m2;

test_expect_fatal_string("memory allocation overflow");
switch(stage) {
case 0:
test_expect_fatal_string("Trying to allocate");
test_begin("fatal mempool overflow");
m1 = p_new(&test_pool, uint32max_array_t, LITTLE_MAX + 3);
return FATAL_TEST_FAILURE;
case 1:
test_expect_fatal_string("Trying to allocate");
m2 = p_new(&test_pool, uint32_t, BIG_MAX / sizeof(uint32_t) + 1);
return FATAL_TEST_FAILURE;
case 2: /* grow */
test_expect_fatal_string("Trying to allocate");
m1 = p_realloc_type(&test_pool, m1, uint32max_array_t,
LITTLE_MAX + 2, LITTLE_MAX + 3);
return FATAL_TEST_FAILURE;
case 3:
test_expect_fatal_string("Trying to allocate");
m2 = p_realloc_type(&test_pool, m2, uint32_t,
BIG_MAX / sizeof(uint32_t),
BIG_MAX / sizeof(uint32_t) + 1);
return FATAL_TEST_FAILURE;
case 4: /* shrink */
test_expect_fatal_string("Trying to allocate");
m1 = p_realloc_type(&test_pool, m1, uint32max_array_t,
LITTLE_MAX + 3, LITTLE_MAX + 2);
return FATAL_TEST_FAILURE;
case 5:
test_expect_fatal_string("Trying to allocate");
m2 = p_realloc_type(&test_pool, m2, uint32_t,
BIG_MAX / sizeof(uint32_t) + 1,
BIG_MAX / sizeof(uint32_t));
BIG_MAX / sizeof(uint32_t) + 2,
BIG_MAX / sizeof(uint32_t) + 1);
return FATAL_TEST_FAILURE;
}
test_expect_fatal_string(NULL);
Expand Down

0 comments on commit f4cac7d

Please sign in to comment.