1 /*
2 * Copyright (c) Huawei Technologies Co., Ltd. 2024-2025. All rights reserved.
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 #define _GNU_SOURCE
17 #include <errno.h>
18 #include <fcntl.h>
19 #include <dlfcn.h>
20 #include <unistd.h>
21 #include <stdio.h>
22 #include <string.h>
23 #include <pthread.h>
24 #include <sys/mman.h>
25
26 #include "musl_log.h"
27
28 #define DEFAULT_STRING_SIZE 409600
29 #define DFX_LOG_LIB "libasan_logger.z.so"
30 #define DFX_LOG_INTERFACE "WriteSanitizerLog"
31 static void (*g_dfxLogPtr)(char*, size_t, char*);
32 static void* g_dfxLibHandler = NULL;
33 static pthread_mutex_t g_muslLogMutex = PTHREAD_MUTEX_INITIALIZER;
34 extern bool g_dl_inited; // flag indicates musl ldso initialization completed
35 extern bool g_global_destroyed; // flag indicates global variables have been destroyed
36 char *g_buffer = NULL; // an area of memory used to cache each line of sanitizer logs
37 size_t g_offset = 0; // the size of logs that have been cached to g_buffer
38 size_t g_end = DEFAULT_STRING_SIZE; // the size of g_buffer
39
buffer_clean_up(const char * str)40 static void buffer_clean_up(const char *str)
41 {
42 int unmapRes = munmap(g_buffer, g_end);
43 if (unmapRes == -1) {
44 MUSL_LOGE("[ohos_dfx_log] munmap g_buffer failed: %{public}d %{public}s:\n %{public}s\n",
45 errno, strerror(errno), str);
46 }
47 g_buffer = NULL;
48 g_offset = 0;
49 g_end = DEFAULT_STRING_SIZE;
50 }
51
load_asan_logger()52 bool load_asan_logger()
53 {
54 if (g_dfxLibHandler == NULL) {
55 g_dfxLibHandler = dlopen(DFX_LOG_LIB, RTLD_LAZY);
56 if (g_dfxLibHandler == NULL) {
57 MUSL_LOGE("[ohos_dfx_log] dlopen %{public}s failed!\n", DFX_LOG_LIB);
58 return false;
59 }
60 }
61 if (g_dfxLogPtr == NULL) {
62 *(void **)(&g_dfxLogPtr) = dlsym(g_dfxLibHandler, DFX_LOG_INTERFACE);
63 if (g_dfxLogPtr == NULL) {
64 MUSL_LOGE("[ohos_dfx_log] dlsym %{public}s, failed!\n", DFX_LOG_INTERFACE);
65 return false;
66 }
67 }
68 return true;
69 }
70
71 /*
72 * This function is not async-signal-safe
73 * Don't use it in signal handler or in child process that is forked from any other process
74 * Don't use it during initialization
75 * Don't use it before execve
76 */
write_to_dfx(const char * str,const char * path)77 static void write_to_dfx(const char *str, const char *path)
78 {
79 if (g_dfxLogPtr != NULL) {
80 g_dfxLogPtr(g_buffer, g_offset, (char *)path);
81 return;
82 }
83
84 if (!g_dl_inited) {
85 return;
86 }
87
88 if (!load_asan_logger()) {
89 return;
90 }
91 g_dfxLogPtr(g_buffer, g_offset, (char *)path);
92 }
93
94 /*
95 * This function is exclusively for LLVM Sanitizers to flush logs to disk
96 * Don't use it for other purposes
97 * This function is not async-signal-safe
98 * Don't use it in signal handler or in child process that is forked from any other process
99 * Don't use it during initialization
100 * Don't use it before execve
101 */
ohos_dfx_log(const char * str,const char * path)102 int ohos_dfx_log(const char *str, const char *path)
103 {
104 if (g_global_destroyed) {
105 return 0;
106 }
107
108 pthread_mutex_lock(&g_muslLogMutex);
109 if (g_buffer == NULL) {
110 g_buffer = mmap(NULL, DEFAULT_STRING_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
111 if (g_buffer == MAP_FAILED) {
112 MUSL_LOGE("[ohos_dfx_log] mmap g_buffer failed: %{public}d %{public}s:\n %{public}s\n",
113 errno, strerror(errno), str);
114 pthread_mutex_unlock(&g_muslLogMutex);
115 return 0;
116 }
117 }
118
119 size_t new_end = g_offset + strlen(str);
120 if (new_end > g_end) {
121 char *new_buffer = mremap(g_buffer, g_end, g_end * 2, MREMAP_MAYMOVE);
122 if (new_buffer == MAP_FAILED) {
123 MUSL_LOGE("[ohos_dfx_log] mremap new_buffer failed: %{public}d %{public}s:\n %{public}s\n",
124 errno, strerror(errno), str);
125 pthread_mutex_unlock(&g_muslLogMutex);
126 return 0;
127 }
128 g_end = g_end * 2;
129 g_buffer = new_buffer;
130 MUSL_LOGE("[ohos_dfx_log] g_end expand to %{public}lu\n", g_end);
131 }
132
133 strcpy(g_buffer + g_offset, str);
134 g_offset += strlen(str);
135
136 if (!(strstr(str, "End Hwasan report") || strstr(str, "End Asan report") ||
137 strstr(str, "End Tsan report") || strstr(str, "End Ubsan report") ||
138 strstr(str, "End CFI report"))) {
139 pthread_mutex_unlock(&g_muslLogMutex);
140 return 0;
141 }
142
143 write_to_dfx(str, path);
144 buffer_clean_up(str);
145 pthread_mutex_unlock(&g_muslLogMutex);
146
147 return 0;
148 }
149
musl_log(const char * fmt,...)150 int musl_log(const char *fmt, ...)
151 {
152 int ret;
153 va_list ap;
154 va_start(ap, fmt);
155 ret = HiLogAdapterPrintArgs(MUSL_LOG_TYPE, LOG_INFO, MUSL_LOG_DOMAIN, MUSL_LOG_TAG, fmt, ap);
156 va_end(ap);
157 return ret;
158 }