1 /* 2 * Copyright (c) 2021 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 <dlfcn.h> 17 #include <fcntl.h> 18 #include <hwext/gtest-ext.h> 19 #include <hwext/gtest-tag.h> 20 #include <vector> 21 #include <sys/syscall.h> 22 23 #include "buffer_splitter.h" 24 #include "logging.h" 25 26 #pragma clang optimize off 27 28 using namespace testing::ext; 29 30 namespace { 31 const int DEFAULT_MALLOC_SIZE = 10; 32 const int DEFAULT_CALLOC_SIZE = 100; 33 const int DEFAULT_REALLOC_SIZE = 1000; 34 const int DATA_SIZE = 50; 35 const int WAIT_KILL_SIGNL = 4; 36 const int SLEEP_TIME = 5; 37 const int WAIT_FLUSH = 2; 38 39 const std::string DEFAULT_NATIVE_DAEMON_PATH("/system/bin/native_daemon"); 40 std::string DEFAULT_PATH("/data/local/tmp/"); 41 const int g_shareMemorySize = 1000 * 4096; 42 const int g_bufferSize = 100 * 1024; 43 const int g_defaultDepth = 32; 44 const int g_callocDepth = 13; 45 const int g_reallocDepth = 10; 46 const int g_mallocVecSize = 5; 47 const int g_freeVecSize = 4; 48 const int g_mallocGetDataSize = 3; 49 const int g_freeGetDataSize = 2; 50 std::unique_ptr<uint8_t[]> g_buffer = std::make_unique<uint8_t[]>(g_bufferSize); 51 52 using StaticSpace = struct { 53 int data[DATA_SIZE]; 54 } ; 55 56 class CheckHookDataTest : public ::testing::Test { 57 public: SetUpTestCase()58 static void SetUpTestCase() {} TearDownTestCase()59 static void TearDownTestCase() {} 60 StartDaemonProcessArgs(int pid,std::string outFile)61 void StartDaemonProcessArgs(int pid, std::string outFile) 62 { 63 int processNum = fork(); 64 if (processNum == 0) { 65 // start running native_daemon -o -s -p 66 execl(DEFAULT_NATIVE_DAEMON_PATH.c_str(), DEFAULT_NATIVE_DAEMON_PATH.c_str(), "-o", outFile.c_str(), 67 "-s", std::to_string(g_shareMemorySize).c_str(), "-p", std::to_string(pid).c_str(), nullptr); 68 _exit(1); 69 } else { 70 g_daemonPid = processNum; 71 } 72 } 73 StartDaemonProcessArgsDepth(int pid,int setHookDepth,std::string outFile)74 void StartDaemonProcessArgsDepth(int pid, int setHookDepth, std::string outFile) 75 { 76 int processNum = fork(); 77 if (processNum == 0) { 78 // start running native_daemon -o -s -p -d 79 execl(DEFAULT_NATIVE_DAEMON_PATH.c_str(), DEFAULT_NATIVE_DAEMON_PATH.c_str(), "-o", outFile.c_str(), 80 "-s", std::to_string(g_shareMemorySize).c_str(), "-p", std::to_string(pid).c_str(), 81 "-d", std::to_string(setHookDepth).c_str(), nullptr); 82 _exit(1); 83 } else { 84 g_daemonSetDepthPid = processNum; 85 } 86 } 87 StopProcess(int processNum)88 void StopProcess(int processNum) 89 { 90 std::string stopCmd = "kill -9 " + std::to_string(processNum); 91 system(stopCmd.c_str()); 92 } 93 ReadFile(std::string file)94 int32_t ReadFile(std::string file) 95 { 96 int fd = -1; 97 ssize_t bytesRead = 0; 98 char filePath[PATH_MAX + 1] = {0}; 99 100 if (snprintf_s(filePath, sizeof(filePath), sizeof(filePath) - 1, "%s", file.c_str()) < 0) { 101 const int bufSize = 256; 102 char buf[bufSize] = { 0 }; 103 strerror_r(errno, buf, bufSize); 104 HILOG_ERROR(LOG_CORE, "snprintf_s(%s) error, errno(%d:%s)", file.c_str(), errno, buf); 105 return -1; 106 } 107 108 char* realPath = realpath(filePath, nullptr); 109 if (realPath == nullptr) { 110 const int bufSize = 256; 111 char buf[bufSize] = { 0 }; 112 strerror_r(errno, buf, bufSize); 113 HILOG_ERROR(LOG_CORE, "realpath(%s) failed, errno(%d:%s)", file.c_str(), errno, buf); 114 return -1; 115 } 116 117 fd = open(realPath, O_RDONLY | O_CLOEXEC); 118 if (fd == -1) { 119 const int bufSize = 256; 120 char buf[bufSize] = { 0 }; 121 strerror_r(errno, buf, bufSize); 122 HILOG_ERROR(LOG_CORE, "%s:failed to open(%s), errno(%d:%s)", __func__, realPath, errno, buf); 123 return -1; 124 } 125 if (g_buffer == nullptr) { 126 HILOG_ERROR(LOG_CORE, "%s:empty address, g_buffer is NULL", __func__); 127 close(fd); 128 return -1; 129 } 130 bytesRead = read(fd, g_buffer.get(), g_bufferSize - 1); 131 if (bytesRead <= 0) { 132 close(fd); 133 HILOG_ERROR(LOG_CORE, "%s:failed to read(%s), errno=%d", __func__, realPath, errno); 134 return -1; 135 } 136 close(fd); 137 free(realPath); 138 139 return bytesRead; 140 } 141 DepthFree(int depth,void * p)142 void DepthFree(int depth, void *p) 143 { 144 StaticSpace staticeData; 145 if (depth == 0) { 146 staticeData.data[0] = 1; 147 free(p); 148 return; 149 } 150 return (DepthFree(depth - 1, p)); 151 } 152 DepthMalloc(int depth)153 char *DepthMalloc(int depth) 154 { 155 StaticSpace staticeData; 156 if (depth == 0) { 157 staticeData.data[0] = 1; 158 return reinterpret_cast<char *>(malloc(DEFAULT_MALLOC_SIZE)); 159 } 160 return (DepthMalloc(depth - 1)); 161 } 162 ApplyForMalloc(int depth)163 void ApplyForMalloc(int depth) 164 { 165 char *p = DepthMalloc(depth); 166 if (!p) { 167 const int bufSize = 256; 168 char buf[bufSize] = { 0 }; 169 strerror_r(errno, buf, bufSize); 170 HILOG_ERROR(LOG_CORE, "ApplyForMalloc: malloc failure, errno(%d:%s)", errno, buf); 171 return; 172 } 173 DepthFree(depth, p); 174 } 175 StartMallocProcess(int depth,int & pid)176 void StartMallocProcess(int depth, int& pid) 177 { 178 int processNum = fork(); 179 if (processNum == 0) { 180 while (1) { 181 ApplyForMalloc(depth); 182 sleep(1); 183 } 184 } else { 185 pid = processNum; 186 } 187 } 188 DepthCalloc(int depth,int callocSize)189 char *DepthCalloc(int depth, int callocSize) 190 { 191 StaticSpace staticeData; 192 if (depth == 0) { 193 staticeData.data[0] = 1; 194 return reinterpret_cast<char *>(calloc(sizeof(char), callocSize)); 195 } 196 return (DepthCalloc(depth - 1, callocSize)); 197 } 198 ApplyForCalloc(int depth)199 void ApplyForCalloc(int depth) 200 { 201 int callocSize = DEFAULT_CALLOC_SIZE / sizeof(char); 202 char *p = DepthCalloc(depth, callocSize); 203 if (!p) { 204 const int bufSize = 256; 205 char buf[bufSize] = { 0 }; 206 strerror_r(errno, buf, bufSize); 207 HILOG_ERROR(LOG_CORE, "ApplyForCalloc: calloc failure, errno(%d:%s)", errno, buf); 208 return; 209 } 210 DepthFree(depth, p); 211 } 212 StartCallocProcess(int depth,int & pid)213 void StartCallocProcess(int depth, int& pid) 214 { 215 int processNum = fork(); 216 if (processNum == 0) { 217 sleep(WAIT_KILL_SIGNL); 218 auto ret = malloc(DEFAULT_MALLOC_SIZE); 219 free(ret); 220 while (1) { 221 ApplyForCalloc(depth); 222 sleep(1); 223 } 224 } else { 225 pid = processNum; 226 } 227 } 228 DepthRealloc(int depth,void * p,int reallocSize)229 char *DepthRealloc(int depth, void *p, int reallocSize) 230 { 231 StaticSpace staticeData; 232 if (depth == 0) { 233 staticeData.data[0] = 1; 234 return reinterpret_cast<char *>(realloc(p, reallocSize)); 235 } 236 return (DepthRealloc(depth - 1, p, reallocSize)); 237 } 238 ApplyForRealloc(int depth)239 void ApplyForRealloc(int depth) 240 { 241 int reallocSize = DEFAULT_REALLOC_SIZE; 242 char *p = reinterpret_cast<char *>(malloc(DEFAULT_MALLOC_SIZE)); 243 if (!p) { 244 const int bufSize = 256; 245 char buf[bufSize] = { 0 }; 246 strerror_r(errno, buf, bufSize); 247 HILOG_ERROR(LOG_CORE, "ApplyForRealloc: malloc failure, errno(%d:%s)", errno, buf); 248 return; 249 } 250 char *np = DepthRealloc(depth, p, reallocSize); 251 if (!np) { 252 free(p); 253 const int bufSize = 256; 254 char buf[bufSize] = { 0 }; 255 strerror_r(errno, buf, bufSize); 256 HILOG_ERROR(LOG_CORE, "ApplyForRealloc: realloc failure, errno(%d:%s)", errno, buf); 257 return; 258 } 259 DepthFree(depth, np); 260 } 261 StartReallocProcess(int depth,int & pid)262 void StartReallocProcess(int depth, int& pid) 263 { 264 int processNum = fork(); 265 if (processNum == 0) { 266 while (1) { 267 ApplyForRealloc(depth); 268 sleep(1); 269 } 270 } else { 271 pid = processNum; 272 } 273 } 274 Getdata(BufferSplitter & totalbuffer,std::vector<std::string> & hookVec,char delimiter)275 bool Getdata(BufferSplitter& totalbuffer, std::vector<std::string>& hookVec, char delimiter) 276 { 277 totalbuffer.NextWord(delimiter); 278 if (!totalbuffer.CurWord()) { 279 return false; 280 } 281 std::string curWord = std::string(totalbuffer.CurWord(), totalbuffer.CurWordSize()); 282 hookVec.push_back(curWord); 283 return true; 284 } 285 286 private: 287 int g_daemonPid = -1; 288 int g_daemonSetDepthPid = -1; 289 }; 290 291 /** 292 * @tc.name: native hook 293 * @tc.desc: Test hook malloc normal process. 294 * @tc.type: FUNC 295 */ 296 HWTEST_F(CheckHookDataTest, DFX_DFR_Hiprofiler_0080, Function | MediumTest | Level1) 297 { 298 int setDepth = 100; // 递归深度大于hook默认深度30,测试文本 299 int mallocPid = -1; 300 std::string outFile = DEFAULT_PATH + "hooktest_malloc.txt"; 301 StartMallocProcess(setDepth, mallocPid); 302 sleep(1); 303 StartDaemonProcessArgs(mallocPid, outFile); 304 305 sleep(1); 306 std::string cmd = "kill -36 " + std::to_string(mallocPid); 307 system(cmd.c_str()); 308 309 sleep(SLEEP_TIME); // 等待生成文本 310 std::string cmdEnd = "kill -37 " + std::to_string(mallocPid); 311 system(cmdEnd.c_str()); 312 sleep(WAIT_FLUSH); 313 StopProcess(mallocPid); 314 StopProcess(g_daemonPid); 315 316 int32_t ret = ReadFile(outFile); 317 ASSERT_NE(ret, -1); 318 319 BufferSplitter totalbuffer(const_cast<char*>((char*)g_buffer.get()), ret + 1); 320 std::vector<std::string> hookVec; 321 std::string addr = ""; 322 int depth = 0; 323 int addrPos = 3; 324 bool isFirstHook = true; 325 do { 326 char delimiter = ';'; 327 Getdata(totalbuffer, hookVec, delimiter); 328 329 if (hookVec[0] == "malloc" && !isFirstHook) { 330 for (int i = 0; i < g_mallocGetDataSize; i++) { 331 EXPECT_TRUE(Getdata(totalbuffer, hookVec, delimiter)); 332 } 333 delimiter = '\n'; 334 EXPECT_TRUE(Getdata(totalbuffer, hookVec, delimiter)); 335 ASSERT_EQ(static_cast<int>(hookVec.size()), g_mallocVecSize); 336 ASSERT_EQ(atoi(hookVec[4].c_str()), DEFAULT_MALLOC_SIZE); 337 338 addr = hookVec[addrPos]; 339 depth = 0; 340 } else if (hookVec[0] == "free" && !isFirstHook) { 341 for (int i = 0; i < g_freeGetDataSize; i++) { 342 EXPECT_TRUE(Getdata(totalbuffer, hookVec, delimiter)); 343 } 344 delimiter = '\n'; 345 EXPECT_TRUE(Getdata(totalbuffer, hookVec, delimiter)); 346 ASSERT_EQ(static_cast<int>(hookVec.size()), g_freeVecSize); 347 EXPECT_STREQ(hookVec[addrPos].c_str(), addr.c_str()); 348 EXPECT_EQ(depth, g_defaultDepth); 349 350 isFirstHook = false; 351 addr = ""; 352 depth = 0; 353 } else { 354 depth++; 355 } 356 357 hookVec.clear(); 358 } while (totalbuffer.NextLine()); 359 } 360 361 /** 362 * @tc.name: native hook 363 * @tc.desc: Test hook calloc normal process. 364 * @tc.type: FUNC 365 */ 366 HWTEST_F(CheckHookDataTest, DFX_DFR_Hiprofiler_0090, Function | MediumTest | Level3) 367 { 368 int setDepth = 1; // 递归深度小于hook深度100,测试文本 369 int setHookDepth = 100; 370 int callocPid = -1; 371 std::string outFile = DEFAULT_PATH + "hooktest_calloc.txt"; 372 StartCallocProcess(setDepth, callocPid); 373 sleep(1); 374 StartDaemonProcessArgsDepth(callocPid, setHookDepth, outFile); 375 376 sleep(1); 377 std::string cmd = "kill -36 " + std::to_string(callocPid); 378 system(cmd.c_str()); 379 380 sleep(SLEEP_TIME); // 等待生成文本 381 std::string cmdEnd = "kill -37 " + std::to_string(callocPid); 382 system(cmdEnd.c_str()); 383 sleep(WAIT_FLUSH); 384 StopProcess(callocPid); 385 StopProcess(g_daemonSetDepthPid); 386 387 int32_t ret = ReadFile(outFile); 388 ASSERT_NE(ret, -1); 389 390 BufferSplitter totalbuffer(const_cast<char*>((char*)g_buffer.get()), ret + 1); 391 std::vector<std::string> hookVec; 392 std::string addr = ""; 393 int depth = 0; 394 int addrPos = 3; 395 bool isFirstHook = true; 396 do { 397 char delimiter = ';'; 398 Getdata(totalbuffer, hookVec, delimiter); 399 400 if (hookVec[0] == "malloc" && !isFirstHook) { 401 for (int i = 0; i < g_mallocGetDataSize; i++) { 402 EXPECT_TRUE(Getdata(totalbuffer, hookVec, delimiter)); 403 } 404 delimiter = '\n'; 405 EXPECT_TRUE(Getdata(totalbuffer, hookVec, delimiter)); 406 ASSERT_EQ(static_cast<int>(hookVec.size()), g_mallocVecSize); 407 ASSERT_EQ(atoi(hookVec[4].c_str()), DEFAULT_CALLOC_SIZE); 408 409 addr = hookVec[addrPos]; 410 depth = 0; 411 } else if (hookVec[0] == "free" && !isFirstHook) { 412 for (int i = 0; i < g_freeGetDataSize; i++) { 413 EXPECT_TRUE(Getdata(totalbuffer, hookVec, delimiter)); 414 } 415 delimiter = '\n'; 416 EXPECT_TRUE(Getdata(totalbuffer, hookVec, delimiter)); 417 ASSERT_EQ(static_cast<int>(hookVec.size()), g_freeVecSize); 418 EXPECT_STREQ(hookVec[addrPos].c_str(), addr.c_str()); 419 EXPECT_GE(depth, g_callocDepth); 420 421 isFirstHook = false; 422 addr = ""; 423 depth = 0; 424 } else { 425 depth++; 426 } 427 428 hookVec.clear(); 429 } while (totalbuffer.NextLine()); 430 } 431 432 /** 433 * @tc.name: native hook 434 * @tc.desc: Test hook realloc normal process. 435 * @tc.type: FUNC 436 */ 437 HWTEST_F(CheckHookDataTest, DFX_DFR_Hiprofiler_0100, Function | MediumTest | Level3) 438 { 439 int setDepth = 100; // realloc测试文本 440 int reallocPid = -1; 441 std::string outFile = DEFAULT_PATH + "hooktest_realloc.txt"; 442 StartReallocProcess(setDepth, reallocPid); 443 sleep(1); 444 StartDaemonProcessArgs(reallocPid, outFile); 445 446 sleep(1); 447 std::string cmd = "kill -36 " + std::to_string(reallocPid); 448 system(cmd.c_str()); 449 450 sleep(SLEEP_TIME); // 等待生成文本 451 std::string cmdEnd = "kill -37 " + std::to_string(reallocPid); 452 system(cmdEnd.c_str()); 453 sleep(WAIT_FLUSH); 454 StopProcess(reallocPid); 455 StopProcess(g_daemonPid); 456 457 int32_t ret = ReadFile(outFile); 458 ASSERT_NE(ret, -1); 459 460 BufferSplitter totalbuffer(const_cast<char*>((char*)g_buffer.get()), ret + 1); 461 std::vector<std::string> hookVec; 462 std::string mallocAddr = ""; 463 std::string reallocAddr = ""; 464 int depth = 0; 465 int addrPos = 3; 466 bool isFirstHook = true; 467 bool isRealloc = false; 468 do { 469 char delimiter = ';'; 470 Getdata(totalbuffer, hookVec, delimiter); 471 472 if (hookVec[0] == "malloc" && !isFirstHook) { 473 for (int i = 0; i < g_mallocGetDataSize; i++) { 474 EXPECT_TRUE(Getdata(totalbuffer, hookVec, delimiter)); 475 } 476 delimiter = '\n'; 477 EXPECT_TRUE(Getdata(totalbuffer, hookVec, delimiter)); 478 ASSERT_EQ(static_cast<int>(hookVec.size()), g_mallocVecSize); 479 480 if (isRealloc) { 481 reallocAddr = hookVec[addrPos]; 482 ASSERT_GE(atoi(hookVec[4].c_str()), DEFAULT_REALLOC_SIZE); 483 EXPECT_GE(depth, g_reallocDepth); 484 isFirstHook = false; 485 } else { 486 mallocAddr = hookVec[addrPos]; 487 ASSERT_EQ(atoi(hookVec[4].c_str()), DEFAULT_MALLOC_SIZE); 488 } 489 490 isRealloc = true; 491 depth = 0; 492 } else if (hookVec[0] == "free" && !isFirstHook) { 493 for (int i = 0; i < g_freeGetDataSize; i++) { 494 EXPECT_TRUE(Getdata(totalbuffer, hookVec, delimiter)); 495 } 496 delimiter = '\n'; 497 EXPECT_TRUE(Getdata(totalbuffer, hookVec, delimiter)); 498 ASSERT_EQ(static_cast<int>(hookVec.size()), g_freeVecSize); 499 500 if (isRealloc) { 501 EXPECT_STREQ(hookVec[addrPos].c_str(), reallocAddr.c_str()); 502 reallocAddr = ""; 503 } else { 504 EXPECT_STREQ(hookVec[addrPos].c_str(), mallocAddr.c_str()); 505 mallocAddr = ""; 506 } 507 508 isRealloc = false; 509 depth = 0; 510 } else { 511 depth++; 512 } 513 514 hookVec.clear(); 515 } while (totalbuffer.NextLine()); 516 } 517 } 518 519 #pragma clang optimize on 520