1 /*
2 * Copyright (c) 2021 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 "ecmascript/ecma_vm.h"
17 #include "ecmascript/global_env.h"
18 #include "ecmascript/js_handle.h"
19 #include "ecmascript/js_runtime_options.h"
20 #include "ecmascript/log_wrapper.h"
21 #include "ecmascript/mem/concurrent_marker.h"
22 #include "ecmascript/mem/space.h"
23 #include "ecmascript/mem/verification.h"
24 #include "ecmascript/object_factory.h"
25 #include "ecmascript/tagged_array-inl.h"
26 #include "ecmascript/tests/test_helper.h"
27 #include "../runtime_core/common_interfaces/base_runtime.h"
28 #include "common_components/heap/heap_manager.h"
29 #include <csetjmp>
30 #include <csignal>
31 #include <sys/syscall.h>
32 using namespace panda::ecmascript;
33
34 namespace panda::test {
35 class ReadOnlySpaceTest : public BaseTestWithScope<false> {
36 public:
SetUp()37 void SetUp() override
38 {
39 InitializeLogger();
40 TestHelper::CreateEcmaVMWithScope(instance, thread, scope);
41 factory = thread->GetEcmaVM()->GetFactory();
42 const_cast<panda::ecmascript::Heap *>(thread->GetEcmaVM()->GetHeap())->SetMarkType(MarkType::MARK_FULL);
43 }
44
InitializeLogger()45 void InitializeLogger()
46 {
47 panda::ecmascript::JSRuntimeOptions runtimeOptions;
48 runtimeOptions.SetLogLevel("error");
49 common::Log::Initialize(runtimeOptions.GetLogOptions());
50 }
51
52 ObjectFactory *factory {nullptr};
53 };
54
55 static sigjmp_buf g_env;
56 static bool g_segmentfault_flag = false;
57 class ReadOnlyTestManager {
58 public:
59 // static constexpr int RO_SEGMENTFAULT = 1;
ProcessReadOnlySegmentFault(int sig)60 static void ProcessReadOnlySegmentFault(int sig)
61 {
62 g_segmentfault_flag = true;
63 siglongjmp(g_env, sig);
64 }
65
RegisterSignal()66 static int RegisterSignal()
67 {
68 struct sigaction act;
69 act.sa_handler = ProcessReadOnlySegmentFault;
70 sigemptyset(&act.sa_mask);
71 sigaddset(&act.sa_mask, SIGQUIT);
72 act.sa_flags = SA_RESETHAND;
73 return sigaction(SIGSEGV, &act, nullptr);
74 }
75 };
76
ForkBySyscall(void)77 static pid_t ForkBySyscall(void)
78 {
79 #ifdef SYS_fork
80 return syscall(SYS_fork);
81 #else
82 return syscall(SYS_clone, SIGCHLD, 0);
83 #endif
84 }
85
HWTEST_F_L0(ReadOnlySpaceTest,ReadOnlyTest)86 HWTEST_F_L0(ReadOnlySpaceTest, ReadOnlyTest)
87 {
88 auto *heap = const_cast<panda::ecmascript::Heap *>(thread->GetEcmaVM()->GetHeap());
89 if (!g_isEnableCMCGC) {
90 heap->GetReadOnlySpace()->SetReadOnly();
91 } else {
92 auto heapManager = common::BaseRuntime::GetInstance()->GetHeapManager();
93 heapManager.SetReadOnlyToROSpace();
94 }
95 if (ReadOnlyTestManager::RegisterSignal() == -1) {
96 perror("sigaction error");
97 exit(1);
98 }
99 if (!g_isEnableCMCGC) {
100 SharedHeap::GetInstance()->WaitGCFinished(thread);
101 }
102 auto ret = sigsetjmp(g_env, 1);
103 if (ret != SIGSEGV) {
104 heap->AllocateReadOnlyOrHugeObject(
105 JSHClass::Cast(thread->GlobalConstants()->GetBigIntClass().GetTaggedObject()));
106 } else {
107 // catch signal SIGSEGV caused by modify read only memory
108 EXPECT_TRUE(g_segmentfault_flag);
109 }
110 if (g_isEnableCMCGC) {
111 EXPECT_TRUE(g_segmentfault_flag);
112 }
113 }
114
HWTEST_F_L0(ReadOnlySpaceTest,AllocateTest)115 HWTEST_F_L0(ReadOnlySpaceTest, AllocateTest)
116 {
117 auto *heap = const_cast<panda::ecmascript::Heap *>(thread->GetEcmaVM()->GetHeap());
118 auto *object = heap->AllocateReadOnlyOrHugeObject(
119 JSHClass::Cast(thread->GlobalConstants()->GetBigIntClass().GetTaggedObject()));
120 if (!g_isEnableCMCGC) {
121 auto *region = Region::ObjectAddressToRange(object);
122 EXPECT_TRUE(region->InReadOnlySpace());
123 } else {
124 auto heapManager = common::BaseRuntime::GetInstance()->GetHeapManager();
125 EXPECT_TRUE(heapManager.IsInROSpace(object));
126 }
127 }
128
HWTEST_F_L0(ReadOnlySpaceTest,CompactHeapBeforeForkTest)129 HWTEST_F_L0(ReadOnlySpaceTest, CompactHeapBeforeForkTest)
130 {
131 if (!g_isEnableCMCGC) {
132 auto *heap = const_cast<panda::ecmascript::Heap *>(thread->GetEcmaVM()->GetHeap());
133 std::string rawStr = "test string";
134 JSHandle<EcmaString> string = factory->NewFromStdString(rawStr);
135 JSHandle<JSObject> obj = factory->NewEmptyJSObject();
136 auto *regionBefore = Region::ObjectAddressToRange(string.GetObject<TaggedObject>());
137 auto *objRegionBefore = Region::ObjectAddressToRange(obj.GetObject<TaggedObject>());
138 EXPECT_TRUE(regionBefore->InSharedHeap());
139 EXPECT_FALSE(objRegionBefore->InReadOnlySpace());
140 heap->CompactHeapBeforeFork();
141 auto *regionAfter = Region::ObjectAddressToRange(string.GetObject<TaggedObject>());
142 auto *objRegionAfter = Region::ObjectAddressToRange(obj.GetObject<TaggedObject>());
143 EXPECT_TRUE(regionAfter->InSharedHeap());
144 EXPECT_FALSE(objRegionAfter->InReadOnlySpace());
145 } else {
146 EXPECT_TRUE(true);
147 }
148 }
149
HWTEST_F_L0(ReadOnlySpaceTest,GCTest)150 HWTEST_F_L0(ReadOnlySpaceTest, GCTest)
151 {
152 auto *heap = const_cast<panda::ecmascript::Heap *>(thread->GetEcmaVM()->GetHeap());
153 auto *object = heap->AllocateReadOnlyOrHugeObject(
154 JSHClass::Cast(thread->GlobalConstants()->GetBigIntClass().GetTaggedObject()));
155 if (!g_isEnableCMCGC) {
156 heap->CollectGarbage(TriggerGCType::YOUNG_GC);
157 heap->CollectGarbage(TriggerGCType::OLD_GC);
158 heap->CollectGarbage(TriggerGCType::FULL_GC);
159 auto *region = Region::ObjectAddressToRange(object);
160 EXPECT_TRUE(region->InReadOnlySpace());
161 } else {
162 auto baseRuntime = common::BaseRuntime::GetInstance();
163 auto heapManager = baseRuntime->GetHeapManager();
164 baseRuntime->RequestGC(common::GC_REASON_BACKUP, false, common::GC_TYPE_FULL);
165 EXPECT_TRUE(heapManager.IsInROSpace(object));
166 }
167 }
168
HWTEST_F_L0(ReadOnlySpaceTest,ForkTest)169 HWTEST_F_L0(ReadOnlySpaceTest, ForkTest)
170 {
171 auto vm = thread->GetEcmaVM();
172 auto *heap = const_cast<panda::ecmascript::Heap *>(thread->GetEcmaVM()->GetHeap());
173 if (!g_isEnableCMCGC) {
174 std::string rawStr = "fork string";
175 JSHandle<EcmaString> string = factory->NewFromStdString(rawStr);
176 JSNApi::PreFork(vm);
177 if (ForkBySyscall() == 0) {
178 panda::RuntimeOption postOption;
179 JSNApi::PostFork(vm, postOption);
180 // test gc in child process
181 heap->CollectGarbage(TriggerGCType::OLD_GC);
182 auto *region = Region::ObjectAddressToRange(string.GetObject<TaggedObject>());
183 EXPECT_TRUE(region->InSharedHeap());
184 }
185 } else {
186 auto *object = heap->AllocateReadOnlyOrHugeObject(
187 JSHClass::Cast(thread->GlobalConstants()->GetBigIntClass().GetTaggedObject()));
188 auto baseRuntime = common::BaseRuntime::GetInstance();
189 auto heapManager = baseRuntime->GetHeapManager();
190
191 JSNApi::PreFork(vm);
192 if (ForkBySyscall() == 0) {
193 panda::RuntimeOption postOption;
194 JSNApi::PostFork(vm, postOption);
195 // test gc in child process
196 baseRuntime->RequestGC(common::GC_REASON_BACKUP, false, common::GC_TYPE_FULL);
197 EXPECT_TRUE(heapManager.IsInROSpace(object));
198 } else {
199 int status;
200 pid_t ter_pid = wait(&status);
201 if (ter_pid == -1)
202 {
203 exit(1);
204 }
205 }
206 }
207 }
208 } // namespace panda::test
209