1 /*
2 * Copyright (c) 2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include <csetjmp>
17 #include <csignal>
18
19 #include "ecmascript/ecma_handle_scope.h"
20 #include "ecmascript/global_env.h"
21 #include "ecmascript/js_tagged_value.h"
22 #include "ecmascript/jspandafile/program_object.h"
23 #include "ecmascript/mem/barriers.h"
24 #include "ecmascript/mem/heap-inl.h"
25 #include "ecmascript/mem/verification.h"
26 #include "ecmascript/napi/include/jsnapi.h"
27 #include "ecmascript/tagged_array.h"
28 #include "ecmascript/tests/test_helper.h"
29 #include "gtest/gtest.h"
30
31 using namespace panda;
32
33 using namespace panda::ecmascript;
34
35 namespace panda::test {
36 class HandleLeakTest : public testing::Test {
37 public:
SetUpTestCase()38 static void SetUpTestCase()
39 {
40 GTEST_LOG_(INFO) << "SetUpTestCase";
41 }
42
TearDownTestCase()43 static void TearDownTestCase()
44 {
45 GTEST_LOG_(INFO) << "TearDownCase";
46 }
47
SetUp()48 void SetUp() override
49 {
50 JSRuntimeOptions options;
51 options.SetEnableForceGC(false);
52 options.SetLogLevel("info");
53 instance = JSNApi::CreateEcmaVM(options);
54 ASSERT_TRUE(instance != nullptr) << "Cannot create EcmaVM";
55 thread = instance->GetJSThread();
56 scope = new EcmaHandleScope(thread);
57 }
58
TearDown()59 void TearDown() override
60 {
61 TestHelper::DestroyEcmaVMWithScope(instance, scope);
62 }
63
64 EcmaVM *instance {nullptr};
65 ecmascript::EcmaHandleScope *scope {nullptr};
66 JSThread *thread {nullptr};
67 };
68
69 static sigjmp_buf env;
70 static bool segmentFaultFlag = false;
71 class HandleLeakTestManager {
72 public:
ProcessHandleLeakSegmentFault(int sig)73 static void ProcessHandleLeakSegmentFault(int sig)
74 {
75 segmentFaultFlag = true;
76 siglongjmp(env, sig);
77 }
78
RegisterSignal()79 static int RegisterSignal()
80 {
81 segmentFaultFlag = false;
82 struct sigaction act;
83 act.sa_handler = ProcessHandleLeakSegmentFault;
84 sigemptyset(&act.sa_mask);
85 sigaddset(&act.sa_mask, SIGQUIT);
86 act.sa_flags = SA_RESETHAND;
87 return sigaction(SIGSEGV, &act, nullptr);
88 }
89 };
90
HWTEST_F_L0(HandleLeakTest,HandleLeakCheck)91 HWTEST_F_L0(HandleLeakTest, HandleLeakCheck)
92 {
93 EcmaHandleScope scope(thread);
94 std::vector<Global<ArrayRef>> result;
95 for (int i = 0; i < 150000; i++) {
96 result.emplace_back(Global<ArrayRef>(instance, ArrayRef::New(instance, 100)));
97 }
98 }
99
HWTEST_F_L0(HandleLeakTest,UnInitializeCheckOneProperty)100 HWTEST_F_L0(HandleLeakTest, UnInitializeCheckOneProperty)
101 {
102 EcmaHandleScope scope(thread);
103 JSHandle<TaggedObject> newProgram(thread, const_cast<Heap *>(instance->GetHeap())->AllocateYoungOrHugeObject(
104 JSHClass::Cast(thread->GlobalConstants()->GetProgramClass().GetTaggedObject())));
105
106 if (HandleLeakTestManager::RegisterSignal() == -1) {
107 perror("sigaction error");
108 exit(1);
109 }
110 size_t failCount = 0;
111 auto ret = sigsetjmp(env, 1);
112 if (ret != SIGSEGV) {
113 VerifyObjectVisitor verifier(instance->GetHeap(), &failCount);
114 verifier(*newProgram);
115 ASSERT_TRUE(false);
116 } else {
117 // catch signal SIGSEGV caused by uninitialize
118 EXPECT_TRUE(segmentFaultFlag);
119 ASSERT_TRUE(failCount == 0);
120 }
121 }
122
HWTEST_F_L0(HandleLeakTest,InitializeCheckOneProperty)123 HWTEST_F_L0(HandleLeakTest, InitializeCheckOneProperty)
124 {
125 EcmaHandleScope scope(thread);
126 JSHandle<Program> newProgram(thread, const_cast<Heap *>(instance->GetHeap())->AllocateYoungOrHugeObject(
127 JSHClass::Cast(thread->GlobalConstants()->GetProgramClass().GetTaggedObject())));
128
129 newProgram->SetMainFunction(thread, JSTaggedValue::Undefined());
130
131 size_t failCount = 0;
132 VerifyObjectVisitor verifier(instance->GetHeap(), &failCount);
133 verifier(*newProgram);
134 ASSERT_TRUE(newProgram.GetTaggedValue().IsProgram());
135 ASSERT_TRUE(failCount == 0);
136 }
137
HWTEST_F_L0(HandleLeakTest,UnInitializeCheckMoreProperty)138 HWTEST_F_L0(HandleLeakTest, UnInitializeCheckMoreProperty)
139 {
140 EcmaHandleScope scope(thread);
141 JSHandle<JSHClass> arrayClass(thread->GlobalConstants()->GetHandledArrayClass());
142 static constexpr int SIZE = 100;
143 JSHandle<TaggedArray> newArray(thread, const_cast<Heap *>(instance->GetHeap())->AllocateNonMovableOrHugeObject(
144 *arrayClass, TaggedArray::ComputeSize(JSTaggedValue::TaggedTypeSize(), SIZE)));
145 newArray->SetLength(SIZE);
146
147 if (HandleLeakTestManager::RegisterSignal() == -1) {
148 perror("sigaction error");
149 exit(1);
150 }
151 size_t failCount = 0;
152 auto ret = sigsetjmp(env, 1);
153 if (ret != SIGSEGV) {
154 VerifyObjectVisitor verifier(instance->GetHeap(), &failCount);
155 verifier(*newArray);
156 ASSERT_TRUE(false);
157 } else {
158 // catch signal SIGSEGV caused by uninitialize
159 EXPECT_TRUE(segmentFaultFlag);
160 ASSERT_TRUE(failCount == 0);
161 }
162 }
163
HWTEST_F_L0(HandleLeakTest,PartInitializeCheckMoreProperty)164 HWTEST_F_L0(HandleLeakTest, PartInitializeCheckMoreProperty)
165 {
166 EcmaHandleScope scope(thread);
167 JSHandle<JSHClass> arrayClass(thread->GlobalConstants()->GetHandledArrayClass());
168 static constexpr int SIZE = 100;
169 JSHandle<TaggedArray> newArray(thread, const_cast<Heap *>(instance->GetHeap())->AllocateNonMovableOrHugeObject(
170 *arrayClass, TaggedArray::ComputeSize(JSTaggedValue::TaggedTypeSize(), SIZE)));
171 newArray->SetLength(SIZE);
172 for (uint32_t i = 0; i < SIZE / 2; i++) {
173 size_t offset = JSTaggedValue::TaggedTypeSize() * i;
174 ecmascript::Barriers::SetPrimitive(newArray->GetData(), offset, JSTaggedValue::Undefined());
175 }
176
177 if (HandleLeakTestManager::RegisterSignal() == -1) {
178 perror("sigaction error");
179 exit(1);
180 }
181 size_t failCount = 0;
182 auto ret = sigsetjmp(env, 1);
183 if (ret != SIGSEGV) {
184 VerifyObjectVisitor verifier(instance->GetHeap(), &failCount);
185 verifier(*newArray);
186 ASSERT_TRUE(false);
187 } else {
188 // catch signal SIGSEGV caused by partinitialize
189 EXPECT_TRUE(segmentFaultFlag);
190 ASSERT_TRUE(failCount == 0);
191 }
192 }
193
HWTEST_F_L0(HandleLeakTest,InitializeCheckMoreProperty)194 HWTEST_F_L0(HandleLeakTest, InitializeCheckMoreProperty)
195 {
196 EcmaHandleScope scope(thread);
197 JSHandle<JSHClass> arrayClass(thread->GlobalConstants()->GetHandledArrayClass());
198 static constexpr int SIZE = 100;
199 JSHandle<TaggedArray> newArray(thread, const_cast<Heap *>(instance->GetHeap())->AllocateNonMovableOrHugeObject(
200 *arrayClass, TaggedArray::ComputeSize(JSTaggedValue::TaggedTypeSize(), SIZE)));
201
202 newArray->InitializeWithSpecialValue(JSTaggedValue::Hole(), SIZE);
203 size_t failCount = 0;
204 VerifyObjectVisitor verifier(instance->GetHeap(), &failCount);
205 verifier(*newArray);
206 ASSERT_TRUE(newArray.GetTaggedValue().IsTaggedArray());
207 ASSERT_TRUE(failCount == 0);
208 }
209 } // namespace panda::test
210