mempool-alloconly.c revision e4a7dbe5e02280e561d9ac438326ed70700cd39b
02c335c23bf5fa225a467c19f2c063fb0dc7b8c3Timo Sirainen/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen/* @UNSAFE: whole file */
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen (MEM_ALIGN(sizeof(size_t)) + MEM_ALIGN(1) + MEM_ALIGN(SENTRY_COUNT))
da7f1a07f583df8905684a7b78469960afd7c78dPhil Carmody /* unsigned char data[]; */
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen#define SIZEOF_POOLBLOCK (MEM_ALIGN(sizeof(struct pool_block)))
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen ((unsigned char *) (block) + SIZEOF_POOLBLOCK)
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen#define DEFAULT_BASE_SIZE MEM_ALIGN(sizeof(struct alloconly_pool))
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainenstatic const char *pool_alloconly_get_name(pool_t pool);
51fb710488efa419a2964335c30451c62b9633b1Timo Sirainenstatic void pool_alloconly_unref(pool_t *pool);
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainenstatic void *pool_alloconly_malloc(pool_t pool, size_t size);
93a7d1ee4b518b5c85f9721dc6539e4dab6aae00Timo Sirainenstatic void pool_alloconly_free(pool_t pool, void *mem);
f7f25f9e1a38678d0e97d2e609beac16285fac6bTimo Sirainenstatic void *pool_alloconly_realloc(pool_t pool, void *mem,
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainenstatic size_t pool_alloconly_get_max_easy_alloc_size(pool_t pool);
35fcdde46a71ac151c2518d48c841019f1181bb2Timo Sirainenstatic void block_alloc(struct alloconly_pool *pool, size_t size);
35fcdde46a71ac151c2518d48c841019f1181bb2Timo Sirainenstatic const struct pool_vfuncs static_alloconly_pool_vfuncs = {
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainenstatic const struct pool static_alloconly_pool = {
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainenstatic void check_sentries(struct pool_block *block)
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen const unsigned char *data = POOL_BLOCK_DATA(block);
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen for (i = 0; i < used_size; ) {
ba8498efbf886ca8b69fdb20c0ba2f5dba9416e3Timo Sirainen if (alloc_size == 0 || used_size - i < alloc_size)
93a7d1ee4b518b5c85f9721dc6539e4dab6aae00Timo Sirainen i_panic("mempool-alloconly: saved alloc size broken");
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen max_pos = i + MEM_ALIGN(alloc_size + SENTRY_COUNT);
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen for (; i < max_pos; i++) {
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen i_panic("mempool-alloconly: buffer overflow");
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen i_panic("mempool-alloconly: used_size wrong");
544a727de8ab0e6c55cab18a7ee475fffdf5eff3Timo Sirainen /* The unused data must be NULs */
cd75c360f244c96b9ee10e01ee3a66fad13183c8Timo Sirainenpool_t pool_alloconly_create(const char *name ATTR_UNUSED, size_t size)
b484ab4b55b0d5341f2f4dd98a655a75f0bf1275Timo Sirainen MEM_ALIGN(sizeof(struct alloconly_pool) + SENTRY_COUNT);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen min_alloc += MEM_ALIGN(strlen(name) + 1 + SENTRY_COUNT) +
3cf67672fdc87583cb23ce088c95bb5dee60e74dTimo Sirainen /* create a fake alloconly_pool so we can call block_alloc() */
cd75c360f244c96b9ee10e01ee3a66fad13183c8Timo Sirainen /* now allocate the actual alloconly_pool from the created block */
3cf67672fdc87583cb23ce088c95bb5dee60e74dTimo Sirainen new_apool = p_new(&apool.pool, struct alloconly_pool, 1);
b484ab4b55b0d5341f2f4dd98a655a75f0bf1275Timo Sirainen if (strncmp(name, MEMPOOL_GROWING, strlen(MEMPOOL_GROWING)) == 0 ||
1b823b2b7790a1e1b7974fcf11a4c48a28e70f37Timo Sirainen new_apool->name = p_strdup(&new_apool->pool, name);
544a727de8ab0e6c55cab18a7ee475fffdf5eff3Timo Sirainen /* set base_size so p_clear() doesn't trash alloconly_pool structure. */
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen new_apool->base_size = new_apool->block->size - new_apool->block->left;
9dd1c256910f1fb42823116a641e7edb3ad11970Timo Sirainen /* the first pool allocations must be from the first block */
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainenpool_t pool_alloconly_create_clean(const char *name, size_t size)
cd75c360f244c96b9ee10e01ee3a66fad13183c8Timo Sirainenstatic void pool_alloconly_destroy(struct alloconly_pool *apool)
b484ab4b55b0d5341f2f4dd98a655a75f0bf1275Timo Sirainen /* destroy all but the last block */
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen /* destroy the last block */
74fb6b5a156c5a61bb6ec827089bb142a10547ddTimo Sirainen safe_memset(block, CLEAR_CHR, SIZEOF_POOLBLOCK + apool->block->size);
b484ab4b55b0d5341f2f4dd98a655a75f0bf1275Timo Sirainenstatic const char *pool_alloconly_get_name(pool_t pool ATTR_UNUSED)
3cf67672fdc87583cb23ce088c95bb5dee60e74dTimo Sirainen struct alloconly_pool *apool = (struct alloconly_pool *)pool;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen return "alloconly";
9dd1c256910f1fb42823116a641e7edb3ad11970Timo Sirainen struct alloconly_pool *apool = (struct alloconly_pool *)pool;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen struct alloconly_pool *apool = (struct alloconly_pool *)*pool;
57d2429fae575e96ca276355af675deb66b76d00Timo Sirainen /* erase the pointer before freeing anything, as the pointer may
401b0787fff2dc986a5321ddb32acb1947ff66b0Timo Sirainen exist inside the pool's memory area */
61618d4c58080570f689614fec204ae14e90cef2Timo Sirainenstatic void block_alloc(struct alloconly_pool *apool, size_t size)
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen /* each block is at least twice the size of the previous one */
44cf91b7a701a9b4d9f59a990552eab4f7f64fbcTimo Sirainen /* i_debug() overwrites unallocated data in data
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen stack, so make sure everything is allocated before
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen calling it. */
4addfd26372c6ae32ec93252696d86fd32081327Timo Sirainen i_debug("Growing pool '%s' with: %"PRIuSIZE_T,
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen i_fatal_status(FATAL_OUTOFMEM, "block_alloc(%"PRIuSIZE_T
7744586e3e0fd60158abfbb03a233d3bd8d6c48bTimo Sirainenstatic void *pool_alloconly_malloc(pool_t pool, size_t size)
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen struct alloconly_pool *apool = (struct alloconly_pool *)pool;
f2df3069766c747cbf020fea5d3a4261949064b0Timo Sirainen if (unlikely(size == 0 || size > SSIZE_T_MAX - POOL_ALLOCONLY_MAX_EXTRA))
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen i_panic("Trying to allocate %"PRIuSIZE_T" bytes", size);
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen alloc_size = MEM_ALIGN(sizeof(size)) + MEM_ALIGN(size + SENTRY_COUNT);
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen /* we need a new block */
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen block_alloc(apool, alloc_size + SIZEOF_POOLBLOCK);
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen mem = PTR_OFFSET(mem, MEM_ALIGN(sizeof(size)));
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen /* write CLEAR_CHRs to sentry */
f01eb1f51d618633c0189be9ab60a774f47fb7dfTimo Sirainenstatic void pool_alloconly_free(pool_t pool, void *mem)
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen struct alloconly_pool *apool = (struct alloconly_pool *)pool;
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen /* we can free only the last allocation */
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen memset(mem, 0, apool->block->last_alloc_size);
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen apool->block->left += apool->block->last_alloc_size;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainenstatic bool pool_try_grow(struct alloconly_pool *apool, void *mem, size_t size)
d477acb83e14a776ece4ca94dcd1869e75d0c6eeTimo Sirainen /* see if we want to grow the memory we allocated last */
738cfeb96c4b9cd92aa3c791d77734c2745cdd1aTimo Sirainen /* yeah, see if we can grow */
738cfeb96c4b9cd92aa3c791d77734c2745cdd1aTimo Sirainen if (apool->block->left >= size-apool->block->last_alloc_size) {
4ee00532a265bdfb38539d811fcd12d51210ac35Timo Sirainen /* just shrink the available size */
738cfeb96c4b9cd92aa3c791d77734c2745cdd1aTimo Sirainenstatic void *pool_alloconly_realloc(pool_t pool, void *mem,
738cfeb96c4b9cd92aa3c791d77734c2745cdd1aTimo Sirainen struct alloconly_pool *apool = (struct alloconly_pool *)pool;
738cfeb96c4b9cd92aa3c791d77734c2745cdd1aTimo Sirainen unsigned char *new_mem;
738cfeb96c4b9cd92aa3c791d77734c2745cdd1aTimo Sirainen if (unlikely(new_size == 0 || new_size > SSIZE_T_MAX - POOL_ALLOCONLY_MAX_EXTRA))
738cfeb96c4b9cd92aa3c791d77734c2745cdd1aTimo Sirainen i_panic("Trying to allocate %"PRIuSIZE_T" bytes", new_size);
4addfd26372c6ae32ec93252696d86fd32081327Timo Sirainen /* see if we can directly grow it */
4addfd26372c6ae32ec93252696d86fd32081327Timo Sirainen /* slow way - allocate + copy */
4addfd26372c6ae32ec93252696d86fd32081327Timo Sirainen new_mem = pool_alloconly_malloc(pool, new_size);
4addfd26372c6ae32ec93252696d86fd32081327Timo Sirainen struct alloconly_pool *apool = (struct alloconly_pool *)pool;
3190f12fb96daf61f7c880390472e18184cbb2d8Timo Sirainen /* destroy all blocks but the oldest, which contains the
3190f12fb96daf61f7c880390472e18184cbb2d8Timo Sirainen struct alloconly_pool allocation. */
4addfd26372c6ae32ec93252696d86fd32081327Timo Sirainen safe_memset(block, CLEAR_CHR, SIZEOF_POOLBLOCK + block->size);
15f526e5ac611b4532568d131fcd0abf664abe41Timo Sirainen /* clear the first block */
f93c833d644ecff0b0f80bee4f1cdde3e697b5c8Timo Sirainen memset(PTR_OFFSET(POOL_BLOCK_DATA(apool->block), base_size), 0,
4fc74bba3548987b7e8597491cd9fafc1f701be6Timo Sirainenstatic size_t pool_alloconly_get_max_easy_alloc_size(pool_t pool)
4fc74bba3548987b7e8597491cd9fafc1f701be6Timo Sirainen struct alloconly_pool *apool = (struct alloconly_pool *)pool;
7289c5600711b45f30fe289ab5b0293b51d87041Timo Sirainensize_t pool_alloconly_get_total_used_size(pool_t pool)
7289c5600711b45f30fe289ab5b0293b51d87041Timo Sirainen struct alloconly_pool *apool = (struct alloconly_pool *)pool;
7289c5600711b45f30fe289ab5b0293b51d87041Timo Sirainen i_assert(pool->v == &static_alloconly_pool_vfuncs);
7289c5600711b45f30fe289ab5b0293b51d87041Timo Sirainen for (block = apool->block; block != NULL; block = block->prev)
7289c5600711b45f30fe289ab5b0293b51d87041Timo Sirainensize_t pool_alloconly_get_total_alloc_size(pool_t pool)
7289c5600711b45f30fe289ab5b0293b51d87041Timo Sirainen struct alloconly_pool *apool = (struct alloconly_pool *)pool;
d477acb83e14a776ece4ca94dcd1869e75d0c6eeTimo Sirainen i_assert(pool->v == &static_alloconly_pool_vfuncs);
9dd1c256910f1fb42823116a641e7edb3ad11970Timo Sirainen for (block = apool->block; block != NULL; block = block->prev)