• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
42 static bool gwp_asan_initialized = false;
43 static uint8_t process_sample_rate = 128;
44 static uint8_t force_sample_alloctor = 0;
45 static uint8_t previous_random_value = 0;
46 
47 typedef 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.
55 extern void init_gwp_asan(void *init_options);
56 extern void* gwp_asan_malloc(size_t bytes);
57 extern void gwp_asan_free(void *mem);
58 extern bool gwp_asan_should_sample();
59 extern bool gwp_asan_pointer_is_mine(void *mem);
60 extern bool gwp_asan_has_free_mem();
61 extern size_t gwp_asan_get_size(void *mem);
62 extern void gwp_asan_disable();
63 extern void gwp_asan_enable();
64 extern void gwp_asan_iterate(void *base, size_t size,
65                                   void (*callback)(void* base, size_t size, void *arg), void *arg);
66 
67 
68 extern int dladdr(const void *addr, Dl_info *info);
69 
get_process_short_name(char * buf,size_t length)70 static 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 
is_gwp_asan_disable()94 bool 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 
force_sample_process_by_env()108 bool 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 
force_sample_alloctor_by_env()130 bool 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 
should_sample_process()145 bool should_sample_process()
146 {
147 #ifdef OHOS_ENABLE_PARAMETER
148     if (force_sample_process_by_env()) {
149         return true;
150     }
151 #endif
152     uint8_t random_value;
153     // If getting a random number using a non-blocking fails, the random value is incremented.
154     if (getrandom(&random_value, sizeof(random_value), GRND_RANDOM | GRND_NONBLOCK) == -1) {
155         random_value = previous_random_value + 1;
156         previous_random_value = random_value;
157     }
158 
159     return (random_value % process_sample_rate) == 0 ? true : false;
160 }
161 
162 #define ASAN_LOG_LIB "libasan_logger.z.so"
163 static void (*WriteGwpAsanLog)(char*, size_t);
164 static void* handle = NULL;
165 static bool try_load_asan_logger = false;
166 static pthread_mutex_t gwpasan_mutex = PTHREAD_MUTEX_INITIALIZER;
167 
gwp_asan_printf(const char * fmt,...)168 void gwp_asan_printf(const char *fmt, ...)
169 {
170     char para_name[GWP_ASAN_NAME_LEN] = "gwp_asan.log.path";
171     static CachedHandle para_handler = NULL;
172     if (para_handler == NULL) {
173         para_handler = CachedParameterCreate(para_name, "file");
174     }
175     char *para_value = CachedParameterGet(para_handler);
176     if (strcmp(para_value, "file") == 0) {
177         char process_short_name[GWP_ASAN_NAME_LEN];
178         char *path = get_process_short_name(process_short_name, GWP_ASAN_NAME_LEN);
179         if (!path) {
180             MUSL_LOGE("[gwp_asan]: get_process_short_name failed!");
181             return;
182         }
183         char log_path[GWP_ASAN_NAME_LEN];
184         snprintf(log_path, GWP_ASAN_NAME_LEN, "%s%s.%s.%d.log", GWP_ASAN_LOG_DIR, GWP_ASAN_LOG_TAG, path, getpid());
185         FILE *fp = fopen(log_path, "a+");
186         if (!fp) {
187             MUSL_LOGE("[gwp_asan]: %{public}s fopen %{public}s succeed.", path, log_path);
188             return;
189         } else {
190             MUSL_LOGE("[gwp_asan]: %{public}s fopen %{public}s failed!", path, log_path);
191         }
192         va_list ap;
193         va_start(ap, fmt);
194         int result = vfprintf(fp, fmt, ap);
195         va_end(ap);
196         fclose(fp);
197         if (result < 0) {
198             MUSL_LOGE("[gwp_asan] %{public}s write log failed!\n", path);
199         }
200         return;
201     }
202     if (strcmp(para_value, "default") == 0) {
203         va_list ap;
204         va_start(ap, fmt);
205         char log_buffer[PATH_MAX];
206         int result = vsnprintf(log_buffer, PATH_MAX, fmt, ap);
207         va_end(ap);
208         if (result < 0) {
209             MUSL_LOGE("[gwp_asan] write log failed!\n");
210         }
211         if(WriteGwpAsanLog != NULL) {
212             WriteGwpAsanLog(log_buffer, strlen(log_buffer));
213             return;
214 	}
215         if(try_load_asan_logger) {
216             return;
217         }
218         pthread_mutex_lock(&gwpasan_mutex);
219         if(WriteGwpAsanLog != NULL) {
220             WriteGwpAsanLog(log_buffer, strlen(log_buffer));
221             pthread_mutex_unlock(&gwpasan_mutex);
222             return;
223         }
224         if (!try_load_asan_logger && handle == NULL) {
225             try_load_asan_logger = true;
226             handle = dlopen(ASAN_LOG_LIB, RTLD_LAZY);
227 	    if (handle == NULL) {
228                 pthread_mutex_unlock(&gwpasan_mutex);
229                 return;
230             }
231             *(void**)(&WriteGwpAsanLog) = dlsym(handle, "WriteGwpAsanLog");
232             if (WriteGwpAsanLog != NULL) {
233                 WriteGwpAsanLog(log_buffer, strlen(log_buffer));
234             }
235         }
236         pthread_mutex_unlock(&gwpasan_mutex);
237         return;
238     }
239     if (strcmp(para_value, "stdout") == 0) {
240         va_list ap;
241         va_start(ap, fmt);
242         int result = vfprintf(stdout, fmt, ap);
243         va_end(ap);
244         if (result < 0) {
245             MUSL_LOGE("[gwp_asan] write log failed!\n");
246         }
247         return;
248     }
249 }
250 
gwp_asan_printf_backtrace(uintptr_t * trace_buffer,size_t trace_length,printf_t gwp_asan_printf)251 void gwp_asan_printf_backtrace(uintptr_t *trace_buffer, size_t trace_length, printf_t gwp_asan_printf)
252 {
253     if (trace_length == 0) {
254         gwp_asan_printf("It dosen't see any stack trace!\n");
255     }
256     for (size_t i = 0; i < trace_length; i++) {
257         if (trace_buffer[i]) {
258             Dl_info info;
259             if (dladdr(trace_buffer[i], &info)) {
260                 size_t offset = trace_buffer[i] - (uintptr_t)info.dli_fbase;
261                 gwp_asan_printf("  #%zu %p (%s+%p)\n", i, trace_buffer[i], info.dli_fname, offset);
262             } else {
263                 gwp_asan_printf("  #%zu %p\n", i, trace_buffer[i]);
264             }
265         }
266     }
267     gwp_asan_printf("\n");
268 }
269 
270 // Strip pc because pc may have been protected by pac(Pointer Authentication) when build with "-mbranch-protection".
strip_pac_pc(size_t ptr)271 size_t strip_pac_pc(size_t ptr)
272 {
273 #if defined(MUSL_AARCH64_ARCH)
274     register size_t x30 __asm__("x30") = ptr;
275     // "xpaclri" is a NOP on pre armv8.3-a arch.
276     __asm__ volatile("xpaclri" : "+r"(x30));
277     return x30;
278 #else
279     return ptr;
280 #endif
281 }
282 
283 /* This function is used for gwp_asan to record the call stack when allocate and deallocate.
284  * So we implemented a fast unwind function by using fp.
285  * The unwind process may stop because the value of fp is incorrect(fp was not saved on the stack due to optimization)
286  * We can build library with "-fno-omit-frame-pointer" to get a more accurate call stack.
287  * The basic unwind principle is as follows:
288  * Stack: func1->func2->func3
289  * --------------------| [Low Adress]
290  * |   fp      |       |-------->|
291  * |   lr      | func3 |         |
292  * |   ......  |       |         |
293  * --------------------|<--------|
294  * |   fp      |       |-------->|
295  * |   lr      | func2 |         |
296  * |   ......  |       |         |
297  * --------------------|         |
298  * |   fp      |       |<--------|
299  * |   lr      | func1 |
300  * |   ......  |       |
301  * --------------------| [High Address]
302  */
libc_gwp_asan_unwind_fast(size_t * frame_buf,size_t max_record_stack,void * signal_context)303 GWP_ASAN_NO_ADDRESS size_t libc_gwp_asan_unwind_fast(size_t *frame_buf, size_t max_record_stack,
304                                                      __attribute__((unused)) void *signal_context)
305 {
306     size_t current_frame_addr = __builtin_frame_address(0);
307     size_t stack_end = (size_t)(__pthread_self()->stack);
308     size_t num_frames = 0;
309     while (true) {
310         unwind_info *frame = (unwind_info*)(current_frame_addr);
311         GWP_ASAN_LOGD("[gwp_asan] unwind info:%{public}d cur:%{public}p, end:%{public}p fp:%{public}p lr:%{public}p \n",
312                       num_frames, current_frame_addr, stack_end, frame->fp, frame->lr);
313         if (!frame->lr) {
314             break;
315         }
316         if (num_frames < max_record_stack) {
317             frame_buf[num_frames] = strip_pac_pc(frame->lr) - 4;
318             GWP_ASAN_LOGD("[gwp_asan]: before strip pc:%{public}p after strip pc:%{public}p\n",
319                           frame->lr, frame_buf[num_frames]);
320         }
321         ++num_frames;
322         if (frame->fp < current_frame_addr || frame->fp >= stack_end ||
323             frame->fp % sizeof(void*) != 0) {
324             break;
325         }
326         current_frame_addr = frame->fp;
327     }
328 
329     return num_frames;
330 }
331 
may_init_gwp_asan(bool force_init)332 bool may_init_gwp_asan(bool force_init)
333 {
334     GWP_ASAN_LOGD("[gwp_asan]: may_init_gwp_asan enter force_init:%{public}d.\n", force_init);
335     if (gwp_asan_initialized) {
336         GWP_ASAN_LOGD("[gwp_asan]: may_init_gwp_asan return because gwp_asan_initialized is true.\n");
337         return false;
338     }
339 #ifdef OHOS_ENABLE_PARAMETER
340     // Turn off gwp_asan.
341     if (GWP_ASAN_PREDICT_FALSE(is_gwp_asan_disable())) {
342         GWP_ASAN_LOGD("[gwp_asan]: may_init_gwp_asan return because gwp_asan is disable by env.\n");
343         return false;
344     }
345 #endif
346 
347     if (!force_init && !should_sample_process()) {
348         GWP_ASAN_LOGD("[gwp_asan]: may_init_gwp_asan return because sample not hit.\n");
349         return false;
350     }
351 
352 #ifdef OHOS_ENABLE_PARAMETER
353     // All memory allocations use gwp_asan.
354     force_sample_alloctor_by_env();
355 #endif
356 
357     gwp_asan_option gwp_asan_option = {
358         .enable = true,
359         .install_fork_handlers = true,
360         .install_signal_handlers = true,
361         .max_simultaneous_allocations = MAX_SIMULTANEOUS_ALLOCATIONS,
362         .sample_rate = SAMPLE_RATE,
363         .backtrace = libc_gwp_asan_unwind_fast,
364         .gwp_asan_printf = gwp_asan_printf,
365         .printf_backtrace = gwp_asan_printf_backtrace,
366         .segv_backtrace = libc_gwp_asan_unwind_fast,
367     };
368 
369     char buf[GWP_ASAN_NAME_LEN];
370     char *path = get_process_short_name(buf, GWP_ASAN_NAME_LEN);
371     if (!path) {
372         return false;
373     }
374 
375     MUSL_LOGE("[gwp_asan]: %{public}d %{public}s gwp_asan initializing.\n", getpid(), path);
376     init_gwp_asan((void*)&gwp_asan_option);
377     gwp_asan_initialized = true;
378     MUSL_LOGE("[gwp_asan]: %{public}d %{public}s gwp_asan initialized.\n", getpid(), path);
379     return true;
380 }
init_gwp_asan_by_libc(bool force_init)381 bool init_gwp_asan_by_libc(bool force_init)
382 {
383     char buf[GWP_ASAN_NAME_LEN];
384     char *path = get_process_short_name(buf, GWP_ASAN_NAME_LEN);
385     if (!path) {
386         return false;
387     }
388     // We don't sample appspawn, and the chaild process decides whether to sample or not.
389     if (strcmp(path, "appspawn") == 0 || strcmp(path, "sh") == 0) {
390         return false;
391     }
392     return may_init_gwp_asan(force_init);
393 }
394 
libc_gwp_asan_malloc(size_t bytes)395 void* libc_gwp_asan_malloc(size_t bytes)
396 {
397     if (GWP_ASAN_PREDICT_TRUE(!gwp_asan_initialized)) {
398         return MuslFunc(malloc)(bytes);
399     }
400     void *res = NULL;
401     if (GWP_ASAN_PREDICT_FALSE(force_sample_alloctor || gwp_asan_should_sample())) {
402         res = gwp_asan_malloc(bytes);
403         if (res != NULL) {
404             return res;
405         }
406     }
407     return MuslFunc(malloc)(bytes);
408 }
409 
libc_gwp_asan_calloc(size_t nmemb,size_t size)410 void* libc_gwp_asan_calloc(size_t nmemb, size_t size)
411 {
412     if (GWP_ASAN_PREDICT_TRUE(!gwp_asan_initialized)) {
413         return MuslFunc(calloc)(nmemb, size);
414     }
415 
416     if (GWP_ASAN_PREDICT_FALSE(force_sample_alloctor || gwp_asan_should_sample())) {
417         size_t total_bytes;
418         void* result = NULL;
419         if (!__builtin_mul_overflow(nmemb, size, &total_bytes)) {
420             GWP_ASAN_LOGD("[gwp_asan]: call gwp_asan_malloc nmemb:%{public}d size:%{public}d.\n", nmemb, size);
421             result = gwp_asan_malloc(total_bytes);
422             if (result != NULL) {
423                 return result;
424             }
425         }
426     }
427 
428     return MuslFunc(calloc)(nmemb, size);
429 }
430 
libc_gwp_asan_realloc(void * ptr,size_t size)431 void* libc_gwp_asan_realloc(void *ptr, size_t size)
432 {
433     if (GWP_ASAN_PREDICT_TRUE(!gwp_asan_initialized)) {
434         return MuslFunc(realloc)(ptr, size);
435     }
436 
437     if (GWP_ASAN_PREDICT_FALSE(gwp_asan_pointer_is_mine(ptr))) {
438         GWP_ASAN_LOGD("[gwp_asan]: call gwp_asan_malloc  ptr:%{public}p size:%{public}d.\n", ptr, size);
439         void* new_addr = gwp_asan_malloc(size);
440         if (new_addr != NULL) {
441             size_t old_size = gwp_asan_get_size(ptr);
442             memcpy(new_addr, ptr, (size < old_size) ? size : old_size);
443             gwp_asan_free(ptr);
444             return new_addr;
445         } else {
446             return 0;
447         }
448     }
449     return MuslFunc(realloc)(ptr, size);
450 }
451 
libc_gwp_asan_free(void * addr)452 void libc_gwp_asan_free(void *addr)
453 {
454     if (GWP_ASAN_PREDICT_TRUE(!gwp_asan_initialized)) {
455         return MuslFunc(free)(addr);
456     }
457     if (GWP_ASAN_PREDICT_FALSE(gwp_asan_pointer_is_mine(addr))) {
458         return gwp_asan_free(addr);
459     }
460     return MuslFunc(free)(addr);
461 }
462 
libc_gwp_asan_malloc_usable_size(void * addr)463 size_t libc_gwp_asan_malloc_usable_size(void *addr)
464 {
465     if (GWP_ASAN_PREDICT_TRUE(!gwp_asan_initialized)) {
466         return MuslMalloc(malloc_usable_size)(addr);
467     }
468     if (GWP_ASAN_PREDICT_FALSE(gwp_asan_pointer_is_mine(addr))) {
469         return gwp_asan_get_size(addr);
470     }
471     return MuslMalloc(malloc_usable_size)(addr);
472 }
473 
libc_gwp_asan_malloc_iterate(void * base,size_t size,void (* callback)(uintptr_t base,size_t size,void * arg),void * arg)474 void libc_gwp_asan_malloc_iterate(void *base, size_t size,
475                              void (*callback)(uintptr_t base, size_t size, void *arg), void *arg)
476 {
477     if (GWP_ASAN_PREDICT_TRUE(!gwp_asan_initialized)) {
478         return;
479     }
480     if (GWP_ASAN_PREDICT_FALSE(gwp_asan_pointer_is_mine(base))) {
481         return gwp_asan_iterate(base, size, callback, arg);
482     }
483     return;
484 }
485 
libc_gwp_asan_malloc_disable()486 void libc_gwp_asan_malloc_disable()
487 {
488     if (GWP_ASAN_PREDICT_TRUE(!gwp_asan_initialized)) {
489         return;
490     }
491 
492     return gwp_asan_disable();
493 }
494 
libc_gwp_asan_malloc_enable()495 void libc_gwp_asan_malloc_enable()
496 {
497     if (GWP_ASAN_PREDICT_TRUE(!gwp_asan_initialized)) {
498         return;
499     }
500 
501     return gwp_asan_enable();
502 }
503 
libc_gwp_asan_has_free_mem()504 bool libc_gwp_asan_has_free_mem()
505 {
506     if (GWP_ASAN_PREDICT_TRUE(!gwp_asan_initialized)) {
507         return false;
508     }
509     gwp_asan_disable();
510     int res = gwp_asan_has_free_mem();
511     gwp_asan_enable();
512     return res;
513 }
libc_gwp_asan_ptr_is_mine(void * addr)514 bool libc_gwp_asan_ptr_is_mine(void *addr)
515 {
516     if (GWP_ASAN_PREDICT_TRUE(!gwp_asan_initialized)) {
517         return false;
518     }
519 
520     return gwp_asan_pointer_is_mine(addr);
521 }
522 
523 #endif
524