Commit 01a8325a authored by Michael Lippautz's avatar Michael Lippautz Committed by V8 LUCI CQ

[heap] Provide base::Malloc::AllocateAtLeast and use in Worklist

Provides a v8::base::Malloc::AllocateAtLeast() method that is also
UBSan-safe and use it in the GC's worklist.

Depends on https://crrev.com/c/3834601

Bug: v8:13193
Change-Id: I1bd182e613fb3c6a5a6b90bf56f12bd210d5ef8c
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3833818
Commit-Queue: Michael Lippautz <mlippautz@chromium.org>
Reviewed-by: 's avatarAnton Bikineev <bikineev@chromium.org>
Cr-Commit-Position: refs/heads/main@{#82562}
parent d35f3e2b
...@@ -52,6 +52,9 @@ ...@@ -52,6 +52,9 @@
#if V8_OS_DARWIN #if V8_OS_DARWIN
#include <mach/mach.h> #include <mach/mach.h>
#include <malloc/malloc.h>
#else
#include <malloc.h>
#endif #endif
#if V8_OS_LINUX #if V8_OS_LINUX
...@@ -1262,5 +1265,14 @@ Stack::StackSlot Stack::GetCurrentStackPosition() { ...@@ -1262,5 +1265,14 @@ Stack::StackSlot Stack::GetCurrentStackPosition() {
#undef MAP_ANONYMOUS #undef MAP_ANONYMOUS
#undef MADV_FREE #undef MADV_FREE
// static
size_t Malloc::GetUsableSize(void* ptr) {
#if defined(V8_OS_DARWIN)
return malloc_size(ptr);
#else // defined(V8_OS_DARWIN)
return malloc_usable_size(ptr);
#endif // !defined(V8_OS_DARWIN)
}
} // namespace base } // namespace base
} // namespace v8 } // namespace v8
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
// This has to come after windows.h. // This has to come after windows.h.
#include <VersionHelpers.h> #include <VersionHelpers.h>
#include <dbghelp.h> // For SymLoadModule64 and al. #include <dbghelp.h> // For SymLoadModule64 and al.
#include <malloc.h> // For _msize()
#include <mmsystem.h> // For timeGetTime(). #include <mmsystem.h> // For timeGetTime().
#include <tlhelp32.h> // For Module32First and al. #include <tlhelp32.h> // For Module32First and al.
...@@ -1765,5 +1766,8 @@ Stack::StackSlot Stack::GetCurrentStackPosition() { ...@@ -1765,5 +1766,8 @@ Stack::StackSlot Stack::GetCurrentStackPosition() {
#endif #endif
} }
// static
size_t Malloc::GetUsableSize(void* ptr) { return _msize(ptr); }
} // namespace base } // namespace base
} // namespace v8 } // namespace v8
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#include "src/base/base-export.h" #include "src/base/base-export.h"
#include "src/base/build_config.h" #include "src/base/build_config.h"
#include "src/base/compiler-specific.h" #include "src/base/compiler-specific.h"
#include "src/base/macros.h"
#include "src/base/optional.h" #include "src/base/optional.h"
#include "src/base/platform/mutex.h" #include "src/base/platform/mutex.h"
#include "src/base/platform/semaphore.h" #include "src/base/platform/semaphore.h"
...@@ -661,6 +662,42 @@ class V8_BASE_EXPORT Stack { ...@@ -661,6 +662,42 @@ class V8_BASE_EXPORT Stack {
} }
}; };
class V8_BASE_EXPORT Malloc final {
public:
// Returns the usable size in bytes for a `ptr` allocated using `malloc()`.
// Note that the bytes returned may not be generally accessed on e.g. UBSan
// builds. Use `AllocateAtLeast()` for a malloc version that works with UBSan.
static size_t GetUsableSize(void* ptr);
// Mimics C++23 `allocation_result`.
template <class Pointer>
struct AllocationResult {
Pointer ptr;
std::size_t count;
};
// Allocates at least `n * sizeof(T)` uninitialized storage but may allocate
// more which is indicated by the return value. Mimics C++23
// `allocate_ate_least()`.
template <typename T>
V8_NODISCARD static AllocationResult<T*> AllocateAtLeast(std::size_t n) {
const size_t min_wanted_size = n * sizeof(T);
auto* memory = static_cast<T*>(malloc(min_wanted_size));
const size_t usable_size = v8::base::Malloc::GetUsableSize(memory);
#if V8_USE_UNDEFINED_BEHAVIOR_SANITIZER
// UBSan (specifically, -fsanitize=bounds) assumes that any access outside
// of the requested size for malloc is UB and will trap in ud2 instructions.
// This can be worked around by using `realloc()` on the specific memory
// regon, assuming that the allocator doesn't actually reallocate the
// buffer.
if (usable_size != min_wanted_size) {
CHECK_EQ(static_cast<T*>(realloc(memory, usable_size)), memory);
}
#endif // V8_USE_UNDEFINED_BEHAVIOR_SANITIZER
return {memory, usable_size};
}
};
} // namespace base } // namespace base
} // namespace v8 } // namespace v8
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "src/base/atomic-utils.h" #include "src/base/atomic-utils.h"
#include "src/base/logging.h" #include "src/base/logging.h"
#include "src/base/platform/mutex.h" #include "src/base/platform/mutex.h"
#include "src/base/platform/platform.h"
namespace heap::base { namespace heap::base {
namespace internal { namespace internal {
...@@ -220,10 +221,10 @@ class Worklist<EntryType, MinSegmentSize>::Segment final ...@@ -220,10 +221,10 @@ class Worklist<EntryType, MinSegmentSize>::Segment final
: public internal::SegmentBase { : public internal::SegmentBase {
public: public:
static Segment* Create(uint16_t min_segment_size) { static Segment* Create(uint16_t min_segment_size) {
// TODO(v8:13193): Refer to a cross-platform `malloc_usable_size()` to make auto result = v8::base::Malloc::AllocateAtLeast<char>(
// use of all the memory allocated by `malloc()`. MallocSizeForCapacity(min_segment_size));
void* memory = malloc(MallocSizeForCapacity(min_segment_size)); return new (result.ptr)
return new (memory) Segment(min_segment_size); Segment(CapacityForMallocSize(result.count * sizeof(char)));
} }
static void Delete(Segment* segment) { free(segment); } static void Delete(Segment* segment) { free(segment); }
...@@ -243,6 +244,9 @@ class Worklist<EntryType, MinSegmentSize>::Segment final ...@@ -243,6 +244,9 @@ class Worklist<EntryType, MinSegmentSize>::Segment final
static constexpr size_t MallocSizeForCapacity(size_t num_entries) { static constexpr size_t MallocSizeForCapacity(size_t num_entries) {
return sizeof(Segment) + sizeof(EntryType) * num_entries; return sizeof(Segment) + sizeof(EntryType) * num_entries;
} }
static constexpr size_t CapacityForMallocSize(size_t malloc_size) {
return (malloc_size - sizeof(Segment)) / sizeof(EntryType);
}
constexpr explicit Segment(size_t capacity) constexpr explicit Segment(size_t capacity)
: internal::SegmentBase(capacity) {} : internal::SegmentBase(capacity) {}
......
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