[libcxx-commits] [libcxx] [libc++] Fix std::pmr::new_delete_resource() returning underaligned pointers (PR #219166)

Nikolas Klauser via libcxx-commits libcxx-commits at lists.llvm.org
Thu Aug 27 04:22:25 PDT 2026


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/219166

>From 7848eda2af8e7a1c98121c18dada8e9039ed1c37 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Thu, 27 Aug 2026 12:18:05 +0200
Subject: [PATCH] [libc++] Fix std::pmr::new_delete_resource() returning
 underaligned pointers

---
 libcxx/src/memory_resource.cpp                | 14 +++--
 .../new_delete_resource.align.pass.cpp        | 59 +++++++++++++++++++
 .../allocate_overaligned_request.pass.cpp     |  2 +-
 3 files changed, 69 insertions(+), 6 deletions(-)
 create mode 100644 libcxx/test/std/utilities/utility/mem.res/mem.res.global/new_delete_resource.align.pass.cpp

diff --git a/libcxx/src/memory_resource.cpp b/libcxx/src/memory_resource.cpp
index c58e4a3790b25..4930942e0b12f 100644
--- a/libcxx/src/memory_resource.cpp
+++ b/libcxx/src/memory_resource.cpp
@@ -34,11 +34,11 @@ static bool is_aligned_to(void* ptr, size_t align) {
 class _LIBCPP_HIDDEN __new_delete_memory_resource_imp : public memory_resource {
   void* do_allocate(size_t bytes, size_t align) override {
 #if _LIBCPP_HAS_ALIGNED_ALLOCATION
-    return std::__libcpp_allocate<std::byte>(__element_count(bytes), align);
+    return ::operator new(bytes, std::align_val_t(align));
 #else
-    if (bytes == 0)
-      bytes = 1;
-    std::byte* result = std::__libcpp_allocate<std::byte>(__element_count(bytes), align);
+    if (bytes < align)
+      bytes = align;
+    std::byte* result = ::operator new(bytes);
     if (!is_aligned_to(result, align)) {
       std::__libcpp_deallocate<std::byte>(result, __element_count(bytes), align);
       std::__throw_bad_alloc();
@@ -48,7 +48,11 @@ class _LIBCPP_HIDDEN __new_delete_memory_resource_imp : public memory_resource {
   }
 
   void do_deallocate(void* p, size_t bytes, size_t align) override {
-    std::__libcpp_deallocate<std::byte>(static_cast<std::byte*>(p), __element_count(bytes), align);
+#if _LIBCPP_HAS_ALIGNED_ALLOCATION
+  ::operator delete(p, align_val_t(align));
+#else
+  ::operator delete(p);
+#endif
   }
 
   bool do_is_equal(const memory_resource& other) const noexcept override { return &other == this; }
diff --git a/libcxx/test/std/utilities/utility/mem.res/mem.res.global/new_delete_resource.align.pass.cpp b/libcxx/test/std/utilities/utility/mem.res/mem.res.global/new_delete_resource.align.pass.cpp
new file mode 100644
index 0000000000000..57a0ee37546de
--- /dev/null
+++ b/libcxx/test/std/utilities/utility/mem.res/mem.res.global/new_delete_resource.align.pass.cpp
@@ -0,0 +1,59 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+// TODO: Change to XFAIL once https://llvm.org/PR40995 is fixed
+// UNSUPPORTED: availability-pmr-missing
+
+// <memory_resource>
+
+// memory_resource *new_delete_resource()
+
+#include <algorithm>
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+#include <memory_resource>
+#include <new>
+
+alignas(128) char buffer[128];
+bool allocated = false;
+
+void* operator new(std::size_t size) {
+  if (allocated)
+    throw std::bad_alloc();
+
+  allocated = true;
+  if (size <= 4)
+    return buffer + 4;
+  if (size <= 8)
+    return buffer + 8;
+  if (size <= 16)
+    return buffer + 16;
+  if (size <= 128)
+    return buffer + 128;
+  throw std::bad_alloc();
+}
+
+void operator delete(void*) noexcept { allocated = false; }
+
+void* operator new(std::size_t size, std::align_val_t align) {
+  if (static_cast<size_t>(align) > 128)
+    throw std::bad_alloc();
+  return operator new(std::max(size, static_cast<size_t>(align)));
+}
+
+void operator delete(void*, std::align_val_t) noexcept { allocated = false; }
+
+int main(int, char**) {
+  std::pmr::memory_resource* res = std::pmr::new_delete_resource();
+  void* ptr                      = res->allocate(1, __STDCPP_DEFAULT_NEW_ALIGNMENT__);
+  assert((reinterpret_cast<uintptr_t>(ptr) & (__STDCPP_DEFAULT_NEW_ALIGNMENT__ - 1)) == 0);
+
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/utility/mem.res/mem.res.monotonic.buffer/mem.res.monotonic.buffer.mem/allocate_overaligned_request.pass.cpp b/libcxx/test/std/utilities/utility/mem.res/mem.res.monotonic.buffer/mem.res.monotonic.buffer.mem/allocate_overaligned_request.pass.cpp
index 95c6b36b71f98..479b0f87215b2 100644
--- a/libcxx/test/std/utilities/utility/mem.res/mem.res.monotonic.buffer/mem.res.monotonic.buffer.mem/allocate_overaligned_request.pass.cpp
+++ b/libcxx/test/std/utilities/utility/mem.res/mem.res.monotonic.buffer/mem.res.monotonic.buffer.mem/allocate_overaligned_request.pass.cpp
@@ -42,7 +42,7 @@ int main(int, char**) {
   ret = r1.allocate(globalMemCounter.last_new_size, 4);
   assert(ret != nullptr);
   ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(globalMemCounter.checkNewCalledEq(2));
-  ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(globalMemCounter.checkAlignedNewCalledEq(1));
+  ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(globalMemCounter.checkAlignedNewCalledEq(2));
 
   return 0;
 }



More information about the libcxx-commits mailing list