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