Commit 84db13d2 authored by Benoit Lize's avatar Benoit Lize Committed by Chromium LUCI CQ

[base/allocator] Properly intercept "new (std::nothrow)".

This is used by several clients in Chromium, yet is not implemented the
right way in the shim. This means that large allocations crash rather
than returning nullptr with PartitionAlloc-Everywhere.

Bug: 1121427, dawn:579
Change-Id: Iaf1d3d1abcee5790f9485e1a31dbacfbb2825f40
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2557987Reviewed-by: default avatarYuki Shiino <yukishiino@chromium.org>
Reviewed-by: default avatarWill Harris <wfh@chromium.org>
Commit-Queue: Benoit L <lizeb@chromium.org>
Cr-Commit-Position: refs/heads/master@{#832769}
parent 6eebbc15
...@@ -149,6 +149,15 @@ ALWAYS_INLINE void* ShimCppNew(size_t size) { ...@@ -149,6 +149,15 @@ ALWAYS_INLINE void* ShimCppNew(size_t size) {
return ptr; return ptr;
} }
ALWAYS_INLINE void* ShimCppNewNoThrow(size_t size) {
void* context = nullptr;
#if defined(OS_APPLE)
context = malloc_default_zone();
#endif
const base::allocator::AllocatorDispatch* const chain_head = GetChainHead();
return chain_head->alloc_unchecked_function(chain_head, size, context);
}
ALWAYS_INLINE void* ShimCppAlignedNew(size_t size, size_t alignment) { ALWAYS_INLINE void* ShimCppAlignedNew(size_t size, size_t alignment) {
const base::allocator::AllocatorDispatch* const chain_head = GetChainHead(); const base::allocator::AllocatorDispatch* const chain_head = GetChainHead();
void* ptr; void* ptr;
......
...@@ -71,12 +71,12 @@ SHIM_ALWAYS_EXPORT void operator delete[](void* p) __THROW { ...@@ -71,12 +71,12 @@ SHIM_ALWAYS_EXPORT void operator delete[](void* p) __THROW {
SHIM_ALWAYS_EXPORT void* operator new(size_t size, SHIM_ALWAYS_EXPORT void* operator new(size_t size,
const std::nothrow_t&) __THROW { const std::nothrow_t&) __THROW {
return ShimCppNew(size); return ShimCppNewNoThrow(size);
} }
SHIM_ALWAYS_EXPORT void* operator new[](size_t size, SHIM_ALWAYS_EXPORT void* operator new[](size_t size,
const std::nothrow_t&) __THROW { const std::nothrow_t&) __THROW {
return ShimCppNew(size); return ShimCppNewNoThrow(size);
} }
SHIM_ALWAYS_EXPORT void operator delete(void* p, const std::nothrow_t&) __THROW { SHIM_ALWAYS_EXPORT void operator delete(void* p, const std::nothrow_t&) __THROW {
......
...@@ -12,6 +12,22 @@ ...@@ -12,6 +12,22 @@
#include <windows.h> #include <windows.h>
#include "base/allocator/allocator_shim_internals.h"
// Even though most C++ allocation operators can be left alone since the
// interception works at a lower level, these ones should be
// overridden. Otherwise they redirect to malloc(), which is configured to crash
// with an OOM in failure cases, such as allocation requests that are too large.
SHIM_ALWAYS_EXPORT void* operator new(size_t size,
const std::nothrow_t&) __THROW {
return ShimCppNewNoThrow(size);
}
SHIM_ALWAYS_EXPORT void* operator new[](size_t size,
const std::nothrow_t&) __THROW {
return ShimCppNewNoThrow(size);
}
extern "C" { extern "C" {
void* (*malloc_unchecked)(size_t) = &base::allocator::UncheckedAlloc; void* (*malloc_unchecked)(size_t) = &base::allocator::UncheckedAlloc;
......
...@@ -550,6 +550,23 @@ TEST_F(AllocatorShimTest, InterceptCppSymbols) { ...@@ -550,6 +550,23 @@ TEST_F(AllocatorShimTest, InterceptCppSymbols) {
RemoveAllocatorDispatchForTesting(&g_mock_dispatch); RemoveAllocatorDispatchForTesting(&g_mock_dispatch);
} }
// PartitionAlloc disallows large allocations to avoid errors with int
// overflows.
#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
struct TooLarge {
char padding1[1UL << 31];
int padding2;
};
TEST_F(AllocatorShimTest, NewNoThrowTooLarge) {
char* too_large_array = new (std::nothrow) char[(1UL << 31) + 100];
EXPECT_EQ(nullptr, too_large_array);
TooLarge* too_large_struct = new (std::nothrow) TooLarge;
EXPECT_EQ(nullptr, too_large_struct);
}
#endif
// This test exercises the case of concurrent OOM failure, which would end up // This test exercises the case of concurrent OOM failure, which would end up
// invoking std::new_handler concurrently. This is to cover the CallNewHandler() // invoking std::new_handler concurrently. This is to cover the CallNewHandler()
// paths of allocator_shim.cc and smoke-test its thread safey. // paths of allocator_shim.cc and smoke-test its thread safey.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment