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 if (g_buffer != NULL) {
43 int unmapRes = munmap(g_buffer, g_end);
44 if (unmapRes == -1) {
45 MUSL_LOGW("[ohos_dfx_log] munmap g_buffer failed: %{public}d %{public}s:\n %{public}s\n",
46 errno, strerror(errno), str);
47 }
48 }
49 g_buffer = NULL;
50 g_offset = 0;
51 g_end = DEFAULT_STRING_SIZE;
52 }
53
load_asan_logger()54 bool load_asan_logger()
55 {
56 if (g_dfxLibHandler == NULL) {
57 g_dfxLibHandler = dlopen(DFX_LOG_LIB, RTLD_LAZY);
58 if (g_dfxLibHandler == NULL) {
59 MUSL_LOGW("[ohos_dfx_log] dlopen %{public}s failed!\n", DFX_LOG_LIB);
60 return false;
61 }
62 }
63 if (g_dfxLogPtr == NULL) {
64 *(void **)(&g_dfxLogPtr) = dlsym(g_dfxLibHandler, DFX_LOG_INTERFACE);
65 if (g_dfxLogPtr == NULL) {
66 MUSL_LOGW("[ohos_dfx_log] dlsym %{public}s, failed!\n", DFX_LOG_INTERFACE);
67 return false;
68 }
69 }
70 return true;
71 }
72
73 /*
74 * This function is not async-signal-safe
75 * Don't use it in signal handler or in child process that is forked from any other process
76 * Don't use it during initialization
77 * Don't use it before execve
78 */
write_to_dfx(const char * str,const char * path)79 static void write_to_dfx(const char *str, const char *path)
80 {
81 if (g_dfxLogPtr != NULL) {
82 g_dfxLogPtr(g_buffer, g_offset, (char *)path);
83 return;
84 }
85
86 if (!g_dl_inited) {
87 return;
88 }
89
90 if (!load_asan_logger()) {
91 return;
92 }
93 g_dfxLogPtr(g_buffer, g_offset, (char *)path);
94 }
95
96 /*
97 * This function is exclusively for LLVM Sanitizers to flush logs to disk
98 * Don't use it for other purposes
99 * This function is not async-signal-safe
100 * Don't use it in signal handler or in child process that is forked from any other process
101 * Don't use it during initialization
102 * Don't use it before execve
103 */
ohos_dfx_log(const char * str,const char * path)104 int ohos_dfx_log(const char *str, const char *path)
105 {
106 if (g_global_destroyed || str == NULL) {
107 return 0;
108 }
109
110 pthread_mutex_lock(&g_muslLogMutex);
111 if (g_buffer == NULL) {
112 g_buffer = mmap(NULL, DEFAULT_STRING_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
113 if (g_buffer == MAP_FAILED) {
114 MUSL_LOGW("[ohos_dfx_log] mmap g_buffer failed: %{public}d %{public}s:\n %{public}s\n",
115 errno, strerror(errno), str);
116 pthread_mutex_unlock(&g_muslLogMutex);
117 return 0;
118 }
119 }
120
121 size_t str_len = strlen(str);
122 size_t new_end = g_offset + str_len + 1;
123 if (new_end > g_end) {
124 char *new_buffer = mremap(g_buffer, g_end, g_end * 2, MREMAP_MAYMOVE);
125 if (new_buffer == MAP_FAILED) {
126 MUSL_LOGW("[ohos_dfx_log] mremap new_buffer failed: %{public}d %{public}s:\n %{public}s\n",
127 errno, strerror(errno), str);
128 pthread_mutex_unlock(&g_muslLogMutex);
129 return 0;
130 }
131 g_end = g_end * 2;
132 g_buffer = new_buffer;
133 MUSL_LOGW("[ohos_dfx_log] g_end expand to %{public}lu\n", g_end);
134 }
135
136 strcpy(g_buffer + g_offset, str);
137 g_offset += str_len;
138
139 if (!(strstr(str, "End Hwasan report") || strstr(str, "End Asan report") ||
140 strstr(str, "End Tsan report") || strstr(str, "End Ubsan report") ||
141 strstr(str, "End CFI report"))) {
142 pthread_mutex_unlock(&g_muslLogMutex);
143 return 0;
144 }
145
146 write_to_dfx(str, path);
147 buffer_clean_up(str);
148 pthread_mutex_unlock(&g_muslLogMutex);
149
150 return 0;
151 }
152
musl_log(const char * fmt,...)153 int musl_log(const char *fmt, ...)
154 {
155 int ret;
156 va_list ap;
157 va_start(ap, fmt);
158 ret = HiLogAdapterPrintArgs(MUSL_LOG_TYPE, LOG_INFO, MUSL_LOG_DOMAIN, MUSL_LOG_TAG, fmt, ap);
159 va_end(ap);
160 return ret;
161 }