[PATCH] libstdc++: Ensure that aligned new detects size_t overflow (CVE-2026-95619)

Jonathan Wakely jwakely@redhat.com
Wed Sep 23 08:28:30 GMT 2026


On Wed, 23 Sept 2026 at 00:34, Jonathan Wakely <jwakely@redhat.com> wrote:
>
> Depending on the implementation used for aligned new, we might need to
> round the size argument up to a multiple of the alignment. If either the
> size or the alignment is a huge value then the rounded result could
> wraparound to a small value which would then be successfully allocated
> by the underlying libc allocation function. This means that we return
> a non-null pointer but not of the requested size. Instead we should
> throw std::bad_alloc because the allocation cannot succeed.
>
> This doesn't affect most modern targets because they use the
> posix_memalign implementation, which doesn't do that rounding. The
> affected targets are the ones without posix_memalign (where we round
> before calling C11 aligned_alloc) and without any aligned allocation
> function (where we provide our own __gnu_cxx::aligned_alloc in terms of
> malloc).
>
> libstdc++-v3/ChangeLog:
>
>         * libsupc++/new_opa.cc (__gnu_cxx::aligned_alloc): Check for
>         size_t overflow.
>         (operator new) [_GLIBCXX_HAVE_ALIGNED_ALLOC]: Likewise.
>         * testsuite/18_support/new_aligned_wrap.cc: New test.
> ---
>
> Tested x86_64-linux. This should be backported too.
>
> This fixes https://access.redhat.com/security/cve/cve-2026-95619 so
> distros should backport it to any supported releases that are affected.
> The bug was introduced in GCC 7.1 and fixed for most targets in 12.4.0,
> 13.3.0, and 14.1.0 by PR113258. For *-*-mingw* all versions after 7.1.0
> are affected.

After testing on mingw-w64 I've realized that the first hunk here
doesn't fix it for that platform, because we use the Windows
_aligned_malloc function, which appears to overflow internally.

So another fix is needed for mingw*

>
>  libstdc++-v3/libsupc++/new_opa.cc             | 11 +++++---
>  .../testsuite/18_support/new_aligned_wrap.cc  | 25 +++++++++++++++++++
>  2 files changed, 33 insertions(+), 3 deletions(-)
>  create mode 100644 libstdc++-v3/testsuite/18_support/new_aligned_wrap.cc
>
> diff --git a/libstdc++-v3/libsupc++/new_opa.cc b/libstdc++-v3/libsupc++/new_opa.cc
> index 7bda847a9257..ad9cf21dfcae 100644
> --- a/libstdc++-v3/libsupc++/new_opa.cc
> +++ b/libstdc++-v3/libsupc++/new_opa.cc
> @@ -103,8 +103,10 @@ aligned_alloc (std::size_t al, std::size_t sz)
>    // We need extra bytes to store the original value returned by malloc.
>    if (al < sizeof(void*))
>      al = sizeof(void*);
> -  void* const malloc_ptr = malloc(sz + al);
> -  if (!malloc_ptr)
> +  if (__builtin_add_overflow(sz, al, &sz)) [[unlikely]]
> +    return nullptr;
> +  void* const malloc_ptr = malloc(sz);
> +  if (!malloc_ptr) [[unlikely]]
>      return nullptr;
>    // Align to the requested value, leaving room for the original malloc value.
>    void* const aligned_ptr = (void *) (((uintptr_t) malloc_ptr + al) & -al);
> @@ -141,7 +143,10 @@ operator new (std::size_t sz, std::align_val_t al)
>      align = sizeof(void*);
>  # endif
>    /* C11: the value of size shall be an integral multiple of alignment.  */
> -  sz = (sz + align - 1) & ~(align - 1);
> +  if (!__builtin_add_overflow(sz, align - 1, &sz))
> +    sz &= ~(align - 1);
> +  else
> +    _GLIBCXX_THROW_OR_ABORT(bad_alloc());
>  #endif
>
>    void *p;
> diff --git a/libstdc++-v3/testsuite/18_support/new_aligned_wrap.cc b/libstdc++-v3/testsuite/18_support/new_aligned_wrap.cc
> new file mode 100644
> index 000000000000..d17a97a5e712
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/18_support/new_aligned_wrap.cc
> @@ -0,0 +1,25 @@
> +// { dg-do run }
> +
> +#include <new>
> +#include <testsuite_hooks.h>
> +
> +#pragma GCC diagnostic ignored "-Walloc-size-larger-than="
> +
> +int main()
> +{
> +#if __cpp_aligned_new
> +  std::size_t size = -9;
> +  std::align_val_t align = (std::align_val_t)32;
> +  try
> +    {
> +      (void*) ::operator new(size, align);
> +      VERIFY(false);
> +    }
> +  catch (const std::bad_alloc&)
> +    {
> +    }
> +
> +  void* p = ::operator new(size, align, std::nothrow);
> +  VERIFY(p == 0);
> +#endif
> +}
> --
> 2.55.0
>



More information about the Libstdc++ mailing list