1 /*
2 * Copyright (c) 2023 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 <fcntl.h>
17 #include <gtest/gtest.h>
18 #include <sys/socket.h>
19 #include <sys/un.h>
20 #include <unistd.h>
21
22 #include "dfx_define.h"
23 #include "dfx_test_util.h"
24 #include "faultloggerd_client.h"
25 #include "faultloggerd_socket.h"
26
27 #if defined(HAS_LIB_SELINUX)
28 #include <selinux/selinux.h>
29 #endif
30
31 using namespace testing;
32 using namespace testing::ext;
33
34 namespace OHOS {
35 namespace HiviewDFX {
36 class FaultloggerdClientTest : public testing::Test {
37 public:
38 static void SetUpTestCase();
39 static void TearDownTestCase();
40 void SetUp();
41 void TearDown();
42 };
43
SetUpTestCase()44 void FaultloggerdClientTest::SetUpTestCase()
45 {
46 }
47
TearDownTestCase()48 void FaultloggerdClientTest::TearDownTestCase()
49 {
50 }
51
SetUp()52 void FaultloggerdClientTest::SetUp()
53 {
54 }
55
TearDown()56 void FaultloggerdClientTest::TearDown()
57 {
58 }
59
IsSelinuxEnforced()60 bool IsSelinuxEnforced()
61 {
62 std::string cmd = "getenforce";
63 std::string selinuxStatus = ExecuteCommands(cmd);
64 GTEST_LOG_(INFO) << "getenforce return:" << selinuxStatus;
65 return (selinuxStatus.find("Enforcing") != std::string::npos) ? true : false;
66 }
67
68 /**
69 * @tc.name: FaultloggerdClientTest001
70 * @tc.desc: request a file descriptor for logging crash
71 * @tc.type: FUNC
72 */
73 HWTEST_F(FaultloggerdClientTest, FaultloggerdClientTest001, TestSize.Level2)
74 {
75 GTEST_LOG_(INFO) << "FaultloggerdClientTest001: start.";
76 int32_t fd = RequestFileDescriptor(FaultLoggerType::CPP_CRASH);
77 ASSERT_GT(fd, 0);
78 close(fd);
79
80 fd = RequestFileDescriptor(FaultLoggerType::JS_HEAP_SNAPSHOT);
81 ASSERT_GT(fd, 0);
82 close(fd);
83
84 fd = RequestFileDescriptor(FaultLoggerType::JS_RAW_SNAPSHOT);
85 ASSERT_GT(fd, 0);
86 close(fd);
87 GTEST_LOG_(INFO) << "FaultloggerdClientTest001: end.";
88 }
89
90 /**
91 * @tc.name: FaultloggerdClientTest002
92 * @tc.desc: request a file descriptor for logging with app uid
93 * @tc.type: FUNC
94 */
95 HWTEST_F(FaultloggerdClientTest, FaultloggerdClientTest002, TestSize.Level2)
96 {
97 GTEST_LOG_(INFO) << "FaultloggerdClientTest002: start.";
98 int32_t pid = fork();
99 if (pid == 0) {
100 int ret = 0;
101 constexpr int32_t appUid = 100068;
102 setuid(appUid);
103 do {
104 int32_t fd = RequestFileDescriptor(FaultLoggerType::CPP_CRASH);
105 if (fd < 0) {
106 ret = -1;
107 break;
108 }
109 close(fd);
110
111 fd = RequestFileDescriptor(FaultLoggerType::JS_HEAP_SNAPSHOT);
112 if (fd < 0) {
113 ret = -1;
114 break;
115 }
116 close(fd);
117
118 fd = RequestFileDescriptor(FaultLoggerType::JS_RAW_SNAPSHOT);
119 if (fd < 0) {
120 ret = -1;
121 break;
122 }
123 close(fd);
124 } while (false);
125 exit(ret);
126 } else if (pid > 0) {
127 int status;
128 if (waitpid(pid, &status, 0) == -1) {
129 return;
130 }
131
132 int exitCode = -1;
133 if (WIFEXITED(status)) {
134 exitCode = WEXITSTATUS(status);
135 printf("Exit status was %d\n", exitCode);
136 }
137 ASSERT_EQ(exitCode, 0);
138 }
139 GTEST_LOG_(INFO) << "FaultloggerdClientTest002: end.";
140 }
141
142 /**
143 * @tc.name: FaultloggerdClientTest003
144 * @tc.desc: request a file descriptor for logging with inconsistent pid
145 * @tc.type: FUNC
146 */
147 HWTEST_F(FaultloggerdClientTest, FaultloggerdClientTest003, TestSize.Level2)
148 {
149 GTEST_LOG_(INFO) << "FaultloggerdClientTest003: start.";
150 int32_t pid = fork();
151 if (pid == 0) {
152 int ret = 1;
153 constexpr int32_t appUid = 100068;
154 setuid(appUid);
155 FaultLoggerdRequest request;
156 request.type = FaultLoggerType::JS_HEAP_SNAPSHOT;
157 request.pid = appUid;
158 int32_t fd = RequestFileDescriptorEx(&request);
159 if (fd >= 0) {
160 close(fd);
161 ret = 0;
162 }
163 exit(ret);
164 } else if (pid > 0) {
165 int status;
166 if (waitpid(pid, &status, 0) == -1) {
167 return;
168 }
169
170 int exitCode = -1;
171 if (WIFEXITED(status)) {
172 exitCode = WEXITSTATUS(status);
173 printf("Exit status was %d\n", exitCode);
174 }
175 ASSERT_EQ(exitCode, 1);
176 }
177 GTEST_LOG_(INFO) << "FaultloggerdClientTest003: end.";
178 }
179 #if defined(HAS_LIB_SELINUX)
180 /**
181 * @tc.name: FaultloggerdClientTest004
182 * @tc.desc: request a file descriptor for logging with inconsistent pid but in processdump scontext
183 * @tc.type: FUNC
184 */
185 HWTEST_F(FaultloggerdClientTest, FaultloggerdClientTest004, TestSize.Level2)
186 {
187 GTEST_LOG_(INFO) << "FaultloggerdClientTest004: start.";
188
189 // If selinux is not opened, skip this test item
190 if (!IsSelinuxEnforced()) {
191 GTEST_LOG_(INFO) << "Selinux is not opened, skip FaultloggerdClientTest004";
192 return;
193 }
194 int32_t pid = fork();
195 if (pid == 0) {
196 int ret = 1;
197 constexpr int32_t appUid = 100068;
198 setcon("u:r:processdump:s0");
199 setuid(appUid);
200 FaultLoggerdRequest request;
201 request.type = FaultLoggerType::JS_HEAP_SNAPSHOT;
202 request.pid = appUid;
203 int32_t fd = RequestFileDescriptorEx(&request);
204 if (fd >= 0) {
205 close(fd);
206 ret = 0;
207 }
208 exit(ret);
209 } else if (pid > 0) {
210 int status;
211 if (waitpid(pid, &status, 0) == -1) {
212 return;
213 }
214
215 int exitCode = -1;
216 if (WIFEXITED(status)) {
217 exitCode = WEXITSTATUS(status);
218 printf("Exit status was %d\n", exitCode);
219 }
220 ASSERT_EQ(exitCode, 0);
221 }
222 GTEST_LOG_(INFO) << "FaultloggerdClientTest004: end.";
223 }
224
225 /**
226 * @tc.name: FaultloggerdClientTest005
227 * @tc.desc: sdkdump request which selinux label belongs to { hiview hidumper foundation }
228 * @tc.type: FUNC
229 */
230 HWTEST_F(FaultloggerdClientTest, FaultloggerdClientTest005, TestSize.Level2)
231 {
232 GTEST_LOG_(INFO) << "FaultloggerdClientTest005: start.";
233
234 // If selinux is not opened, skip this test item
235 if (!IsSelinuxEnforced()) {
236 GTEST_LOG_(INFO) << "Selinux is not opened, skip FaultloggerdClientTest005";
237 return;
238 }
239 int32_t pid = fork();
240 if (pid == 0) {
241 int ret = 1;
242 constexpr int32_t appUid = 100068;
243 setcon("u:r:hiview:s0");
244 setuid(appUid);
245 int resp = RequestSdkDump(1, 1);
246 if (resp == FaultLoggerCheckPermissionResp::CHECK_PERMISSION_PASS) {
247 ret = 0;
248 }
249 exit(ret);
250 } else if (pid > 0) {
251 int status;
252 if (waitpid(pid, &status, 0) == -1) {
253 return;
254 }
255
256 int exitCode = -1;
257 if (WIFEXITED(status)) {
258 exitCode = WEXITSTATUS(status);
259 printf("Exit status was %d\n", exitCode);
260 }
261 ASSERT_EQ(exitCode, 0);
262 }
263 GTEST_LOG_(INFO) << "FaultloggerdClientTest005: end.";
264 }
265
266 /**
267 * @tc.name: FaultloggerdClientTest006
268 * @tc.desc: sdkdump request which selinux label doesn't belongs to { hiview hidumper foundation }
269 * @tc.type: FUNC
270 */
271 HWTEST_F(FaultloggerdClientTest, FaultloggerdClientTest006, TestSize.Level2)
272 {
273 GTEST_LOG_(INFO) << "FaultloggerdClientTest006: start.";
274
275 // If selinux is not opened, skip this test item
276 if (!IsSelinuxEnforced()) {
277 GTEST_LOG_(INFO) << "Selinux is not opened, skip FaultloggerdClientTest006";
278 return;
279 }
280 int32_t pid = fork();
281 if (pid == 0) {
282 int ret = 1;
283 constexpr int32_t appUid = 100068;
284 setcon("u:r:wifi_host:s0");
285 setuid(appUid);
286 int resp = RequestSdkDump(1, 1);
287 if (resp == FaultLoggerCheckPermissionResp::CHECK_PERMISSION_REJECT) {
288 ret = 0;
289 }
290 exit(ret);
291 } else if (pid > 0) {
292 int status;
293 if (waitpid(pid, &status, 0) == -1) {
294 return;
295 }
296
297 int exitCode = -1;
298 if (WIFEXITED(status)) {
299 exitCode = WEXITSTATUS(status);
300 printf("Exit status was %d\n", exitCode);
301 }
302 ASSERT_EQ(exitCode, 0);
303 }
304 GTEST_LOG_(INFO) << "FaultloggerdClientTest006: end.";
305 }
306 #endif
307
DoClientProcess(const std::string & socketFileName)308 void DoClientProcess(const std::string& socketFileName)
309 {
310 // wait 2 seconds, waiting for the service to be ready
311 sleep(2);
312 int clientSocketFd = -1;
313
314 // socket connect time out 10 second
315 bool retBool = StartConnect(clientSocketFd, socketFileName.c_str(), 10);
316 ASSERT_TRUE(retBool);
317 ASSERT_NE(clientSocketFd, -1);
318 GTEST_LOG_(INFO) << "child connect finished, client fd:" << clientSocketFd;
319
320 int data = 12345; // 12345 is for server Cred test
321 retBool = SendMsgIovToSocket(clientSocketFd, reinterpret_cast<void *>(&data), sizeof(data));
322 ASSERT_TRUE(retBool);
323
324 GTEST_LOG_(INFO) << "Start read file desc";
325 int testFd = ReadFileDescriptorFromSocket(clientSocketFd);
326 GTEST_LOG_(INFO) << "recv testFd:" << testFd;
327 ASSERT_NE(testFd, -1);
328 close(clientSocketFd);
329 close(testFd);
330 }
331
DoServerProcess(const std::string & socketFileName)332 void DoServerProcess(const std::string& socketFileName)
333 {
334 GTEST_LOG_(INFO) << "server prepare listen";
335 int32_t serverSocketFd = -1;
336 std::string testFileName = "/data/test.txt";
337
338 // 5: means max connection count is 5
339 bool ret = StartListen(serverSocketFd, socketFileName.c_str(), 5);
340 ASSERT_TRUE(ret);
341 ASSERT_NE(serverSocketFd, -1);
342 GTEST_LOG_(INFO) << "server start listen fd:" << serverSocketFd;
343
344 struct timeval timev = {
345 20, // recv timeout 20 seconds
346 0
347 };
348 void* pTimev = &timev;
349 int retOpt = OHOS_TEMP_FAILURE_RETRY(setsockopt(serverSocketFd, SOL_SOCKET, SO_RCVTIMEO,
350 static_cast<const char*>(pTimev), sizeof(struct timeval)));
351 ASSERT_NE(retOpt, -1);
352
353 struct sockaddr_un clientAddr;
354 socklen_t clientAddrSize = static_cast<socklen_t>(sizeof(clientAddr));
355 int32_t connectionFd = OHOS_TEMP_FAILURE_RETRY(accept(serverSocketFd,
356 reinterpret_cast<struct sockaddr *>(&clientAddr), &clientAddrSize));
357 ASSERT_GT(connectionFd, 0);
358 GTEST_LOG_(INFO) << "server accept fd:" << connectionFd;
359
360 int optval = 1;
361 retOpt = setsockopt(connectionFd, SOL_SOCKET, SO_PASSCRED, &optval, sizeof(optval));
362 ASSERT_NE(retOpt, -1);
363
364 struct ucred rcred;
365 bool retCred = RecvMsgCredFromSocket(connectionFd, &rcred);
366 ASSERT_TRUE(retCred);
367 GTEST_LOG_(INFO) << "uid:" << rcred.uid;
368
369 // 0666 for file read and write
370 int testFdServer = open(testFileName.c_str(), O_RDWR | O_CREAT, 0666);
371 GTEST_LOG_(INFO) << "Start SendFileDescriptorToSocket";
372 SendFileDescriptorToSocket(connectionFd, testFdServer);
373 GTEST_LOG_(INFO) << "Close server connect fd";
374 close(connectionFd);
375 close(testFdServer);
376 close(serverSocketFd);
377 unlink(testFileName.c_str());
378 }
379
380 /**
381 * @tc.name: FaultloggerdSocketTest001
382 * @tc.desc: test StartListen, RecvMsgCredFromSocket and SendMsgCtlToSocket
383 * @tc.type: FUNC
384 */
385 HWTEST_F(FaultloggerdClientTest, FaultloggerdSocketTest001, TestSize.Level2)
386 {
387 GTEST_LOG_(INFO) << "FaultloggerdSocketTest001: start.";
388 std::string testSocketName = "faultloggerd.server.test";
389
390 int32_t pid = fork();
391 if (pid == 0) {
392 DoClientProcess(testSocketName);
393 GTEST_LOG_(INFO) << "client exit";
394 exit(0);
395 } else if (pid > 0) {
396 DoServerProcess(testSocketName);
397
398 int status;
399 if (waitpid(pid, &status, 0) == -1) {
400 return;
401 }
402
403 int exitCode = -1;
404 if (WIFEXITED(status)) {
405 exitCode = WEXITSTATUS(status);
406 GTEST_LOG_(INFO) << "Exit status was " << exitCode;
407 }
408 ASSERT_EQ(exitCode, 0);
409 }
410 GTEST_LOG_(INFO) << "FaultloggerdSocketTest001: end.";
411 }
412 } // namespace HiviewDFX
413 } // namepsace OHOS
414