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