1 /*
2 * Copyright (C) 2021-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 #include <gtest/gtest.h>
16 #include <cinttypes>
17 #include <climits>
18 #include <cstdio>
19 #include <unistd.h>
20 #include <string>
21 #include <sys/time.h>
22
23 #define SIZE_ALIGN (4 * sizeof(size_t))
24 #define THRESHOLD (0x1c00 * SIZE_ALIGN)
25 #define ITER_TIME 20
26 const int MALLOC_SIZE_ONE = 1024;
27 const int MALLOC_SIZE_TWO = 2048;
28
handler(int s)29 static void handler(int s)
30 {
31 }
32
33 using namespace testing::ext;
34 using namespace std;
35
36 class MallocSafeUnlink : public testing::Test {
37 public:
38
39 static void SetUpTestCase();
40 static void TearDownTestCase();
41 void SetUp();
42 void TearDown();
43 private:
44 };
45
SetUp()46 void MallocSafeUnlink::SetUp()
47 {
48 }
TearDown()49 void MallocSafeUnlink::TearDown()
50 {
51 }
SetUpTestCase()52 void MallocSafeUnlink::SetUpTestCase()
53 {
54 }
TearDownTestCase()55 void MallocSafeUnlink::TearDownTestCase()
56 {
57 }
58
59 volatile void *g_ptr;
60
set_devide_chunk(int size)61 int set_devide_chunk(int size)
62 {
63 if (!(g_ptr = malloc(size))) {
64 printf("Malloc failed: %s\n", strerror(errno));
65 return -1;
66 }
67 return 0;
68 }
69
child(void)70 static int child(void)
71 {
72 int *c;
73 int *d;
74
75 /* Set first dividing chunk */
76 if (set_devide_chunk(MALLOC_SIZE_TWO)) {
77 return -1;
78 }
79
80 /*
81 * The init procedure makes the freelist unpredictable. To make sure trigger the safe-unlink
82 * check, Here we create as many chunks as possible to make sure there are enough chunks in
83 * bin[0] and malloc again. Basically this is heap spray.
84 */
85 for (int i = 0; i < 512; ++i) {
86
87 c = (int *)malloc(MALLOC_SIZE_ONE);
88 if (!c) {
89 printf("Malloc failed: %s\n", strerror(errno));
90 return -1;
91 }
92 if (set_devide_chunk(MALLOC_SIZE_TWO)) {
93 return -1;
94 }
95 d = (int *)malloc(MALLOC_SIZE_ONE);
96 if (!d) {
97 printf("Malloc failed: %s\n", strerror(errno));
98 return -1;
99 }
100 if (set_devide_chunk(MALLOC_SIZE_TWO)) {
101 return -1;
102 }
103 free(d);
104 free(c);
105 /* exchange the prev and next pointer */
106 uintptr_t temp = c[0];
107 c[0] = c[1];
108 c[1] = temp;
109 }
110
111 return 0;
112 }
113
start_child(void)114 static pid_t start_child(void)
115 {
116 pid_t pid;
117 int ret;
118 pid = fork();
119 if (pid == 0) {
120 ret = child();
121 printf("child process normally out with %d\n", ret);
122 return ret;
123 }
124 return pid;
125 }
126 /*
127 * @tc.number CAM_FREE_FUN_0100
128 * @tc.name Apply part of the memory camfree reduction
129 * @tc.desc Test the basic features of CamFree
130 */
131 HWTEST_F(MallocSafeUnlink, safeUnlinkTest0100, Function | MediumTest | Level1)
132 {
133 sigset_t set;
134 int status;
135 pid_t pid;
136 int flag = 0;
137 struct timespec time1 = {5, 0};
138
139 sigemptyset(&set);
140 sigaddset(&set, SIGCHLD);
141 sigprocmask(SIG_BLOCK, &set, 0);
142 signal(SIGCHLD, handler);
143
144 pid = start_child();
145 if (pid == -1) {
146 printf("fork failed: %s\n", strerror(errno));
147 }
148 if (sigtimedwait(&set, 0, &time1) == -1) { /* Wait for 5 seconds */
149 if (errno == EAGAIN) {
150 flag = 1;
151 } else {
152 printf("sigtimedwait failed: %s\n", strerror(errno));
153 }
154 if (kill(pid, SIGKILL) == -1) {
155 printf("kill failed: %s\n", strerror(errno));
156 }
157 }
158
159 if (waitpid(pid, &status, 0) != pid) {
160 printf("waitpid failed: %s\n", strerror(errno));
161 }
162
163 if (flag) {
164 printf("Child process time out\n");
165 }
166
167 if (WIFSIGNALED(status)) {
168 ASSERT_TRUE(WTERMSIG(status) == SIGSEGV || WTERMSIG(status) == SIGILL) << "child process out with %s\n" <<
169 WTERMSIG(status);
170 } else {
171 ASSERT_TRUE(false) << "child process finished normally\n";
172 }
173 }