Line data Source code
1 : // Copyright 2017 the V8 project authors. All rights reserved.
2 : // Use of this source code is governed by a BSD-style license that can be
3 : // found in the LICENSE file.
4 : #include <stdlib.h>
5 : #include <string.h>
6 :
7 : #if V8_OS_POSIX
8 : #include <setjmp.h>
9 : #include <signal.h>
10 : #include <unistd.h> // NOLINT
11 : #endif
12 :
13 : #include "src/v8.h"
14 :
15 : #include "test/cctest/cctest.h"
16 :
17 : using v8::internal::AccountingAllocator;
18 :
19 : using v8::IdleTask;
20 : using v8::Isolate;
21 : using v8::Task;
22 :
23 : #include "src/allocation.h"
24 : #include "src/zone/accounting-allocator.h"
25 :
26 : // ASAN isn't configured to return nullptr, so skip all of these tests.
27 : #if !defined(V8_USE_ADDRESS_SANITIZER) && !defined(MEMORY_SANITIZER) && \
28 : !defined(THREAD_SANITIZER)
29 :
30 : namespace {
31 :
32 : // Implementation of v8::Platform that can register OOM callbacks.
33 : class AllocationPlatform : public TestPlatform {
34 : public:
35 60 : AllocationPlatform() {
36 30 : current_platform = this;
37 : // Now that it's completely constructed, make this the current platform.
38 30 : i::V8::SetPlatformForTesting(this);
39 30 : }
40 15 : ~AllocationPlatform() override = default;
41 :
42 5 : void OnCriticalMemoryPressure() override { oom_callback_called = true; }
43 :
44 50 : bool OnCriticalMemoryPressure(size_t length) override {
45 50 : oom_callback_called = true;
46 50 : return true;
47 : }
48 :
49 : static AllocationPlatform* current_platform;
50 : bool oom_callback_called = false;
51 : };
52 :
53 : AllocationPlatform* AllocationPlatform::current_platform = nullptr;
54 :
55 : bool DidCallOnCriticalMemoryPressure() {
56 15 : return AllocationPlatform::current_platform &&
57 : AllocationPlatform::current_platform->oom_callback_called;
58 : }
59 :
60 : // No OS should be able to malloc/new this number of bytes. Generate enough
61 : // random values in the address space to get a very large fraction of it. Using
62 : // even larger values is that overflow from rounding or padding can cause the
63 : // allocations to succeed somehow.
64 30 : size_t GetHugeMemoryAmount() {
65 : static size_t huge_memory = 0;
66 30 : if (!huge_memory) {
67 3000 : for (int i = 0; i < 100; i++) {
68 6000 : huge_memory |= bit_cast<size_t>(v8::internal::GetRandomMmapAddr());
69 : }
70 : // Make it larger than the available address space.
71 30 : huge_memory *= 2;
72 30 : CHECK_NE(0, huge_memory);
73 : }
74 30 : return huge_memory;
75 : }
76 :
77 5 : void OnMallocedOperatorNewOOM(const char* location, const char* message) {
78 : // exit(0) if the OOM callback was called and location matches expectation.
79 5 : if (DidCallOnCriticalMemoryPressure())
80 5 : exit(strcmp(location, "Malloced operator new"));
81 0 : exit(1);
82 : }
83 :
84 5 : void OnNewArrayOOM(const char* location, const char* message) {
85 : // exit(0) if the OOM callback was called and location matches expectation.
86 5 : if (DidCallOnCriticalMemoryPressure()) exit(strcmp(location, "NewArray"));
87 0 : exit(1);
88 : }
89 :
90 5 : void OnAlignedAllocOOM(const char* location, const char* message) {
91 : // exit(0) if the OOM callback was called and location matches expectation.
92 5 : if (DidCallOnCriticalMemoryPressure()) exit(strcmp(location, "AlignedAlloc"));
93 0 : exit(1);
94 : }
95 :
96 : } // namespace
97 :
98 25880 : TEST(AccountingAllocatorOOM) {
99 5 : AllocationPlatform platform;
100 10 : v8::internal::AccountingAllocator allocator;
101 5 : CHECK(!platform.oom_callback_called);
102 5 : v8::internal::Segment* result = allocator.GetSegment(GetHugeMemoryAmount());
103 : // On a few systems, allocation somehow succeeds.
104 5 : CHECK_EQ(result == nullptr, platform.oom_callback_called);
105 5 : }
106 :
107 25880 : TEST(MallocedOperatorNewOOM) {
108 5 : AllocationPlatform platform;
109 5 : CHECK(!platform.oom_callback_called);
110 5 : CcTest::isolate()->SetFatalErrorHandler(OnMallocedOperatorNewOOM);
111 : // On failure, this won't return, since a Malloced::New failure is fatal.
112 : // In that case, behavior is checked in OnMallocedOperatorNewOOM before exit.
113 5 : void* result = v8::internal::Malloced::New(GetHugeMemoryAmount());
114 : // On a few systems, allocation somehow succeeds.
115 0 : CHECK_EQ(result == nullptr, platform.oom_callback_called);
116 0 : }
117 :
118 25880 : TEST(NewArrayOOM) {
119 5 : AllocationPlatform platform;
120 5 : CHECK(!platform.oom_callback_called);
121 5 : CcTest::isolate()->SetFatalErrorHandler(OnNewArrayOOM);
122 : // On failure, this won't return, since a NewArray failure is fatal.
123 : // In that case, behavior is checked in OnNewArrayOOM before exit.
124 5 : int8_t* result = v8::internal::NewArray<int8_t>(GetHugeMemoryAmount());
125 : // On a few systems, allocation somehow succeeds.
126 0 : CHECK_EQ(result == nullptr, platform.oom_callback_called);
127 0 : }
128 :
129 25880 : TEST(AlignedAllocOOM) {
130 5 : AllocationPlatform platform;
131 5 : CHECK(!platform.oom_callback_called);
132 5 : CcTest::isolate()->SetFatalErrorHandler(OnAlignedAllocOOM);
133 : // On failure, this won't return, since an AlignedAlloc failure is fatal.
134 : // In that case, behavior is checked in OnAlignedAllocOOM before exit.
135 : void* result = v8::internal::AlignedAlloc(GetHugeMemoryAmount(),
136 5 : v8::internal::AllocatePageSize());
137 : // On a few systems, allocation somehow succeeds.
138 0 : CHECK_EQ(result == nullptr, platform.oom_callback_called);
139 0 : }
140 :
141 25880 : TEST(AllocVirtualMemoryOOM) {
142 5 : AllocationPlatform platform;
143 5 : CHECK(!platform.oom_callback_called);
144 : v8::internal::VirtualMemory result(v8::internal::GetPlatformPageAllocator(),
145 10 : GetHugeMemoryAmount(), nullptr);
146 : // On a few systems, allocation somehow succeeds.
147 5 : CHECK_IMPLIES(!result.IsReserved(), platform.oom_callback_called);
148 5 : }
149 :
150 25880 : TEST(AlignedAllocVirtualMemoryOOM) {
151 5 : AllocationPlatform platform;
152 5 : CHECK(!platform.oom_callback_called);
153 : v8::internal::VirtualMemory result(v8::internal::GetPlatformPageAllocator(),
154 : GetHugeMemoryAmount(), nullptr,
155 10 : v8::internal::AllocatePageSize());
156 : // On a few systems, allocation somehow succeeds.
157 5 : CHECK_IMPLIES(!result.IsReserved(), platform.oom_callback_called);
158 77630 : }
159 :
160 : #endif // !defined(V8_USE_ADDRESS_SANITIZER) && !defined(MEMORY_SANITIZER) &&
161 : // !defined(THREAD_SANITIZER)
|