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