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