From e6f0bf2679ba3e0084b28b51ecc2d2094e479ca7 Mon Sep 17 00:00:00 2001 From: Josef 'Jeff' Sipek Date: Fri, 22 Jun 2018 16:51:40 -0400 Subject: [PATCH] lib: mempool - Define a global max allocation size supported by all pools 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. --- src/lib/mempool-allocfree.c | 8 ++++++-- src/lib/mempool-alloconly.c | 7 +++++-- src/lib/mempool-datastack.c | 4 ++-- src/lib/mempool-system.c | 4 ++-- src/lib/mempool-unsafe-datastack.c | 4 ++-- src/lib/mempool.c | 7 +++++++ src/lib/mempool.h | 5 +++++ src/lib/test-mempool-allocfree.c | 6 +++--- src/lib/test-mempool-alloconly.c | 6 +++--- src/lib/test-mempool.c | 19 ++++++++++++------- 10 files changed, 47 insertions(+), 23 deletions(-) diff --git a/src/lib/mempool-allocfree.c b/src/lib/mempool-allocfree.c index d1fbc0bd2f..da4b6510db 100644 --- a/src/lib/mempool-allocfree.c +++ b/src/lib/mempool-allocfree.c @@ -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", @@ -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); @@ -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) diff --git a/src/lib/mempool-alloconly.c b/src/lib/mempool-alloconly.c index be669454e8..999ebd9e6e 100644 --- a/src/lib/mempool-alloconly.c +++ b/src/lib/mempool-alloconly.c @@ -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; @@ -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 @@ -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) diff --git a/src/lib/mempool-datastack.c b/src/lib/mempool-datastack.c index e240dc40a3..f645fd2792 100644 --- a/src/lib/mempool-datastack.c +++ b/src/lib/mempool-datastack.c @@ -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)) @@ -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)) diff --git a/src/lib/mempool-system.c b/src/lib/mempool-system.c index 507b70a73d..ab8373142b 100644 --- a/src/lib/mempool-system.c +++ b/src/lib/mempool-system.c @@ -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); @@ -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) { diff --git a/src/lib/mempool-unsafe-datastack.c b/src/lib/mempool-unsafe-datastack.c index 7958f380d2..6bc612927d 100644 --- a/src/lib/mempool-unsafe-datastack.c +++ b/src/lib/mempool-unsafe-datastack.c @@ -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); @@ -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) diff --git a/src/lib/mempool.c b/src/lib/mempool.c index 3d3c94c052..a657481f93 100644 --- a/src/lib/mempool.c +++ b/src/lib/mempool.c @@ -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; diff --git a/src/lib/mempool.h b/src/lib/mempool.h index 023beedad2..529121b56b 100644 --- a/src/lib/mempool.h +++ b/src/lib/mempool.h @@ -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. */ diff --git a/src/lib/test-mempool-allocfree.c b/src/lib/test-mempool-allocfree.c index ce5adacb4a..6fd69dc001 100644 --- a/src/lib/test-mempool-allocfree.c +++ b/src/lib/test-mempool-allocfree.c @@ -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 diff --git a/src/lib/test-mempool-alloconly.c b/src/lib/test-mempool-alloconly.c index 19c1b57eff..c3b0001b79 100644 --- a/src/lib/test-mempool-alloconly.c +++ b/src/lib/test-mempool-alloconly.c @@ -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 diff --git a/src/lib/test-mempool.c b/src/lib/test-mempool.c index 649b121854..74495a877b 100644 --- a/src/lib/test-mempool.c +++ b/src/lib/test-mempool.c @@ -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 @@ -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); -- 2.47.3