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