• 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 
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 
41 const char *fdsan_parameter_name = "musl.debug.fdsan";
42 #define ALIGN(x,y) ((x)+(y)-1 & -(y))
43 extern int __close(int fd);
44 
45 static struct FdTable g_fd_table = {
46 	.error_level = FDSAN_ERROR_LEVEL_WARN_ALWAYS,
47 	.overflow = NULL,
48 };
49 
__get_fdtable()50 struct FdTable* __get_fdtable()
51 {
52 	return &g_fd_table;
53 }
54 
get_fd_entry(size_t idx)55 static 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 
__init_fdsan()103 void __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.
fdsan_get_fd_table()110 void* fdsan_get_fd_table()
111 {
112 	return __get_fdtable();
113 }
114 
GetFdEntry(int fd)115 static struct FdEntry* GetFdEntry(int fd)
116 {
117 	if (fd < 0) {
118 		return NULL;
119 	}
120 	return get_fd_entry(fd);
121 }
122 
fdsan_error(const char * fmt,...)123 static 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 
fdsan_create_owner_tag(enum fdsan_owner_type type,uint64_t tag)149 uint64_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 
fdsan_get_tag_type(uint64_t tag)166 const 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 
fdsan_get_tag_value(uint64_t tag)190 uint64_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 
fdsan_exchange_owner_tag(int fd,uint64_t expected_tag,uint64_t new_tag)196 void 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 
fdsan_close_with_tag(int fd,uint64_t expected_tag)229 int 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 
fdsan_get_owner_tag(int fd)273 uint64_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 
fdsan_get_error_level()282 enum fdsan_error_level fdsan_get_error_level()
283 {
284 	return __get_fdtable()->error_level;
285 }
286 
fdsan_set_error_level(enum fdsan_error_level new_level)287 enum 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 
fdsan_set_error_level_from_param(enum fdsan_error_level default_level)296 enum 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 
close(int fd)319 int 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 }