]> git.saurik.com Git - apple/xnu.git/blame - libkern/os/log_encode.h
xnu-3789.70.16.tar.gz
[apple/xnu.git] / libkern / os / log_encode.h
CommitLineData
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 32extern boolean_t doprnt_hide_pointers;
39037602
A
33#endif
34
35static 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
83static 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
140OS_ALWAYS_INLINE
141static 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
241static 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 */