1 /*
2 * Copyright (C) 2024 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 <atomic.h>
17 #include <errno.h>
18 #include <string.h>
19 #include <stdint.h>
20 #include <sys/cdefs.h>
21 #include <sys/resource.h>
22 #include <sys/mman.h>
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <stdarg.h>
26 #include <unistd.h>
27 #include <stdbool.h>
28
29
30 #include "musl_log.h"
31 #include "musl_fdsan.h"
32 #include "libc.h"
33 #include "pthread_impl.h"
34 #include "hilog_adapter.h"
35 #include <info/fatal_message.h>
36
37 #ifdef OHOS_ENABLE_PARAMETER
38 #include "sys_param.h"
39 #define MUSL_FDSAN_ERROR(fmt, ...) ((void)HiLogAdapterPrint(MUSL_LOG_TYPE, LOG_ERROR, MUSL_LOG_DOMAIN, "MUSL-FDSAN", \
40 fmt, __VA_ARGS__))
41 #else
42 #define MUSL_FDSAN_ERROR(fmt, ...)
43 #endif
44
45 #define MAX_DEBUG_MSG_LEN 1024
46 #define ZERO (0)
47 #define ONE (1)
48 #define NEGATIVE_ONE (-1)
49
50 const char *fdsan_parameter_name = "musl.debug.fdsan";
51 #define ALIGN(x,y) ((x)+(y)-1 & -(y))
52 extern int __close(int fd);
53
54 static struct FdTable g_fd_table = {
55 .error_level = FDSAN_ERROR_LEVEL_WARN_ALWAYS,
56 .overflow = NULL,
57 };
58
__get_fdtable()59 struct FdTable* __get_fdtable()
60 {
61 return &g_fd_table;
62 }
63
get_fd_entry(size_t idx)64 static struct FdEntry* get_fd_entry(size_t idx)
65 {
66 struct FdEntry *entries = __get_fdtable()->entries;
67 if (idx < FdTableSize) {
68 return &entries[idx];
69 }
70 // Try to create the overflow table ourselves.
71 struct FdTableOverflow* local_overflow = atomic_load(&__get_fdtable()->overflow);
72 if (__predict_false(!local_overflow)) {
73 struct rlimit rlim = { .rlim_max = 32768 };
74 getrlimit(RLIMIT_NOFILE, &rlim);
75 rlim_t max = rlim.rlim_max;
76
77 if (max == RLIM_INFINITY) {
78 max = 32768; // Max fd size
79 }
80
81 if (idx > max) {
82 return NULL;
83 }
84 size_t required_count = max - FdTableSize;
85 size_t required_size = sizeof(struct FdTableOverflow) + required_count * sizeof(struct FdEntry);
86 size_t aligned_size = ALIGN(required_size, PAGE_SIZE);
87 size_t aligned_count = (aligned_size - sizeof(struct FdTableOverflow)) / sizeof(struct FdEntry);
88 void* allocation =
89 mmap(NULL, aligned_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
90 if (allocation == MAP_FAILED) {
91 MUSL_LOGW("fdsan: mmap failed errno=%d", errno);
92 }
93
94 struct FdTableOverflow* new_overflow = (struct FdTableOverflow*)(allocation);
95 new_overflow->len = aligned_count;
96
97 if (atomic_compare_exchange_strong(&__get_fdtable()->overflow, &local_overflow, new_overflow)) {
98 local_overflow = new_overflow;
99 } else {
100 // Another thread had mmaped.
101 munmap(allocation, aligned_size);
102 }
103 }
104
105 size_t offset = idx - FdTableSize;
106 if (local_overflow->len <= offset) {
107 return NULL;
108 }
109 return &local_overflow->entries[offset];
110 }
111
__init_fdsan()112 void __init_fdsan()
113 {
114 enum fdsan_error_level default_level = FDSAN_ERROR_LEVEL_WARN_ALWAYS;
115 fdsan_set_error_level_from_param(default_level);
116 }
117
118 // Exposed to the platform to allow crash_dump to print out the fd table.
fdsan_get_fd_table()119 void* fdsan_get_fd_table()
120 {
121 return __get_fdtable();
122 }
123
GetFdEntry(int fd)124 static struct FdEntry* GetFdEntry(int fd)
125 {
126 if (fd < 0) {
127 return NULL;
128 }
129 return get_fd_entry(fd);
130 }
131
132 /*
133 * @brief Trigger the signal to grab the stack and save it on site,
134 * and the msg will be recorded in the fault log
135 * Will wait for the signal handle to grab the stack until it is completed or exits abnormally
136 * @param msg The debug message
137 */
save_debug_message(const char * msg)138 static void save_debug_message(const char *msg)
139 {
140 if (msg == NULL) {
141 MUSL_LOGI("debug msg is NULL");
142 return;
143 }
144
145 const int NUMBER_ONE_THOUSAND = 1000; // 1000 : second to millisecond convert ratio
146 const int NUMBER_ONE_MILLION = 1000000; // 1000000 : nanosecond to millisecond convert ratio
147 struct timespec ts;
148 (void)clock_gettime(CLOCK_REALTIME, &ts);
149
150 debug_msg_t debug_message = {0, NULL};
151 debug_message.timestamp = ((uint64_t)ts.tv_sec * NUMBER_ONE_THOUSAND) +
152 (((uint64_t)ts.tv_sec) / NUMBER_ONE_MILLION);
153 debug_message.msg = msg;
154
155 const int signo = 42; // Custom stack capture signal and leak reuse
156 const int si_code = 1; // When si_signo = 42, use si_code = 1 mark the event as fdsan
157 siginfo_t info;
158 info.si_signo = signo;
159 info.si_code = si_code;
160 info.si_value.sival_ptr = &debug_message;
161 if (syscall(__NR_rt_tgsigqueueinfo, getpid(), __syscall(SYS_gettid), signo, &info) == -1) {
162 MUSL_LOGW("send failed errno=%{public}d", errno);
163 }
164 }
165
fdsan_error(struct FdEntry * fde,const char * fmt,...)166 static void fdsan_error(struct FdEntry* fde, const char* fmt, ...)
167 {
168 struct FdTable* fd_table = __get_fdtable();
169
170 enum fdsan_error_level error_level = atomic_load(&fd_table->error_level);
171 if (error_level == FDSAN_ERROR_LEVEL_DISABLED) {
172 return;
173 }
174 char msg[MAX_DEBUG_MSG_LEN] = {0};
175 va_list va;
176 va_start(va, fmt);
177 (void)vsnprintf(msg, sizeof(msg) - 1, fmt, va);
178 va_end(va);
179 switch (error_level) {
180 case FDSAN_ERROR_LEVEL_WARN_ONCE:
181 MUSL_FDSAN_ERROR("%{public}s", msg);
182 atomic_compare_exchange_strong(&fd_table->error_level, &error_level, FDSAN_ERROR_LEVEL_DISABLED);
183 case FDSAN_ERROR_LEVEL_WARN_ALWAYS: {
184 MUSL_FDSAN_ERROR("%{public}s", msg);
185 char value = ZERO;
186 if (fde == NULL || (!atomic_load(&fde->signal_flag)
187 && atomic_compare_exchange_strong(&fde->signal_flag, &value, ONE))) {
188 save_debug_message(msg);
189 }
190 break;
191 }
192 case FDSAN_ERROR_LEVEL_FATAL:
193 MUSL_FDSAN_ERROR("%{public}s", msg);
194 abort();
195 case FDSAN_ERROR_LEVEL_DISABLED:
196 break;
197 }
198 }
199
fdsan_create_owner_tag(enum fdsan_owner_type type,uint64_t tag)200 uint64_t fdsan_create_owner_tag(enum fdsan_owner_type type, uint64_t tag)
201 {
202 if (tag == 0) {
203 return 0;
204 }
205
206 if (__predict_false((type & 0xff) != type)) {
207 MUSL_LOGE("invalid fdsan_owner_type value: %x", type);
208 abort();
209 }
210
211 uint64_t result = (uint64_t)(type) << 56;
212 uint64_t mask = ((uint64_t)(1) << 56) - 1;
213 result |= tag & mask;
214 return result;
215 }
216
fdsan_get_tag_type(uint64_t tag)217 const char* fdsan_get_tag_type(uint64_t tag)
218 {
219 uint64_t type = tag >> 56;
220 uint64_t high_bits = tag >> 48;
221 switch (type) {
222 case FDSAN_OWNER_TYPE_FILE:
223 return "FILE*";
224 case FDSAN_OWNER_TYPE_DIRECTORY:
225 return "DIR*";
226 case FDSAN_OWNER_TYPE_UNIQUE_FD:
227 return "unique_fd";
228 case FDSAN_OWNER_TYPE_ZIP_ARCHIVE:
229 return "ZipArchive";
230 case FDSAN_OWNER_TYPE_MAX:
231 if (high_bits == (1 << 16) - 1) {
232 return "native object of unknown type";
233 }
234 return "object of unknown type";
235 case FDSAN_OWNER_TYPE_DEFAULT:
236 default:
237 return "native object of unknown type";
238 }
239 }
240
fdsan_get_tag_value(uint64_t tag)241 uint64_t fdsan_get_tag_value(uint64_t tag)
242 {
243 // Lop off the most significant byte and sign extend.
244 return (uint64_t)((int64_t)(tag << 8) >> 8);
245 }
246
fdsan_exchange_owner_tag(int fd,uint64_t expected_tag,uint64_t new_tag)247 void fdsan_exchange_owner_tag(int fd, uint64_t expected_tag, uint64_t new_tag)
248 {
249 if (__pthread_self()->by_vfork) {
250 return;
251 }
252 struct FdEntry* fde = GetFdEntry(fd);
253 if (!fde) {
254 return;
255 }
256
257 uint64_t tag = expected_tag;
258 if (!atomic_compare_exchange_strong(&fde->close_tag, &tag, new_tag)) {
259 if (expected_tag && tag) {
260 fdsan_error(fde, "failed to exchange ownership of file descriptor: fd %d, "\
261 "was owned by %s 0x%016lx, "\
262 "was expected to be owned by %s 0x%016lx",
263 fd, fdsan_get_tag_type(tag), fdsan_get_tag_value(tag),
264 fdsan_get_tag_type(expected_tag), fdsan_get_tag_value(expected_tag));
265 } else if (expected_tag && !tag) {
266 fdsan_error(fde, "failed to exchange ownership of file descriptor: fd %d is unowned, "\
267 "was expected to be owned by %s 0x%016lx",
268 fd, fdsan_get_tag_type(expected_tag), fdsan_get_tag_value(expected_tag));
269 } else if (!expected_tag && tag) {
270 fdsan_error(fde, "failed to exchange ownership of file descriptor: fd %d, "\
271 "was owned by %s 0x%016lx, was expected to be unowned",
272 fd, fdsan_get_tag_type(tag), fdsan_get_tag_value(tag));
273 } else if (!expected_tag && !tag) {
274 // expected == actual == 0 but cas failed?
275 MUSL_LOGW("fdsan compare and set failed unexpectedly while exchanging owner tag");
276 }
277 }
278 }
279
fdsan_close_with_tag(int fd,uint64_t expected_tag)280 int fdsan_close_with_tag(int fd, uint64_t expected_tag)
281 {
282 if (__pthread_self()->by_vfork) {
283 return __close(fd);
284 }
285 struct FdEntry* fde = GetFdEntry(fd);
286 if (!fde) {
287 return __close(fd);
288 }
289
290 uint64_t tag = expected_tag;
291 if (!atomic_compare_exchange_strong(&fde->close_tag, &tag, 0)) {
292 const char* expected_type = fdsan_get_tag_type(expected_tag);
293 uint64_t expected_owner = fdsan_get_tag_value(expected_tag);
294 const char* actual_type = fdsan_get_tag_type(tag);
295 uint64_t actual_owner = fdsan_get_tag_value(tag);
296 if (expected_tag && tag) {
297 fdsan_error(fde, "attempted to close file descriptor %d, "\
298 "expected to be owned by %s 0x%016lx, "\
299 "actually owned by %s 0x%016lx",
300 fd, expected_type, expected_owner, actual_type, actual_owner);
301 } else if (expected_tag && !tag) {
302 fdsan_error(fde, "attempted to close file descriptor %d,"\
303 "expected to be owned by %s 0x%016lx, actually unowned", \
304 fd, expected_type, expected_owner);
305 } else if (!expected_tag && tag) {
306 fdsan_error(fde, "attempted to close file descriptor %d, "\
307 "expected to be unowned, actually owned by %s 0x%016lx", \
308 fd, actual_type, actual_owner);
309 } else if (!expected_tag && !tag) {
310 // expected == actual == 0 but cas failed?
311 MUSL_LOGE("fdsan compare and set failed unexpectedly while closing");
312 abort();
313 }
314 }
315
316 int rc = __close(fd);
317 // If we were expecting to close with a tag, abort on EBADF.
318 if (expected_tag && rc == -1 && errno == EBADF) {
319 fdsan_error(NULL, "EBADF: close failed for fd %d with expected tag: 0x%016lx", fd, expected_tag);
320 }
321 return rc;
322 }
323
fdsan_get_owner_tag(int fd)324 uint64_t fdsan_get_owner_tag(int fd)
325 {
326 struct FdEntry* fde = GetFdEntry(fd);
327 if (!fde) {
328 return 0;
329 }
330 return fde->close_tag;
331 }
332
fdsan_get_error_level()333 enum fdsan_error_level fdsan_get_error_level()
334 {
335 return __get_fdtable()->error_level;
336 }
337
fdsan_set_error_level(enum fdsan_error_level new_level)338 enum fdsan_error_level fdsan_set_error_level(enum fdsan_error_level new_level)
339 {
340 if (__pthread_self()->by_vfork) {
341 return fdsan_get_error_level();
342 }
343
344 return atomic_exchange(&__get_fdtable()->error_level, new_level);
345 }
346
fdsan_set_error_level_from_param(enum fdsan_error_level default_level)347 enum fdsan_error_level fdsan_set_error_level_from_param(enum fdsan_error_level default_level)
348 {
349 #ifdef OHOS_ENABLE_PARAMETER
350 static CachedHandle param_handler = NULL;
351 if (param_handler == NULL) {
352 param_handler = CachedParameterCreate(fdsan_parameter_name, "0");
353 }
354 const char *param_value = CachedParameterGet(param_handler);
355 if (param_value == NULL) {
356 return fdsan_set_error_level(default_level);
357 } else if (strcmp(param_value, "fatal") == 0) {
358 return fdsan_set_error_level(FDSAN_ERROR_LEVEL_FATAL);
359 } else if (strcmp(param_value, "warn") == 0) {
360 return fdsan_set_error_level(FDSAN_ERROR_LEVEL_WARN_ALWAYS);
361 } else if (strcmp(param_value, "warn_once") == 0) {
362 return fdsan_set_error_level(FDSAN_ERROR_LEVEL_WARN_ONCE);
363 } else {
364 MUSL_LOGD("[fdsan] musl.debug.fdsan set to unknown value '%{public}s'", param_value);
365 }
366 #endif
367 return fdsan_set_error_level(default_level);
368 }
369
close(int fd)370 int close(int fd)
371 {
372 int rc = fdsan_close_with_tag(fd, 0);
373 if (rc == -1 && errno == EINTR) {
374 return 0;
375 }
376 return rc;
377 }
378