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