mirror of
https://git.gnunet.org/libmicrohttpd.git
synced 2026-09-25 04:09:31 +03:00
fix red-zone support in memorypool
This commit is contained in:
@@ -96,6 +96,25 @@
|
||||
#define ROUND_TO_ALIGN(n) (((n) + (ALIGN_SIZE - 1)) \
|
||||
/ (ALIGN_SIZE) *(ALIGN_SIZE))
|
||||
|
||||
/* ROUND_TO_ALIGN_PLUS_RED_ZONE() rounds up and, when user poisoning is
|
||||
active, adds a red zone, so its result is always >= its argument --
|
||||
unless it wrapped. Every caller that passes a caller-supplied size
|
||||
must therefore reject the result with
|
||||
|
||||
if (asize < size)
|
||||
|
||||
and not by testing for zero. Testing for zero is correct only in the
|
||||
build where the red zone is 0: ROUND_TO_ALIGN() lands exactly on 0 for
|
||||
the top ALIGN_SIZE-1 values of size_t, so a zero result is the only
|
||||
wrapped outcome there. Adding a red zone moves that outcome to
|
||||
_MHD_RED_ZONE_SIZE, which is non-zero and small enough to pass the
|
||||
remaining-space test below it, so a zero test silently stops firing in
|
||||
exactly the build that has the extra instrumentation. Sizes that are
|
||||
already bounded by pool->size (MHD_pool_reset(), the block_offset +
|
||||
block_size expressions) cannot wrap and need no such test -- but they
|
||||
do need the trailing red zone clamped to pool->size, see the two sites
|
||||
that do so. */
|
||||
|
||||
|
||||
#ifndef MHD_ASAN_POISON_ACTIVE
|
||||
#define _MHD_NOSANITIZE_PTRS /**/
|
||||
@@ -408,8 +427,8 @@ MHD_pool_allocate (struct MemoryPool *pool,
|
||||
mhd_assert (pool->size >= pool->end - pool->pos);
|
||||
mhd_assert (pool->pos == ROUND_TO_ALIGN (pool->pos));
|
||||
asize = ROUND_TO_ALIGN_PLUS_RED_ZONE (size);
|
||||
if ( (0 == asize) && (0 != size) )
|
||||
return NULL; /* size too close to SIZE_MAX */
|
||||
if (asize < size)
|
||||
return NULL; /* Value wrap, @a size is too close to SIZE_MAX */
|
||||
if (asize > pool->end - pool->pos)
|
||||
return NULL;
|
||||
if (from_end)
|
||||
@@ -490,8 +509,8 @@ MHD_pool_try_alloc (struct MemoryPool *pool,
|
||||
mhd_assert (pool->size >= pool->end - pool->pos);
|
||||
mhd_assert (pool->pos == ROUND_TO_ALIGN (pool->pos));
|
||||
asize = ROUND_TO_ALIGN_PLUS_RED_ZONE (size);
|
||||
if ( (0 == asize) && (0 != size) )
|
||||
{ /* size is too close to SIZE_MAX, very unlikely */
|
||||
if (asize < size)
|
||||
{ /* Value wrap, @a size is too close to SIZE_MAX, very unlikely */
|
||||
*required_bytes = SIZE_MAX;
|
||||
return NULL;
|
||||
}
|
||||
@@ -623,8 +642,7 @@ MHD_pool_reallocate (struct MemoryPool *pool,
|
||||
}
|
||||
/* Need to allocate new block */
|
||||
asize = ROUND_TO_ALIGN_PLUS_RED_ZONE (new_size);
|
||||
if ( ( (0 == asize) &&
|
||||
(0 != new_size) ) || /* Value wrap, too large new_size. */
|
||||
if ( (asize < new_size) || /* Value wrap, too large new_size. */
|
||||
(asize > pool->end - pool->pos) ) /* Not enough space */
|
||||
return NULL;
|
||||
|
||||
@@ -695,8 +713,15 @@ MHD_pool_deallocate (struct MemoryPool *pool,
|
||||
exactly full the two are equal, and a block allocated "from the
|
||||
end" then also satisfies 'block_offset <= pool->pos', so it
|
||||
would be mistaken for a normal block and never returned. */
|
||||
const size_t alg_end =
|
||||
size_t alg_end =
|
||||
ROUND_TO_ALIGN_PLUS_RED_ZONE (block_offset + block_size);
|
||||
/* Clamped exactly as MHD_pool_reset() clamps pool->pos, and for the
|
||||
same reason: a block that reaches the end of the pool has no room
|
||||
for a trailing red zone, so its recorded end is pool->size. The
|
||||
two have to agree or the "is this the last block" test below can
|
||||
never match such a block. */
|
||||
if (alg_end > pool->size)
|
||||
alg_end = pool->size;
|
||||
mhd_assert (alg_end <= pool->pos);
|
||||
if (alg_end == pool->pos)
|
||||
{
|
||||
@@ -834,6 +859,16 @@ MHD_pool_reset (struct MemoryPool *pool,
|
||||
to_zero);
|
||||
}
|
||||
pool->pos = ROUND_TO_ALIGN_PLUS_RED_ZONE (new_size);
|
||||
/* The red zone is a gap kept *inside* the pool so that an overrun of the
|
||||
block lands in poisoned bytes. There is no room for it once the kept
|
||||
block reaches the end of the pool, and none is needed there either --
|
||||
past pool->size the allocation's own ASAN red zone takes over. Without
|
||||
this clamp pool->pos would be set past pool->end for any @a new_size
|
||||
within a red zone of pool->size, breaking the pool->end >= pool->pos
|
||||
invariant that every other entry point asserts. In the build with no
|
||||
user poisoning _MHD_RED_ZONE_SIZE is 0 and this can never fire. */
|
||||
if (pool->pos > pool->size)
|
||||
pool->pos = pool->size;
|
||||
pool->end = pool->size;
|
||||
_MHD_POISON_MEMORY (((uint8_t *) pool->memory) + new_size, \
|
||||
pool->size - new_size);
|
||||
|
||||
Reference in New Issue
Block a user