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#ifdef USE_GWP_ASAN 17 18#include <fcntl.h> 19#include <unistd.h> 20#include <string.h> 21#include <sys/random.h> 22 23#include "gwp_asan.h" 24 25#include "musl_log.h" 26#include "musl_malloc.h" 27#include "pthread.h" 28#include "pthread_impl.h" 29 30#ifdef OHOS_ENABLE_PARAMETER 31#include "sys_param.h" 32#endif 33 34#define MAX_SIMULTANEOUS_ALLOCATIONS 32 35#define SAMPLE_RATE 2500 36#define GWP_ASAN_NAME_LEN 256 37#define GWP_ASAN_PREDICT_TRUE(exp) __builtin_expect((exp) != 0, 1) 38#define GWP_ASAN_PREDICT_FALSE(exp) __builtin_expect((exp) != 0, 0) 39#define GWP_ASAN_LOGD(...) // change it to MUSL_LOGD to get gwp_asan debug log. 40#define GWP_ASAN_NO_ADDRESS __attribute__((no_sanitize("address", "hwaddress"))) 41 42static bool gwp_asan_initialized = false; 43static uint8_t process_sample_rate = 128; 44static uint8_t force_sample_alloctor = 0; 45static uint8_t previous_random_value = 0; 46 47typedef struct { 48 const char *dli_fname; 49 void *dli_fbase; 50 const char *dli_sname; 51 void *dli_saddr; 52} Dl_info; 53 54// C interfaces of gwp_asan provided by LLVM side. 55extern void init_gwp_asan(void *init_options); 56extern void* gwp_asan_malloc(size_t bytes); 57extern void gwp_asan_free(void *mem); 58extern bool gwp_asan_should_sample(); 59extern bool gwp_asan_pointer_is_mine(void *mem); 60extern bool gwp_asan_has_free_mem(); 61extern size_t gwp_asan_get_size(void *mem); 62extern void gwp_asan_disable(); 63extern void gwp_asan_enable(); 64extern void gwp_asan_iterate(void *base, size_t size, 65 void (*callback)(void* base, size_t size, void *arg), void *arg); 66 67 68extern int dladdr(const void *addr, Dl_info *info); 69 70static char *get_process_short_name(char *buf, size_t length) 71{ 72 char *app = NULL; 73 int fd = open("/proc/self/cmdline", O_RDONLY); 74 if (fd != -1) { 75 ssize_t ret = read(fd, buf, length - 1); 76 if (ret != -1) { 77 buf[ret] = 0; 78 app = strrchr(buf, '/'); 79 if (app) { 80 app++; 81 } else { 82 app = buf; 83 } 84 char *app_end = strchr(app, ':'); 85 if (app_end) { 86 *app_end = 0; 87 } 88 } 89 close(fd); 90 } 91 return app; 92} 93 94bool is_gwp_asan_disable() 95{ 96 char para_name[GWP_ASAN_NAME_LEN] = "gwp_asan.disable.all"; 97 static CachedHandle para_handler = NULL; 98 if (para_handler == NULL) { 99 para_handler = CachedParameterCreate(para_name, "false"); 100 } 101 char *para_value = CachedParameterGet(para_handler); 102 if (para_value != NULL && strcmp(para_value, "true") == 0) { 103 return true; 104 } 105 return false; 106} 107 108bool force_sample_process_by_env() 109{ 110 char buf[GWP_ASAN_NAME_LEN]; 111 char *path = get_process_short_name(buf, GWP_ASAN_NAME_LEN); 112 if (!path) { 113 return false; 114 } 115 char para_name[GWP_ASAN_NAME_LEN] = "gwp_asan.enable.app."; 116 strcat(para_name, path); 117 static CachedHandle app_enable_handle = NULL; 118 if (app_enable_handle == NULL) { 119 app_enable_handle = CachedParameterCreate(para_name, "false"); 120 } 121 char *param_value = CachedParameterGet(app_enable_handle); 122 if (param_value != NULL) { 123 if (strcmp(param_value, "true") == 0) { 124 return true; 125 } 126 } 127 return false; 128} 129 130bool force_sample_alloctor_by_env() 131{ 132 char para_name[GWP_ASAN_NAME_LEN] = "gwp_asan.sample.all"; 133 static CachedHandle para_handler = NULL; 134 if (para_handler == NULL) { 135 para_handler = CachedParameterCreate(para_name, "false"); 136 } 137 char *para_value = CachedParameterGet(para_handler); 138 if (para_value != NULL && strcmp(para_value, "true") == 0) { 139 force_sample_alloctor = 1; 140 return true; 141 } 142 return false; 143} 144 145bool should_sample_process() 146{ 147#ifdef OHOS_ENABLE_PARAMETER 148 if (force_sample_process_by_env()) { 149 return true; 150 } 151#endif 152 153 uint8_t random_value; 154 // If getting a random number using a non-blocking fails, the random value is incremented. 155 if (getrandom(&random_value, sizeof(random_value), GRND_RANDOM | GRND_NONBLOCK) == -1) { 156 random_value = previous_random_value + 1; 157 previous_random_value = random_value; 158 } 159 160 return (random_value % process_sample_rate) == 0 ? true : false; 161} 162 163#define ASAN_LOG_LIB "libasan_logger.z.so" 164static void (*WriteGwpAsanLog)(char*, size_t); 165static void* handle = NULL; 166static bool try_load_asan_logger = false; 167static pthread_mutex_t gwpasan_mutex = PTHREAD_MUTEX_INITIALIZER; 168 169void gwp_asan_printf(const char *fmt, ...) 170{ 171 char para_name[GWP_ASAN_NAME_LEN] = "gwp_asan.log.path"; 172 static CachedHandle para_handler = NULL; 173 if (para_handler == NULL) { 174 para_handler = CachedParameterCreate(para_name, "file"); 175 } 176 char *para_value = CachedParameterGet(para_handler); 177 if (strcmp(para_value, "file") == 0) { 178 char process_short_name[GWP_ASAN_NAME_LEN]; 179 char *path = get_process_short_name(process_short_name, GWP_ASAN_NAME_LEN); 180 if (!path) { 181 MUSL_LOGE("[gwp_asan]: get_process_short_name failed!"); 182 return; 183 } 184 char log_path[GWP_ASAN_NAME_LEN]; 185 snprintf(log_path, GWP_ASAN_NAME_LEN, "%s%s.%s.%d.log", GWP_ASAN_LOG_DIR, GWP_ASAN_LOG_TAG, path, getpid()); 186 FILE *fp = fopen(log_path, "a+"); 187 if (!fp) { 188 MUSL_LOGE("[gwp_asan]: %{public}s fopen %{public}s failed!", path, log_path); 189 return; 190 } else { 191 MUSL_LOGE("[gwp_asan]: %{public}s fopen %{public}s succeed.", path, log_path); 192 } 193 va_list ap; 194 va_start(ap, fmt); 195 int result = vfprintf(fp, fmt, ap); 196 va_end(ap); 197 fclose(fp); 198 if (result < 0) { 199 MUSL_LOGE("[gwp_asan] %{public}s write log failed!\n", path); 200 } 201 return; 202 } 203 if (strcmp(para_value, "default") == 0) { 204 va_list ap; 205 va_start(ap, fmt); 206 char log_buffer[PATH_MAX]; 207 int result = vsnprintf(log_buffer, PATH_MAX, fmt, ap); 208 va_end(ap); 209 if (result < 0) { 210 MUSL_LOGE("[gwp_asan] write log failed!\n"); 211 } 212 if (WriteGwpAsanLog != NULL) { 213 WriteGwpAsanLog(log_buffer, strlen(log_buffer)); 214 return; 215 } 216 if (try_load_asan_logger) { 217 return; 218 } 219 pthread_mutex_lock(&gwpasan_mutex); 220 if (WriteGwpAsanLog != NULL) { 221 WriteGwpAsanLog(log_buffer, strlen(log_buffer)); 222 pthread_mutex_unlock(&gwpasan_mutex); 223 return; 224 } 225 if (!try_load_asan_logger && handle == NULL) { 226 try_load_asan_logger = true; 227 handle = dlopen(ASAN_LOG_LIB, RTLD_LAZY); 228 if (handle == NULL) { 229 pthread_mutex_unlock(&gwpasan_mutex); 230 return; 231 } 232 *(void**)(&WriteGwpAsanLog) = dlsym(handle, "WriteGwpAsanLog"); 233 if (WriteGwpAsanLog != NULL) { 234 WriteGwpAsanLog(log_buffer, strlen(log_buffer)); 235 } 236 } 237 pthread_mutex_unlock(&gwpasan_mutex); 238 return; 239 } 240 if (strcmp(para_value, "stdout") == 0) { 241 va_list ap; 242 va_start(ap, fmt); 243 int result = vfprintf(stdout, fmt, ap); 244 va_end(ap); 245 if (result < 0) { 246 MUSL_LOGE("[gwp_asan] write log failed!\n"); 247 } 248 return; 249 } 250} 251 252void gwp_asan_printf_backtrace(uintptr_t *trace_buffer, size_t trace_length, printf_t gwp_asan_printf) 253{ 254 if (trace_length == 0) { 255 gwp_asan_printf("It dosen't see any stack trace!\n"); 256 } 257 for (size_t i = 0; i < trace_length; i++) { 258 if (trace_buffer[i]) { 259 Dl_info info; 260 if (dladdr(trace_buffer[i], &info)) { 261 size_t offset = trace_buffer[i] - (uintptr_t)info.dli_fbase; 262 gwp_asan_printf(" #%zu %p (%s+%p)\n", i, trace_buffer[i], info.dli_fname, offset); 263 } else { 264 gwp_asan_printf(" #%zu %p\n", i, trace_buffer[i]); 265 } 266 } 267 } 268 gwp_asan_printf("\n"); 269} 270 271// Strip pc because pc may have been protected by pac(Pointer Authentication) when build with "-mbranch-protection". 272size_t strip_pac_pc(size_t ptr) 273{ 274#if defined(MUSL_AARCH64_ARCH) 275 register size_t x30 __asm__("x30") = ptr; 276 // "xpaclri" is a NOP on pre armv8.3-a arch. 277 __asm__ volatile("xpaclri" : "+r"(x30)); 278 return x30; 279#else 280 return ptr; 281#endif 282} 283 284/* This function is used for gwp_asan to record the call stack when allocate and deallocate. 285 * So we implemented a fast unwind function by using fp. 286 * The unwind process may stop because the value of fp is incorrect(fp was not saved on the stack due to optimization) 287 * We can build library with "-fno-omit-frame-pointer" to get a more accurate call stack. 288 * The basic unwind principle is as follows: 289 * Stack: func1->func2->func3 290 * --------------------| [Low Adress] 291 * | fp | |-------->| 292 * | lr | func3 | | 293 * | ...... | | | 294 * --------------------|<--------| 295 * | fp | |-------->| 296 * | lr | func2 | | 297 * | ...... | | | 298 * --------------------| | 299 * | fp | |<--------| 300 * | lr | func1 | 301 * | ...... | | 302 * --------------------| [High Address] 303 */ 304GWP_ASAN_NO_ADDRESS size_t libc_gwp_asan_unwind_fast(size_t *frame_buf, size_t max_record_stack, 305 __attribute__((unused)) void *signal_context) 306{ 307 size_t current_frame_addr = __builtin_frame_address(0); 308 size_t stack_end = (size_t)(__pthread_self()->stack); 309 size_t num_frames = 0; 310 size_t prev_fp = 0; 311 size_t prev_lr = 0; 312 while (true) { 313 unwind_info *frame = (unwind_info*)(current_frame_addr); 314 GWP_ASAN_LOGD("[gwp_asan] unwind info:%{public}d cur:%{public}p, end:%{public}p fp:%{public}p lr:%{public}p \n", 315 num_frames, current_frame_addr, stack_end, frame->fp, frame->lr); 316 if (!frame->lr) { 317 break; 318 } 319 if (num_frames < max_record_stack) { 320 frame_buf[num_frames] = strip_pac_pc(frame->lr) - 4; 321 } 322 ++num_frames; 323 if (frame->fp == prev_fp || frame->lr == prev_lr || frame->fp < current_frame_addr + sizeof(unwind_info) || 324 frame->fp >= stack_end || frame->fp % sizeof(void*) != 0) { 325 break; 326 } 327 prev_fp = frame->fp; 328 prev_lr = frame->lr; 329 current_frame_addr = frame->fp; 330 } 331 332 return num_frames; 333} 334 335bool may_init_gwp_asan(bool force_init) 336{ 337 GWP_ASAN_LOGD("[gwp_asan]: may_init_gwp_asan enter force_init:%{public}d.\n", force_init); 338 if (gwp_asan_initialized) { 339 GWP_ASAN_LOGD("[gwp_asan]: may_init_gwp_asan return because gwp_asan_initialized is true.\n"); 340 return false; 341 } 342#ifdef OHOS_ENABLE_PARAMETER 343 // Turn off gwp_asan. 344 if (GWP_ASAN_PREDICT_FALSE(is_gwp_asan_disable())) { 345 GWP_ASAN_LOGD("[gwp_asan]: may_init_gwp_asan return because gwp_asan is disable by env.\n"); 346 return false; 347 } 348#endif 349 350 if (!force_init && !should_sample_process()) { 351 GWP_ASAN_LOGD("[gwp_asan]: may_init_gwp_asan return because sample not hit.\n"); 352 return false; 353 } 354 355#ifdef OHOS_ENABLE_PARAMETER 356 // All memory allocations use gwp_asan. 357 force_sample_alloctor_by_env(); 358#endif 359 360 gwp_asan_option gwp_asan_option = { 361 .enable = true, 362 .install_fork_handlers = true, 363 .install_signal_handlers = true, 364 .max_simultaneous_allocations = MAX_SIMULTANEOUS_ALLOCATIONS, 365 .sample_rate = SAMPLE_RATE, 366 .backtrace = libc_gwp_asan_unwind_fast, 367 .gwp_asan_printf = gwp_asan_printf, 368 .printf_backtrace = gwp_asan_printf_backtrace, 369 .segv_backtrace = libc_gwp_asan_unwind_fast, 370 }; 371 372 char buf[GWP_ASAN_NAME_LEN]; 373 char *path = get_process_short_name(buf, GWP_ASAN_NAME_LEN); 374 if (!path) { 375 return false; 376 } 377 378 MUSL_LOGE("[gwp_asan]: %{public}d %{public}s gwp_asan initializing.\n", getpid(), path); 379 init_gwp_asan((void*)&gwp_asan_option); 380 gwp_asan_initialized = true; 381 MUSL_LOGE("[gwp_asan]: %{public}d %{public}s gwp_asan initialized.\n", getpid(), path); 382 return true; 383} 384bool init_gwp_asan_by_libc(bool force_init) 385{ 386 char buf[GWP_ASAN_NAME_LEN]; 387 char *path = get_process_short_name(buf, GWP_ASAN_NAME_LEN); 388 if (!path) { 389 return false; 390 } 391 // We don't sample appspawn, and the chaild process decides whether to sample or not. 392 if (strcmp(path, "appspawn") == 0 || strcmp(path, "sh") == 0) { 393 return false; 394 } 395 return may_init_gwp_asan(force_init); 396} 397 398void* get_platform_gwp_asan_tls_slot() 399{ 400 return (void*)(&(__pthread_self()->gwp_asan_tls)); 401} 402 403void* libc_gwp_asan_malloc(size_t bytes) 404{ 405 if (GWP_ASAN_PREDICT_TRUE(!gwp_asan_initialized)) { 406 return MuslFunc(malloc)(bytes); 407 } 408 void *res = NULL; 409 if (GWP_ASAN_PREDICT_FALSE(force_sample_alloctor || gwp_asan_should_sample())) { 410 res = gwp_asan_malloc(bytes); 411 if (res != NULL) { 412 return res; 413 } 414 } 415 return MuslFunc(malloc)(bytes); 416} 417 418void* libc_gwp_asan_calloc(size_t nmemb, size_t size) 419{ 420 if (GWP_ASAN_PREDICT_TRUE(!gwp_asan_initialized)) { 421 return MuslFunc(calloc)(nmemb, size); 422 } 423 424 if (GWP_ASAN_PREDICT_FALSE(force_sample_alloctor || gwp_asan_should_sample())) { 425 size_t total_bytes; 426 void* result = NULL; 427 if (!__builtin_mul_overflow(nmemb, size, &total_bytes)) { 428 GWP_ASAN_LOGD("[gwp_asan]: call gwp_asan_malloc nmemb:%{public}d size:%{public}d.\n", nmemb, size); 429 result = gwp_asan_malloc(total_bytes); 430 if (result != NULL) { 431 return result; 432 } 433 } 434 } 435 436 return MuslFunc(calloc)(nmemb, size); 437} 438 439void* libc_gwp_asan_realloc(void *ptr, size_t size) 440{ 441 if (GWP_ASAN_PREDICT_TRUE(!gwp_asan_initialized)) { 442 return MuslFunc(realloc)(ptr, size); 443 } 444 445 if (GWP_ASAN_PREDICT_FALSE(gwp_asan_pointer_is_mine(ptr))) { 446 GWP_ASAN_LOGD("[gwp_asan]: call gwp_asan_malloc ptr:%{public}p size:%{public}d.\n", ptr, size); 447 if (GWP_ASAN_PREDICT_FALSE(size == 0)) { 448 gwp_asan_free(ptr); 449 return NULL; 450 } 451 452 void* new_addr = gwp_asan_malloc(size); 453 if (new_addr != NULL) { 454 size_t old_size = gwp_asan_get_size(ptr); 455 memcpy(new_addr, ptr, (size < old_size) ? size : old_size); 456 gwp_asan_free(ptr); 457 return new_addr; 458 } else { 459 // Use the default allocator if gwp malloc failed. 460 void* addr_of_default_allocator = MuslFunc(malloc)(size); 461 if (addr_of_default_allocator != NULL) { 462 size_t old_size = gwp_asan_get_size(ptr); 463 memcpy(addr_of_default_allocator, ptr, (size < old_size) ? size : old_size); 464 gwp_asan_free(ptr); 465 return addr_of_default_allocator; 466 } else { 467 return NULL; 468 } 469 } 470 } 471 return MuslFunc(realloc)(ptr, size); 472} 473 474void libc_gwp_asan_free(void *addr) 475{ 476 if (GWP_ASAN_PREDICT_TRUE(!gwp_asan_initialized)) { 477 return MuslFunc(free)(addr); 478 } 479 if (GWP_ASAN_PREDICT_FALSE(gwp_asan_pointer_is_mine(addr))) { 480 return gwp_asan_free(addr); 481 } 482 return MuslFunc(free)(addr); 483} 484 485size_t libc_gwp_asan_malloc_usable_size(void *addr) 486{ 487 if (GWP_ASAN_PREDICT_TRUE(!gwp_asan_initialized)) { 488 return MuslMalloc(malloc_usable_size)(addr); 489 } 490 if (GWP_ASAN_PREDICT_FALSE(gwp_asan_pointer_is_mine(addr))) { 491 return gwp_asan_get_size(addr); 492 } 493 return MuslMalloc(malloc_usable_size)(addr); 494} 495 496void libc_gwp_asan_malloc_iterate(void *base, size_t size, 497 void (*callback)(uintptr_t base, size_t size, void *arg), void *arg) 498{ 499 if (GWP_ASAN_PREDICT_TRUE(!gwp_asan_initialized)) { 500 return; 501 } 502 if (GWP_ASAN_PREDICT_FALSE(gwp_asan_pointer_is_mine(base))) { 503 return gwp_asan_iterate(base, size, callback, arg); 504 } 505 return; 506} 507 508void libc_gwp_asan_malloc_disable() 509{ 510 if (GWP_ASAN_PREDICT_TRUE(!gwp_asan_initialized)) { 511 return; 512 } 513 514 return gwp_asan_disable(); 515} 516 517void libc_gwp_asan_malloc_enable() 518{ 519 if (GWP_ASAN_PREDICT_TRUE(!gwp_asan_initialized)) { 520 return; 521 } 522 523 return gwp_asan_enable(); 524} 525 526bool libc_gwp_asan_has_free_mem() 527{ 528 if (GWP_ASAN_PREDICT_TRUE(!gwp_asan_initialized)) { 529 return false; 530 } 531 gwp_asan_disable(); 532 int res = gwp_asan_has_free_mem(); 533 gwp_asan_enable(); 534 return res; 535} 536bool libc_gwp_asan_ptr_is_mine(void *addr) 537{ 538 if (GWP_ASAN_PREDICT_TRUE(!gwp_asan_initialized)) { 539 return false; 540 } 541 542 return gwp_asan_pointer_is_mine(addr); 543} 544#else 545#include <stdbool.h> 546 547// Used for appspawn. 548bool may_init_gwp_asan(bool force_init) 549{ 550 return false; 551} 552#endif 553