libmicrohttpd

HTTP/1.x server C library (MHD 1.x, stable)
Log | Files | Refs | Submodules | README | LICENSE

commit abcbf778676ee0e081e4fa99443f4348828d6ab6
parent 899730e8c70cd7679394d5c940e8ad953eea262b
Author: Evgeny Grin (Karlson2k) <k2k@narod.ru>
Date:   Sat,  9 Oct 2021 17:24:04 +0300

Partially reworked memory allocation from the pool

More robust implementation, should catch non-standard situations.
Always track read and write buffers, even if size is zero as
buffer pointers are used for data size calculations.

Diffstat:
Msrc/microhttpd/connection.c | 51++++++++++++++++++++++++---------------------------
Msrc/microhttpd/memorypool.c | 68+++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
Msrc/microhttpd/memorypool.h | 38+++++++++++++++++++++++++-------------
3 files changed, 100 insertions(+), 57 deletions(-)

diff --git a/src/microhttpd/connection.c b/src/microhttpd/connection.c @@ -204,16 +204,12 @@ connection_alloc_memory (struct MHD_Connection *connection, { struct MHD_Connection *const c = connection; /* a short alias */ struct MemoryPool *const pool = c->pool; /* a short alias */ - size_t required_free_size; /**< The required amount of free memory */ - size_t pool_free; /**< The amount of free memory in the pool */ + size_t need_to_free; /**< The required amount of free memory */ void *res; - required_free_size = MHD_pool_alloc_size (size); - pool_free = MHD_pool_get_free (pool); - if (pool_free < required_free_size) + res = MHD_pool_try_alloc (pool, size, &need_to_free); + if (NULL == res) { - size_t need_to_free = required_free_size - pool_free; - mhd_assert (MHD_pool_alloc_size (need_to_free) == need_to_free); if (NULL != c->write_buffer) { /* The connection is in the sending phase */ @@ -227,12 +223,10 @@ connection_alloc_memory (struct MHD_Connection *connection, c->write_buffer_size, new_buf_size); mhd_assert (c->write_buffer == buf); -#ifdef NDEBUG - (void) buf; /* mute compiler warning */ -#endif mhd_assert (c->write_buffer_append_offset <= new_buf_size); mhd_assert (c->write_buffer_send_offset <= new_buf_size); c->write_buffer_size = new_buf_size; + c->write_buffer = buf; } else return NULL; @@ -249,20 +243,18 @@ connection_alloc_memory (struct MHD_Connection *connection, c->read_buffer_size, new_buf_size); mhd_assert (c->read_buffer == buf); -#ifdef NDEBUG - (void) buf; /* mute compiler warning */ -#endif mhd_assert (c->read_buffer_offset <= new_buf_size); c->read_buffer_size = new_buf_size; + c->read_buffer = buf; } else return NULL; } else return NULL; + res = MHD_pool_allocate (pool, size, true); + mhd_assert (NULL != res); /* It has been checked that pool has enough space */ } - res = MHD_pool_allocate (pool, size, true); - mhd_assert (NULL != res); /* It has been checked that pool has enough space */ return res; } @@ -1486,7 +1478,7 @@ try_grow_read_buffer (struct MHD_Connection *connection, /** - * Shrink connection read buffer to the zero of data in the buffer + * Shrink connection read buffer to the zero size of free space in the buffer * @param connection the connection whose read buffer is being manipulated */ static void @@ -1495,11 +1487,10 @@ connection_shrink_read_buffer (struct MHD_Connection *connection) struct MHD_Connection *const c = connection; /**< a short alias */ void *new_buf; - if (NULL == c->read_buffer) + if ((NULL == c->read_buffer) || (0 == c->read_buffer_size)) { mhd_assert (0 == c->read_buffer_size); mhd_assert (0 == c->read_buffer_offset); - c->read_buffer = NULL; return; } @@ -1507,12 +1498,8 @@ connection_shrink_read_buffer (struct MHD_Connection *connection) new_buf = MHD_pool_reallocate (c->pool, c->read_buffer, c->read_buffer_size, c->read_buffer_offset); mhd_assert (c->read_buffer == new_buf); -#ifdef NDEBUG - (void) new_buf; /* squash compiler warning */ -#endif /* NDEBUG */ + c->read_buffer = new_buf; c->read_buffer_size = c->read_buffer_offset; - if (0 == c->read_buffer_size) - c->read_buffer = NULL; } @@ -1582,16 +1569,25 @@ connection_shrink_write_buffer (struct MHD_Connection *connection) mhd_assert (c->write_buffer_append_offset >= c->write_buffer_send_offset); mhd_assert (c->write_buffer_size >= c->write_buffer_append_offset); - if (NULL == c->write_buffer) + if ( (NULL == c->write_buffer) || (0 == c->write_buffer_size)) + { + mhd_assert (0 == c->write_buffer_append_offset); + mhd_assert (0 == c->write_buffer_send_offset); + c->write_buffer = NULL; return; + } if (c->write_buffer_append_offset == c->write_buffer_size) return; new_buf = MHD_pool_reallocate (pool, c->write_buffer, c->write_buffer_size, c->write_buffer_append_offset); - mhd_assert (c->write_buffer == new_buf); - (void) new_buf; /* squash compiler warning */ + mhd_assert ((c->write_buffer == new_buf) || \ + (0 == c->write_buffer_append_offset)); c->write_buffer_size = c->write_buffer_append_offset; + if (0 == c->write_buffer_size) + c->write_buffer = NULL; + else + c->write_buffer = new_buf; } @@ -1987,8 +1983,9 @@ build_header_response (struct MHD_Connection *connection) buf = c->write_buffer; pos = c->write_buffer_append_offset; buf_size = c->write_buffer_size; - if ((NULL == buf) || (0 == buf_size)) + if (0 == buf_size) return MHD_NO; + mhd_assert (NULL != buf); /* * The status line * */ diff --git a/src/microhttpd/memorypool.c b/src/microhttpd/memorypool.c @@ -105,22 +105,6 @@ MHD_init_mem_pools_ (void) /** - * Get the real size that would be allocated by the memory pool when - * requested to allocate @a size. - * @param size the size of memory area that would be rounded up to the - * allocation granularity - * @return the size that would be allocated by #MHD_pool_allocate() when - * requested to allocate @a size. It is also minimal size of free - * space in the pool required to #MHD_pool_allocate() succeed. - */ -size_t -MHD_pool_alloc_size (size_t size) -{ - return ROUND_TO_ALIGN (size); -} - - -/** * Handle for a memory pool. Pools are not reentrant and must not be * used by multiple threads. */ @@ -312,6 +296,56 @@ MHD_pool_allocate (struct MemoryPool *pool, /** + * Try to allocate @a size bytes memory area from the @a pool. + * + * If allocation fails, @a required_bytes is updated with size required to be + * freed in the @a pool from relocatable area to allocate requested number + * of bytes. + * Allocated memory area is always not rellocatable ("from end"). + * + * @param pool memory pool to use for the operation + * @param size the size of memory in bytes to allocate + * @param[out] required_bytes the pointer to variable to be updated with + * the size of the required additional free + * memory area, not updated if function succeed. + * Cannot be NULL. + * @return the pointer to allocated memory area if succeed, + * NULL if the pool doesn't have enough space, required_bytes is updated + * with amount of space needed to be freed in relocatable area or + * set to SIZE_MAX if requested size is too large for the pool. + */ +void * +MHD_pool_try_alloc (struct MemoryPool *pool, + size_t size, + size_t *required_bytes) +{ + void *ret; + size_t asize; + + mhd_assert (pool->end >= pool->pos); + mhd_assert (pool->size >= pool->end - pool->pos); + asize = ROUND_TO_ALIGN (size); + if ( (0 == asize) && (0 != size) ) + { /* size is too close to SIZE_MAX, very unlikely */ + *required_bytes = SIZE_MAX; + return NULL; + } + if ( (pool->pos + asize > pool->end) || + (pool->pos + asize < pool->pos)) + { + if (asize <= pool->end) + *required_bytes = asize - (pool->end - pool->pos); + else + *required_bytes = SIZE_MAX; + return NULL; + } + ret = &pool->memory[pool->end - asize]; + pool->end -= asize; + return ret; +} + + +/** * Reallocate a block of memory obtained from the pool. * This is particularly efficient when growing or * shrinking the block that was last (re)allocated. @@ -355,7 +389,7 @@ MHD_pool_reallocate (struct MemoryPool *pool, pool->end); if (0 != old_size) - { /* Need to save some data */ + { /* Have previously allocated data */ const size_t old_offset = (uint8_t*) old - pool->memory; const bool shrinking = (old_size > new_size); /* Try resizing in-place */ diff --git a/src/microhttpd/memorypool.h b/src/microhttpd/memorypool.h @@ -53,19 +53,6 @@ MHD_init_mem_pools_ (void); /** - * Get the real size that would be allocated by the memory pool when - * requested to allocate @a size. - * @param size the size of memory area that would be rounded up to the - * allocation granularity - * @return the size that would be allocated by #MHD_pool_allocate() when - * requested to allocate @a size. It is also minimal size of free - * space in the pool required to #MHD_pool_allocate() succeed. - */ -size_t -MHD_pool_alloc_size (size_t size); - - -/** * Create a memory pool. * * @param max maximum size of the pool @@ -102,6 +89,31 @@ MHD_pool_allocate (struct MemoryPool *pool, /** + * Try to allocate @a size bytes memory area from the @a pool. + * + * If allocation fails, @a required_bytes is updated with size required to be + * freed in the @a pool from relocatable area to allocate requested number + * of bytes. + * Allocated memory area is always not rellocatable ("from end"). + * + * @param pool memory pool to use for the operation + * @param size the size of memory in bytes to allocate + * @param[out] required_bytes the pointer to variable to be updated with + * the size of the required additional free + * memory area, not updated if function succeed. + * Cannot be NULL. + * @return the pointer to allocated memory area if succeed, + * NULL if the pool doesn't have enough space, required_bytes is updated + * with amount of space needed to be freed in relocatable area or + * set to SIZE_MAX if requested size is too large for the pool. + */ +void * +MHD_pool_try_alloc (struct MemoryPool *pool, + size_t size, + size_t *required_bytes); + + +/** * Reallocate a block of memory obtained from the pool. * This is particularly efficient when growing or * shrinking the block that was last (re)allocated.