[PATCH v3] memalign: reinstate alignment overflow check (CVE-2026-0861)
Siddhesh Poyarekar
siddhesh@gotplt.org
Thu Jan 15 15:59:32 GMT 2026
On 2026-01-15 10:31, Wilco Dijkstra wrote:
> Hi Siddhesh,
>
> You really opened a can of worms here. All of the arena_get and arena_get_retry
> calls can overflow since they are passed the *original* user size. So checking for
OK, lets shut that can a bit then ;) My intent here is to constrain the
fix to specifically CVE-2026-0861 and I've added both checks because
they existed before and still make sense.
> overflow in one case isn't going to help at all. The arena code will add more to it,
> again without any checks:
>
> h = new_heap (size + (sizeof (*h) + sizeof (*a) + MALLOC_ALIGNMENT),
Having a heap larger than PTRDIFF_MAX (if the system is actually able to
allocate it of course) is OK from the user perspective since the user is
only getting a chunk that is within PTRDIFF_MAX bounds. That
computation does not overflow size_t as long as size is checked to be
within PTRDIFF_MAX bounds.
> Size is the value passed to arena_get(_retry), but that is typically the user
> size, eg:
>
> static void * __attribute_noinline__
> __libc_malloc2 (size_t bytes)
> {
> mstate ar_ptr;
> void *victim;
This needs an early bounds check fail then.
>
> if (SINGLE_THREAD_P)
> {
> victim = tag_new_usable (_int_malloc (&main_arena, bytes));
> assert (!victim || chunk_is_mmapped (mem2chunk (victim)) ||
> &main_arena == arena_for_chunk (mem2chunk (victim)));
> return victim;
> }
>
> arena_get (ar_ptr, bytes);
>
> victim = _int_malloc (ar_ptr, bytes);
>
> Same for calloc2.
Likewise.
>
> - arena_get (ar_ptr, bytes + alignment + MINSIZE);
> + ptrdiff_t total;
> + /* ALIGNMENT is a power of two, so adding MINSIZE won't overflow it. */
> + if (__glibc_unlikely (__builtin_add_overflow (bytes,
> + alignment + MINSIZE,
> + &total)))
> + {
> + __set_errno (ENOMEM);
> + return NULL;
> + }
> +
> + arena_get (ar_ptr, total);
>
> This particular unsigned->signed add overflow is quite complex and adds a
> lot of extra code. All unnecessary complexity since arena_get doesn't even use
> the value in almost all cases - it's only used in the rare case when it creates a
> new arena. And it's not clear there is any advantage in that case, let alone
> trying to compute an accurate value of the total size...
>
> So I don't see any point in passing anything to arena_get here - just use
> arena_get (ar_ptr, 0) everywhere.
Or of course, use arena_get (ar_ptr, 0) and avoid the problem
altogether, which has the problem of the arena maybe being insufficient.
Anyway, I'm shutting this can by dropping this hunk, I'll limit the
change to just _int_memalign; I feel like Florian had seen this coming
when he suggested that ;)
> + ptrdiff_t total;
> + /* ALIGNMENT is a power of two, so adding MINSIZE won't overflow it. */
> + if (__glibc_unlikely (__builtin_add_overflow (nb,
> + alignment + MINSIZE,
> + &total)))
> + {
> + __set_errno (ENOMEM);
> + return NULL;
> + }
>
> Same here, a lot of code to write: if (alignment > PTRDIFF_MAX) when then trivially
> merges with the earlier if statement.
It doesn't though, since bytes <= PTRDIFF_MAX does not imply nb <=
PTRDIFF_MAX. Also, even with nb == alignment == PTRDIFF_MAX, the checks
will pass, but nb + alignment + MINSIZE will overflow.
Sid
More information about the Libc-alpha
mailing list