]> git.saurik.com Git - apple/xnu.git/blob - libkern/os/log_encode.h
xnu-7195.81.3.tar.gz
[apple/xnu.git] / libkern / os / log_encode.h
1 /*
2 * Copyright (c) 2015-2016 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 #ifndef log_encode_h
25 #define log_encode_h
26
27 #include "log_encode_types.h"
28 #include <sys/param.h>
29
30 #if __has_feature(ptrauth_calls)
31 #include <mach/vm_param.h>
32 #include <ptrauth.h>
33 #endif /* __has_feature(ptrauth_calls) */
34
35 #ifdef KERNEL
36 #define isdigit(ch) (((ch) >= '0') && ((ch) <= '9'))
37 extern boolean_t doprnt_hide_pointers;
38 #endif
39
40 static bool
41 _encode_data(os_log_buffer_value_t content, const void *arg, size_t arg_len, os_log_buffer_context_t context)
42 {
43 struct os_log_arginfo_s arginfo;
44 void *databuf;
45
46 arg_len = MIN(arg_len, UINT16_MAX);
47
48 if (content->flags & OS_LOG_CONTENT_FLAG_PRIVATE) {
49 databuf = context->privdata + context->privdata_off;
50 arginfo.length = MIN((uint16_t)arg_len, (context->privdata_sz - context->privdata_off));
51 arginfo.offset = context->privdata_off;
52 } else {
53 databuf = context->pubdata + context->pubdata_off;
54 arginfo.length = MIN((uint16_t)arg_len, (context->pubdata_sz - context->pubdata_off));
55 arginfo.offset = context->pubdata_off;
56 }
57
58 if (context->arg_content_sz > 0) {
59 arginfo.length = MIN((uint16_t)context->arg_content_sz, arginfo.length);
60 }
61
62 memcpy(content->value, &arginfo, sizeof(arginfo));
63 content->size = sizeof(arginfo);
64
65 if (arginfo.length) {
66 if (content->type == OS_LOG_BUFFER_VALUE_TYPE_STRING
67 #ifndef KERNEL
68 || content->type == OS_LOG_BUFFER_VALUE_TYPE_OBJECT
69 #endif
70 ) {
71 strlcpy(databuf, arg, arginfo.length);
72 } else {
73 memcpy(databuf, arg, arginfo.length);
74 }
75 }
76
77 if (content->flags & OS_LOG_CONTENT_FLAG_PRIVATE) {
78 context->privdata_off += arginfo.length;
79 } else {
80 context->pubdata_off += arginfo.length;
81 }
82
83 context->content_off += sizeof(*content) + content->size;
84 context->arg_content_sz = 0;
85
86 return true;
87 }
88
89 #ifndef KERNEL
90 static void
91 _os_log_parse_annotated(char *annotated, const char **visibility, const char **library, const char **type)
92 {
93 char *values[3] = { NULL };
94 int cnt = 0;
95 int idx = 0;
96
97 for (; cnt < 3;) {
98 char *token = strsep(&annotated, ", {}");
99 if (token == NULL) {
100 break;
101 }
102
103 if (*token == '\0') {
104 continue;
105 }
106
107 values[cnt++] = token;
108 }
109
110 if ((cnt > 0) && (!strcmp(values[0], "public") || !strcmp(values[0], "private"))) {
111 if (visibility != NULL) {
112 (*visibility) = values[0];
113 }
114
115 idx++;
116 }
117
118 if (idx < cnt && (library != NULL) && (type != NULL)) {
119 char *decoder = values[idx];
120
121 for (cnt = 0; cnt < 3;) {
122 char *token = strsep(&decoder, ": {}");
123 if (token == NULL) {
124 break;
125 }
126
127 if (*token == '\0') {
128 continue;
129 }
130
131 values[cnt++] = token;
132 }
133
134 if (cnt == 2) {
135 (*library) = values[0];
136 (*type) = values[1];
137 }
138
139 if (cnt == 1) {
140 (*library) = "builtin";
141 (*type) = values[0];
142 }
143 }
144 }
145 #endif /* !KERNEL */
146
147 OS_ALWAYS_INLINE
148 static inline bool
149 _os_log_encode_arg(void *arg, size_t arg_len, os_log_value_type_t ctype, bool is_private, os_log_buffer_context_t context)
150 {
151 os_log_buffer_value_t content = (os_log_buffer_value_t) &context->buffer->content[context->content_off];
152 size_t content_sz = sizeof(*content) + arg_len;
153 char tempString[OS_LOG_BUFFER_MAX_SIZE] = {};
154 #ifndef KERNEL
155 bool obj_private = true;
156 #endif
157
158 #ifdef KERNEL
159 /* scrub kernel pointers */
160 if (doprnt_hide_pointers &&
161 ctype == OS_LOG_BUFFER_VALUE_TYPE_SCALAR &&
162 arg_len >= sizeof(void *)) {
163 unsigned long long value = 0;
164 memcpy(&value, arg, arg_len);
165
166 #if __has_feature(ptrauth_calls)
167 /**
168 * Strip out the pointer authentication code before
169 * checking whether the pointer is a kernel address.
170 */
171 value = (unsigned long long)VM_KERNEL_STRIP_PTR(value);
172 #endif /* __has_feature(ptrauth_calls) */
173
174 if (value >= VM_MIN_KERNEL_AND_KEXT_ADDRESS && value <= VM_MAX_KERNEL_ADDRESS) {
175 is_private = true;
176 bzero(arg, arg_len);
177 }
178 }
179 #endif
180
181 content->type = ctype;
182 content->flags = (is_private ? OS_LOG_CONTENT_FLAG_PRIVATE : 0);
183
184 #ifndef KERNEL
185 if (context->annotated != NULL) {
186 const char *visibility = NULL;
187
188 _os_log_parse_annotated(context->annotated, &visibility, NULL, NULL);
189 if (visibility) {
190 if (!strcasecmp(visibility, "private")) {
191 content->flags |= OS_LOG_CONTENT_FLAG_PRIVATE;
192 } else if (!strcasecmp(visibility, "public")) {
193 content->flags &= ~OS_LOG_CONTENT_FLAG_PRIVATE;
194 }
195 }
196
197 context->annotated = NULL;
198 }
199 #endif /* !KERNEL */
200
201 switch (ctype) {
202 case OS_LOG_BUFFER_VALUE_TYPE_COUNT:
203 case OS_LOG_BUFFER_VALUE_TYPE_SCALAR:
204 if (is_private) {
205 _encode_data(content, tempString, strlen(tempString) + 1, context);
206 } else {
207 if ((context->content_off + content_sz) > context->content_sz) {
208 return false;
209 }
210
211 memcpy(content->value, arg, arg_len);
212 content->size = (uint8_t)arg_len;
213 context->content_off += content_sz;
214 }
215 break;
216
217 case OS_LOG_BUFFER_VALUE_TYPE_STRING:
218 context->buffer->flags |= OS_LOG_BUFFER_HAS_NON_SCALAR;
219 if (_os_log_string_is_public(arg)) {
220 content->flags &= ~OS_LOG_CONTENT_FLAG_PRIVATE;
221 }
222
223 _encode_data(content, arg, arg_len, context);
224 break;
225
226 #ifndef KERNEL
227 case OS_LOG_BUFFER_VALUE_TYPE_POINTER:
228 context->buffer->flags |= OS_LOG_BUFFER_HAS_NON_SCALAR;
229 _encode_data(content, arg, arg_len, context);
230 break;
231
232 case OS_LOG_BUFFER_VALUE_TYPE_OBJECT:
233 context->buffer->flags |= OS_LOG_BUFFER_HAS_NON_SCALAR;
234 if (!_NSCF2data(arg, tempString, sizeof(tempString), &obj_private)) {
235 tempString[0] = '\0';
236 }
237
238 if (!obj_private) {
239 content->flags &= ~OS_LOG_CONTENT_FLAG_PRIVATE;
240 }
241
242 _encode_data(content, tempString, strlen(tempString) + 1, context);
243 break;
244 #endif /* !KERNEL */
245 }
246
247 if (content->flags & OS_LOG_CONTENT_FLAG_PRIVATE) {
248 context->buffer->flags |= OS_LOG_BUFFER_HAS_PRIVATE;
249 }
250
251 context->arg_idx++;
252
253 return true;
254 }
255
256 static bool
257 _os_log_encode(const char *format, va_list args, int saved_errno, os_log_buffer_context_t context)
258 {
259 const char *percent = strchr(format, '%');
260 #ifndef KERNEL
261 char annotated[256];
262 #endif
263
264 while (percent != NULL) {
265 ++percent;
266 if (percent[0] != '%') {
267 struct os_log_format_value_s value;
268 int type = OST_INT;
269 #ifndef KERNEL
270 bool long_double = false;
271 #endif
272 int prec = 0;
273 char ch;
274
275 for (bool done = false; !done; percent++) {
276 switch (ch = percent[0]) {
277 /* type of types or other */
278 case 'l': // longer
279 type++;
280 break;
281
282 case 'h': // shorter
283 type--;
284 break;
285
286 case 'z':
287 type = OST_SIZE;
288 break;
289
290 case 'j':
291 type = OST_INTMAX;
292 break;
293
294 case 't':
295 type = OST_PTRDIFF;
296 break;
297
298 case '.': // precision
299 if ((percent[1]) == '*') {
300 prec = va_arg(args, int);
301 _os_log_encode_arg(&prec, sizeof(prec), OS_LOG_BUFFER_VALUE_TYPE_COUNT, false, context);
302 percent++;
303 continue;
304 } else {
305 // we have to read the precision and do the right thing
306 const char *fmt = percent + 1;
307 prec = 0;
308 while (isdigit(ch = *fmt++)) {
309 prec = 10 * prec + (ch - '0');
310 }
311
312 if (prec > 1024) {
313 prec = 1024;
314 }
315
316 _os_log_encode_arg(&prec, sizeof(prec), OS_LOG_BUFFER_VALUE_TYPE_COUNT, false, context);
317 }
318 break;
319
320 case '-': // left-align
321 case '+': // force sign
322 case ' ': // prefix non-negative with space
323 case '#': // alternate
324 case '\'': // group by thousands
325 break;
326
327 /* fixed types */
328 case 'd': // integer
329 case 'i': // integer
330 case 'o': // octal
331 case 'u': // unsigned
332 case 'x': // hex
333 case 'X': // upper-hex
334 switch (type) {
335 case OST_CHAR:
336 value.type.ch = (char) va_arg(args, int);
337 _os_log_encode_arg(&value.type.ch, sizeof(value.type.ch), OS_LOG_BUFFER_VALUE_TYPE_SCALAR, false, context);
338 break;
339
340 case OST_SHORT:
341 value.type.s = (short) va_arg(args, int);
342 _os_log_encode_arg(&value.type.s, sizeof(value.type.s), OS_LOG_BUFFER_VALUE_TYPE_SCALAR, false, context);
343 break;
344
345 case OST_INT:
346 value.type.i = va_arg(args, int);
347 _os_log_encode_arg(&value.type.i, sizeof(value.type.i), OS_LOG_BUFFER_VALUE_TYPE_SCALAR, false, context);
348 break;
349
350 case OST_LONG:
351 value.type.l = va_arg(args, long);
352 _os_log_encode_arg(&value.type.l, sizeof(value.type.l), OS_LOG_BUFFER_VALUE_TYPE_SCALAR, false, context);
353 break;
354
355 case OST_LONGLONG:
356 value.type.ll = va_arg(args, long long);
357 _os_log_encode_arg(&value.type.ll, sizeof(value.type.ll), OS_LOG_BUFFER_VALUE_TYPE_SCALAR, false, context);
358 break;
359
360 case OST_SIZE:
361 value.type.z = va_arg(args, size_t);
362 _os_log_encode_arg(&value.type.z, sizeof(value.type.z), OS_LOG_BUFFER_VALUE_TYPE_SCALAR, false, context);
363 break;
364
365 case OST_INTMAX:
366 value.type.im = va_arg(args, intmax_t);
367 _os_log_encode_arg(&value.type.im, sizeof(value.type.im), OS_LOG_BUFFER_VALUE_TYPE_SCALAR, false, context);
368 break;
369
370 case OST_PTRDIFF:
371 value.type.pd = va_arg(args, ptrdiff_t);
372 _os_log_encode_arg(&value.type.pd, sizeof(value.type.pd), OS_LOG_BUFFER_VALUE_TYPE_SCALAR, false, context);
373 break;
374
375 default:
376 return false;
377 }
378 done = true;
379 break;
380
381 #ifndef KERNEL
382 case '{':
383 // we do not support this for shimmed code
384 if (context->shimmed) {
385 return false;
386 }
387
388 for (const char *curr2 = percent + 1; (ch = (*curr2)) != NUL; curr2++) {
389 if (ch == '}') {
390 strlcpy(annotated, percent, MIN(curr2 - (percent + 1), sizeof(annotated)));
391 context->annotated = annotated;
392 percent = curr2;
393 break;
394 }
395 }
396 break;
397 #endif /* !KERNEL */
398
399 case 'p': // pointer
400 value.type.p = va_arg(args, void *);
401 _os_log_encode_arg(&value.type.p, sizeof(value.type.p), OS_LOG_BUFFER_VALUE_TYPE_SCALAR, false, context);
402 done = true;
403 break;
404
405 #ifndef KERNEL
406 case 'P': // pointer data
407 if (context->shimmed) { // we do not support this for shimmed code
408 return false;
409 }
410
411 context->buffer->flags |= OS_LOG_BUFFER_HAS_NON_SCALAR;
412 value.type.p = va_arg(args, void *);
413
414 // capture the string pointer to generate a symptom
415 if (context->log && context->log->generate_symptoms && context->arg_idx == 1 && value.type.pch && prec) {
416 context->symptom_ptr = value.type.p;
417 context->symptom_ptr_len = prec;
418 }
419
420 _os_log_encode_arg(value.type.p, prec, OS_LOG_BUFFER_VALUE_TYPE_POINTER, false, context);
421 prec = 0;
422 done = true;
423 break;
424 #endif /* !KERNEL */
425
426 #ifndef KERNEL
427 case 'L': // long double
428 long_double = true;
429 break;
430
431 case 'a': case 'A': case 'e': case 'E': // floating types
432 case 'f': case 'F': case 'g': case 'G':
433 if (long_double) {
434 value.type.ld = va_arg(args, long double);
435 _os_log_encode_arg(&value.type.ld, sizeof(value.type.ld), OS_LOG_BUFFER_VALUE_TYPE_SCALAR, false, context);
436 } else {
437 value.type.d = va_arg(args, double);
438 _os_log_encode_arg(&value.type.d, sizeof(value.type.d), OS_LOG_BUFFER_VALUE_TYPE_SCALAR, false, context);
439 }
440 done = true;
441 break;
442 #endif /* !KERNEL */
443
444 case 'c': // char
445 value.type.ch = (char) va_arg(args, int);
446 _os_log_encode_arg(&value.type.ch, sizeof(value.type.ch), OS_LOG_BUFFER_VALUE_TYPE_SCALAR, false, context);
447 done = true;
448 break;
449
450 #ifndef KERNEL
451 case 'C': // wide-char
452 value.type.wch = va_arg(args, wint_t);
453 _os_log_encode_arg(&value.type.wch, sizeof(value.type.wch), OS_LOG_BUFFER_VALUE_TYPE_SCALAR, false, context);
454 done = true;
455 break;
456 #endif /* !KERNEL */
457
458 case 's': // string
459 value.type.pch = va_arg(args, char *);
460 if (!prec && value.type.pch != NULL) {
461 prec = (int) strlen(value.type.pch) + 1;
462 }
463
464 #ifndef KERNEL
465 // capture the string pointer to generate a symptom
466 if (context->log && context->log->generate_symptoms && context->arg_idx == 0 && value.type.pch) {
467 context->symptom_str = value.type.pch;
468 }
469 #endif
470
471 context->buffer->flags |= OS_LOG_BUFFER_HAS_NON_SCALAR;
472 _os_log_encode_arg(value.type.pch, prec, OS_LOG_BUFFER_VALUE_TYPE_STRING, false, context);
473 prec = 0;
474 done = true;
475 break;
476
477 #ifndef KERNEL
478 case 'S': // wide-string
479 value.type.pwch = va_arg(args, wchar_t *);
480 if (!prec && value.type.pwch != NULL) {
481 prec = (int) wcslen(value.type.pwch) + 1;
482 }
483
484 context->buffer->flags |= OS_LOG_BUFFER_HAS_NON_SCALAR;
485 _os_log_encode_arg(value.type.pwch, prec, OS_LOG_BUFFER_VALUE_TYPE_STRING, false, context);
486 prec = 0;
487 done = true;
488 break;
489 #endif /* !KERNEL */
490
491 #ifndef KERNEL
492 case '@': // CFTypeRef aka NSObject *
493 context->buffer->flags |= OS_LOG_BUFFER_HAS_NON_SCALAR;
494 _os_log_encode_arg(va_arg(args, void *), 0, OS_LOG_BUFFER_VALUE_TYPE_OBJECT, false, context);
495 done = true;
496 break;
497 #endif /* !KERNEL */
498
499 case 'm':
500 value.type.i = saved_errno;
501 _os_log_encode_arg(&value.type.i, sizeof(value.type.i), OS_LOG_BUFFER_VALUE_TYPE_SCALAR, false, context);
502 done = true;
503 break;
504
505 default:
506 if (isdigit(ch)) { // [0-9]
507 continue;
508 }
509 return false;
510 }
511
512 if (done) {
513 percent = strchr(percent, '%'); // Find next format
514 break;
515 }
516 }
517 } else {
518 percent = strchr(percent + 1, '%'); // Find next format after %%
519 }
520 }
521
522 context->buffer->arg_cnt = context->arg_idx;
523 context->content_sz = context->content_off;
524 context->pubdata_sz = context->pubdata_off;
525 context->privdata_sz = context->privdata_off;
526 context->arg_idx = context->content_off = context->pubdata_off = context->privdata_off = 0;
527
528 return true;
529 }
530
531 #endif /* log_encode_h */