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