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 */
HWTEST_F(FaultloggerdClientTest, FaultloggerdClientTest001, TestSize.Level2)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 */
HWTEST_F(FaultloggerdClientTest, FaultloggerdClientTest002, TestSize.Level2)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 bool isSuccess = waitpid(pid, &status, 0) != -1;
129 if (!isSuccess) {
130 ASSERT_FALSE(isSuccess);
131 return;
132 }
133
134 int exitCode = -1;
135 if (WIFEXITED(status)) {
136 exitCode = WEXITSTATUS(status);
137 printf("Exit status was %d\n", exitCode);
138 }
139 ASSERT_EQ(exitCode, 0);
140 }
141 GTEST_LOG_(INFO) << "FaultloggerdClientTest002: end.";
142 }
143
144 /**
145 * @tc.name: FaultloggerdClientTest003
146 * @tc.desc: request a file descriptor for logging with inconsistent pid
147 * @tc.type: FUNC
148 */
HWTEST_F(FaultloggerdClientTest, FaultloggerdClientTest003, TestSize.Level2)149 HWTEST_F(FaultloggerdClientTest, FaultloggerdClientTest003, TestSize.Level2)
150 {
151 GTEST_LOG_(INFO) << "FaultloggerdClientTest003: start.";
152 int32_t pid = fork();
153 if (pid == 0) {
154 int ret = 1;
155 constexpr int32_t appUid = 100068;
156 setuid(appUid);
157 FaultLoggerdRequest request;
158 request.type = FaultLoggerType::JS_HEAP_SNAPSHOT;
159 request.pid = appUid;
160 int32_t fd = RequestFileDescriptorEx(&request);
161 if (fd >= 0) {
162 close(fd);
163 ret = 0;
164 }
165 exit(ret);
166 } else if (pid > 0) {
167 int status;
168 bool isSuccess = waitpid(pid, &status, 0) != -1;
169 if (!isSuccess) {
170 ASSERT_FALSE(isSuccess);
171 return;
172 }
173
174 int exitCode = -1;
175 if (WIFEXITED(status)) {
176 exitCode = WEXITSTATUS(status);
177 printf("Exit status was %d\n", exitCode);
178 }
179 ASSERT_EQ(exitCode, 1);
180 }
181 GTEST_LOG_(INFO) << "FaultloggerdClientTest003: end.";
182 }
183 #if defined(HAS_LIB_SELINUX)
184 /**
185 * @tc.name: FaultloggerdClientTest004
186 * @tc.desc: request a file descriptor for logging with inconsistent pid but in processdump scontext
187 * @tc.type: FUNC
188 */
HWTEST_F(FaultloggerdClientTest, FaultloggerdClientTest004, TestSize.Level2)189 HWTEST_F(FaultloggerdClientTest, FaultloggerdClientTest004, TestSize.Level2)
190 {
191 GTEST_LOG_(INFO) << "FaultloggerdClientTest004: start.";
192
193 // If selinux is not opened, skip this test item
194 bool isSuccess = IsSelinuxEnforced();
195 if (!isSuccess) {
196 ASSERT_FALSE(isSuccess);
197 GTEST_LOG_(INFO) << "Selinux is not opened, skip FaultloggerdClientTest004";
198 return;
199 }
200 int32_t pid = fork();
201 if (pid == 0) {
202 int ret = 1;
203 constexpr int32_t appUid = 100068;
204 setcon("u:r:processdump:s0");
205 setuid(appUid);
206 FaultLoggerdRequest request;
207 request.type = FaultLoggerType::JS_HEAP_SNAPSHOT;
208 request.pid = appUid;
209 int32_t fd = RequestFileDescriptorEx(&request);
210 if (fd >= 0) {
211 close(fd);
212 ret = 0;
213 }
214 exit(ret);
215 } else if (pid > 0) {
216 int status;
217 bool isSuccess = waitpid(pid, &status, 0) != -1;
218 if (!isSuccess) {
219 ASSERT_FALSE(isSuccess);
220 return;
221 }
222
223 int exitCode = -1;
224 if (WIFEXITED(status)) {
225 exitCode = WEXITSTATUS(status);
226 printf("Exit status was %d\n", exitCode);
227 }
228 ASSERT_EQ(exitCode, 0);
229 }
230 GTEST_LOG_(INFO) << "FaultloggerdClientTest004: end.";
231 }
232
233 /**
234 * @tc.name: FaultloggerdClientTest005
235 * @tc.desc: sdkdump request which selinux label belongs to { hiview hidumper foundation }
236 * @tc.type: FUNC
237 */
HWTEST_F(FaultloggerdClientTest, FaultloggerdClientTest005, TestSize.Level2)238 HWTEST_F(FaultloggerdClientTest, FaultloggerdClientTest005, TestSize.Level2)
239 {
240 GTEST_LOG_(INFO) << "FaultloggerdClientTest005: start.";
241
242 // If selinux is not opened, skip this test item
243 bool isSuccess = IsSelinuxEnforced();
244 if (!isSuccess) {
245 ASSERT_FALSE(isSuccess);
246 GTEST_LOG_(INFO) << "Selinux is not opened, skip FaultloggerdClientTest005";
247 return;
248 }
249
250 int32_t pid = fork();
251 if (pid == 0) {
252 int ret = 1;
253 constexpr int32_t appUid = 100068;
254 setcon("u:r:hiview:s0");
255 setuid(appUid);
256 int resp = RequestSdkDump(1, 1);
257 if (resp == FaultLoggerCheckPermissionResp::CHECK_PERMISSION_PASS) {
258 ret = 0;
259 }
260 exit(ret);
261 } else if (pid > 0) {
262 int status;
263 bool isSuccess = waitpid(pid, &status, 0) != -1;
264 if (!isSuccess) {
265 ASSERT_FALSE(isSuccess);
266 return;
267 }
268
269 int exitCode = -1;
270 if (WIFEXITED(status)) {
271 exitCode = WEXITSTATUS(status);
272 printf("Exit status was %d\n", exitCode);
273 }
274 ASSERT_EQ(exitCode, 0);
275 }
276 GTEST_LOG_(INFO) << "FaultloggerdClientTest005: end.";
277 }
278
279 /**
280 * @tc.name: FaultloggerdClientTest006
281 * @tc.desc: sdkdump request which selinux label doesn't belongs to { hiview hidumper foundation }
282 * @tc.type: FUNC
283 */
HWTEST_F(FaultloggerdClientTest, FaultloggerdClientTest006, TestSize.Level2)284 HWTEST_F(FaultloggerdClientTest, FaultloggerdClientTest006, TestSize.Level2)
285 {
286 GTEST_LOG_(INFO) << "FaultloggerdClientTest006: start.";
287
288 // If selinux is not opened, skip this test item
289 bool isSuccess = IsSelinuxEnforced();
290 if (!isSuccess) {
291 ASSERT_FALSE(isSuccess);
292 GTEST_LOG_(INFO) << "Selinux is not opened, skip FaultloggerdClientTest006";
293 return;
294 }
295 int32_t pid = fork();
296 if (pid == 0) {
297 int ret = 1;
298 constexpr int32_t appUid = 100068;
299 setcon("u:r:wifi_host:s0");
300 setuid(appUid);
301 int resp = RequestSdkDump(1, 1);
302 if (resp == FaultLoggerCheckPermissionResp::CHECK_PERMISSION_REJECT) {
303 ret = 0;
304 }
305 exit(ret);
306 } else if (pid > 0) {
307 int status;
308 bool isSuccess = waitpid(pid, &status, 0) != -1;
309 if (!isSuccess) {
310 ASSERT_FALSE(isSuccess);
311 return;
312 }
313
314 int exitCode = -1;
315 if (WIFEXITED(status)) {
316 exitCode = WEXITSTATUS(status);
317 printf("Exit status was %d\n", exitCode);
318 }
319 ASSERT_EQ(exitCode, 0);
320 }
321 GTEST_LOG_(INFO) << "FaultloggerdClientTest006: end.";
322 }
323 #endif
324
325 /**
326 * @tc.name: FaultloggerdClientTest007
327 * @tc.desc: test FaultloggerdClient exception
328 * @tc.type: FUNC
329 */
HWTEST_F(FaultloggerdClientTest, FaultloggerdClientTest007, TestSize.Level2)330 HWTEST_F(FaultloggerdClientTest, FaultloggerdClientTest007, TestSize.Level2)
331 {
332 GTEST_LOG_(INFO) << "FaultloggerdClientTest007: start.";
333 int32_t fd = RequestLogFileDescriptor(nullptr);
334 ASSERT_EQ(fd, -1);
335 fd = RequestFileDescriptorEx(nullptr);
336 ASSERT_EQ(fd, -1);
337
338 bool ret = RequestCheckPermission(-1);
339 ASSERT_FALSE(ret);
340
341 int result = RequestPrintTHilog(nullptr, LINE_BUF_SIZE + 1);
342 ASSERT_EQ(result, -1);
343
344 int timeout = 10000; // 10000 : dump timeout ms
345 result = RequestSdkDumpJson(-1, -1, false, timeout);
346 ASSERT_EQ(result, -1);
347 RequestSdkDumpJson(-1, 1, false, timeout);
348 ASSERT_EQ(result, -1);
349 RequestSdkDumpJson(1, -1, false, timeout);
350 ASSERT_EQ(result, -1);
351 RequestSdkDumpJson(1, 1, false, timeout);
352 ASSERT_EQ(result, -1);
353
354 GTEST_LOG_(INFO) << "FaultloggerdClientTest007: end.";
355 }
356
DoClientProcess(const std::string& socketFileName)357 void DoClientProcess(const std::string& socketFileName)
358 {
359 // wait 2 seconds, waiting for the service to be ready
360 sleep(2);
361 int clientSocketFd = -1;
362
363 // socket connect time out 10 second
364 bool retBool = StartConnect(clientSocketFd, socketFileName.c_str(), 10);
365 ASSERT_TRUE(retBool);
366 ASSERT_NE(clientSocketFd, -1);
367 GTEST_LOG_(INFO) << "child connect finished, client fd:" << clientSocketFd;
368
369 int data = 12345; // 12345 is for server Cred test
370 retBool = SendMsgIovToSocket(clientSocketFd, reinterpret_cast<void *>(&data), sizeof(data));
371 ASSERT_TRUE(retBool);
372
373 GTEST_LOG_(INFO) << "Start read file desc";
374 int testFd = ReadFileDescriptorFromSocket(clientSocketFd);
375 GTEST_LOG_(INFO) << "recv testFd:" << testFd;
376 ASSERT_NE(testFd, -1);
377 close(clientSocketFd);
378 close(testFd);
379 }
380
DoServerProcess(const std::string& socketFileName)381 void DoServerProcess(const std::string& socketFileName)
382 {
383 GTEST_LOG_(INFO) << "server prepare listen";
384 int32_t serverSocketFd = -1;
385 std::string testFileName = "/data/test.txt";
386
387 // 5: means max connection count is 5
388 bool ret = StartListen(serverSocketFd, socketFileName.c_str(), 5);
389 ASSERT_TRUE(ret);
390 ASSERT_NE(serverSocketFd, -1);
391 GTEST_LOG_(INFO) << "server start listen fd:" << serverSocketFd;
392
393 struct timeval timev = {
394 20, // recv timeout 20 seconds
395 0
396 };
397 void* pTimev = &timev;
398 int retOpt = OHOS_TEMP_FAILURE_RETRY(setsockopt(serverSocketFd, SOL_SOCKET, SO_RCVTIMEO,
399 static_cast<const char*>(pTimev), sizeof(struct timeval)));
400 ASSERT_NE(retOpt, -1);
401
402 struct sockaddr_un clientAddr;
403 socklen_t clientAddrSize = static_cast<socklen_t>(sizeof(clientAddr));
404 int32_t connectionFd = OHOS_TEMP_FAILURE_RETRY(accept(serverSocketFd,
405 reinterpret_cast<struct sockaddr *>(&clientAddr), &clientAddrSize));
406 ASSERT_GT(connectionFd, 0);
407 GTEST_LOG_(INFO) << "server accept fd:" << connectionFd;
408
409 int optval = 1;
410 retOpt = setsockopt(connectionFd, SOL_SOCKET, SO_PASSCRED, &optval, sizeof(optval));
411 ASSERT_NE(retOpt, -1);
412
413 struct ucred rcred;
414 bool retCred = RecvMsgCredFromSocket(connectionFd, &rcred);
415 ASSERT_TRUE(retCred);
416 GTEST_LOG_(INFO) << "uid:" << rcred.uid;
417
418 // 0666 for file read and write
419 int testFdServer = open(testFileName.c_str(), O_RDWR | O_CREAT, 0666);
420 GTEST_LOG_(INFO) << "Start SendFileDescriptorToSocket";
421 SendFileDescriptorToSocket(connectionFd, testFdServer);
422 GTEST_LOG_(INFO) << "Close server connect fd";
423 close(connectionFd);
424 close(testFdServer);
425 close(serverSocketFd);
426 unlink(testFileName.c_str());
427 }
428
429 /**
430 * @tc.name: FaultloggerdSocketTest001
431 * @tc.desc: test StartListen, RecvMsgCredFromSocket and SendMsgCtlToSocket
432 * @tc.type: FUNC
433 */
HWTEST_F(FaultloggerdClientTest, FaultloggerdSocketTest001, TestSize.Level2)434 HWTEST_F(FaultloggerdClientTest, FaultloggerdSocketTest001, TestSize.Level2)
435 {
436 GTEST_LOG_(INFO) << "FaultloggerdSocketTest001: start.";
437 std::string testSocketName = "faultloggerd.server.test";
438
439 int32_t pid = fork();
440 if (pid == 0) {
441 DoClientProcess(testSocketName);
442 GTEST_LOG_(INFO) << "client exit";
443 exit(0);
444 } else if (pid > 0) {
445 DoServerProcess(testSocketName);
446
447 int status;
448 bool isSuccess = waitpid(pid, &status, 0) != -1;
449 if (!isSuccess) {
450 ASSERT_FALSE(isSuccess);
451 return;
452 }
453
454 int exitCode = -1;
455 if (WIFEXITED(status)) {
456 exitCode = WEXITSTATUS(status);
457 GTEST_LOG_(INFO) << "Exit status was " << exitCode;
458 }
459 ASSERT_EQ(exitCode, 0);
460 }
461 GTEST_LOG_(INFO) << "FaultloggerdSocketTest001: end.";
462 }
463 } // namespace HiviewDFX
464 } // namepsace OHOS
465