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