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
31using namespace testing;
32using namespace testing::ext;
33
34namespace OHOS {
35namespace HiviewDFX {
36class FaultloggerdClientTest : public testing::Test {
37public:
38    static void SetUpTestCase();
39    static void TearDownTestCase();
40    void SetUp();
41    void TearDown();
42};
43
44void FaultloggerdClientTest::SetUpTestCase()
45{
46}
47
48void FaultloggerdClientTest::TearDownTestCase()
49{
50}
51
52void FaultloggerdClientTest::SetUp()
53{
54}
55
56void FaultloggerdClientTest::TearDown()
57{
58}
59
60bool 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 */
73HWTEST_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 */
95HWTEST_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 */
149HWTEST_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 */
189HWTEST_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 */
238HWTEST_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 */
284HWTEST_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 */
330HWTEST_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
357void 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
381void 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 */
434HWTEST_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