• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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