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.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
28 #include <csetjmp>
29 #include <csignal>
30 using namespace panda::ecmascript;
31
32 namespace panda::test {
33 class ReadOnlySpaceTest : public testing::Test {
34 public:
SetUpTestCase()35 static void SetUpTestCase()
36 {
37 GTEST_LOG_(INFO) << "SetUpTestCase";
38 }
39
TearDownTestCase()40 static void TearDownTestCase()
41 {
42 GTEST_LOG_(INFO) << "TearDownCase";
43 }
44
SetUp()45 void SetUp() override
46 {
47 InitializeLogger();
48 TestHelper::CreateEcmaVMWithScope(instance, thread, scope);
49 factory = thread->GetEcmaVM()->GetFactory();
50 const_cast<Heap *>(thread->GetEcmaVM()->GetHeap())->SetMarkType(MarkType::MARK_FULL);
51 }
52
InitializeLogger()53 void InitializeLogger()
54 {
55 panda::ecmascript::JSRuntimeOptions runtimeOptions;
56 runtimeOptions.SetLogLevel("error");
57 ecmascript::Log::Initialize(runtimeOptions);
58 }
59
TearDown()60 void TearDown() override
61 {
62 TestHelper::DestroyEcmaVMWithScope(instance, scope);
63 }
64
65 JSThread *thread {nullptr};
66 ObjectFactory *factory {nullptr};
67 EcmaVM *instance {nullptr};
68 ecmascript::EcmaHandleScope *scope {nullptr};
69 };
70
71 static sigjmp_buf g_env;
72 static bool g_segmentfault_flag = false;
73 class ReadOnlyTestManager {
74 public:
75 // static constexpr int RO_SEGMENTFAULT = 1;
ProcessReadOnlySegmentFault(int sig)76 static void ProcessReadOnlySegmentFault(int sig)
77 {
78 g_segmentfault_flag = true;
79 siglongjmp(g_env, sig);
80 }
81
RegisterSignal()82 static int RegisterSignal()
83 {
84 struct sigaction act;
85 act.sa_handler = ProcessReadOnlySegmentFault;
86 sigemptyset(&act.sa_mask);
87 sigaddset(&act.sa_mask, SIGQUIT);
88 act.sa_flags = SA_RESETHAND;
89 return sigaction(SIGSEGV, &act, nullptr);
90 }
91 };
92
HWTEST_F_L0(ReadOnlySpaceTest,ReadOnlyTest)93 HWTEST_F_L0(ReadOnlySpaceTest, ReadOnlyTest)
94 {
95 auto *heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
96 heap->GetReadOnlySpace()->SetReadOnly();
97 if (ReadOnlyTestManager::RegisterSignal() == -1) {
98 perror("sigaction error");
99 exit(1);
100 }
101 auto ret = sigsetjmp(g_env, 1);
102 if (ret != SIGSEGV) {
103 heap->AllocateReadOnlyOrHugeObject(
104 JSHClass::Cast(thread->GlobalConstants()->GetBigIntClass().GetTaggedObject()));
105 } else {
106 // catch signal SIGSEGV caused by modify read only memory
107 EXPECT_TRUE(g_segmentfault_flag);
108 }
109 }
110
HWTEST_F_L0(ReadOnlySpaceTest,AllocateTest)111 HWTEST_F_L0(ReadOnlySpaceTest, AllocateTest)
112 {
113 auto *heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
114 auto *object = heap->AllocateReadOnlyOrHugeObject(
115 JSHClass::Cast(thread->GlobalConstants()->GetBigIntClass().GetTaggedObject()));
116 auto *region = Region::ObjectAddressToRange(object);
117 EXPECT_TRUE(region->InReadOnlySpace());
118 }
119
HWTEST_F_L0(ReadOnlySpaceTest,CompactHeapBeforeForkTest)120 HWTEST_F_L0(ReadOnlySpaceTest, CompactHeapBeforeForkTest)
121 {
122 auto *heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
123 std::string rawStr = "test string";
124 JSHandle<EcmaString> string = factory->NewFromStdString(rawStr);
125 JSHandle<JSObject> obj = factory->NewEmptyJSObject();
126 auto *regionBefore = Region::ObjectAddressToRange(string.GetObject<TaggedObject>());
127 auto *objRegionBefore = Region::ObjectAddressToRange(obj.GetObject<TaggedObject>());
128 EXPECT_FALSE(regionBefore->InReadOnlySpace());
129 EXPECT_FALSE(objRegionBefore->InReadOnlySpace());
130 heap->CompactHeapBeforeFork();
131 auto *regionAfter = Region::ObjectAddressToRange(string.GetObject<TaggedObject>());
132 auto *objRegionAfter = Region::ObjectAddressToRange(obj.GetObject<TaggedObject>());
133 EXPECT_TRUE(regionAfter->InReadOnlySpace());
134 EXPECT_FALSE(objRegionAfter->InReadOnlySpace());
135 }
136
HWTEST_F_L0(ReadOnlySpaceTest,GCTest)137 HWTEST_F_L0(ReadOnlySpaceTest, GCTest)
138 {
139 auto *heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
140 auto *object = heap->AllocateReadOnlyOrHugeObject(
141 JSHClass::Cast(thread->GlobalConstants()->GetBigIntClass().GetTaggedObject()));
142 heap->CollectGarbage(TriggerGCType::YOUNG_GC);
143 heap->CollectGarbage(TriggerGCType::OLD_GC);
144 heap->CollectGarbage(TriggerGCType::FULL_GC);
145 auto *region = Region::ObjectAddressToRange(object);
146 EXPECT_TRUE(region->InReadOnlySpace());
147 }
148
HWTEST_F_L0(ReadOnlySpaceTest,ForkTest)149 HWTEST_F_L0(ReadOnlySpaceTest, ForkTest)
150 {
151 auto vm = thread->GetEcmaVM();
152 auto *heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
153 std::string rawStr = "fork string";
154 JSHandle<EcmaString> string = factory->NewFromStdString(rawStr);
155 JSNApi::PreFork(vm);
156 if (fork() != 0) {
157 // test gc in parent process
158 heap->CollectGarbage(TriggerGCType::OLD_GC);
159 } else {
160 panda::RuntimeOption postOption;
161 JSNApi::PostFork(vm, postOption);
162 // test gc in child process
163 heap->CollectGarbage(TriggerGCType::OLD_GC);
164 auto *region = Region::ObjectAddressToRange(string.GetObject<TaggedObject>());
165 EXPECT_TRUE(region->InReadOnlySpace());
166 }
167 }
168 } // namespace panda::test
169