2 * Copyright (c) 2015-2016 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
27 #include "log_encode_types.h"
28 #include <sys/param.h>
30 #if __has_feature(ptrauth_calls)
31 #include <mach/vm_param.h>
33 #endif /* __has_feature(ptrauth_calls) */
36 #define isdigit(ch) (((ch) >= '0') && ((ch) <= '9'))
37 extern boolean_t doprnt_hide_pointers
;
41 _encode_data(os_log_buffer_value_t content
, const void *arg
, size_t arg_len
, os_log_buffer_context_t context
)
43 struct os_log_arginfo_s arginfo
;
46 arg_len
= MIN(arg_len
, UINT16_MAX
);
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
;
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
;
58 if (context
->arg_content_sz
> 0) {
59 arginfo
.length
= MIN((uint16_t)context
->arg_content_sz
, arginfo
.length
);
62 memcpy(content
->value
, &arginfo
, sizeof(arginfo
));
63 content
->size
= sizeof(arginfo
);
66 if (content
->type
== OS_LOG_BUFFER_VALUE_TYPE_STRING
68 || content
->type
== OS_LOG_BUFFER_VALUE_TYPE_OBJECT
71 strlcpy(databuf
, arg
, arginfo
.length
);
73 memcpy(databuf
, arg
, arginfo
.length
);
77 if (content
->flags
& OS_LOG_CONTENT_FLAG_PRIVATE
) {
78 context
->privdata_off
+= arginfo
.length
;
80 context
->pubdata_off
+= arginfo
.length
;
83 context
->content_off
+= sizeof(*content
) + content
->size
;
84 context
->arg_content_sz
= 0;
91 _os_log_parse_annotated(char *annotated
, const char **visibility
, const char **library
, const char **type
)
93 char *values
[3] = { NULL
};
98 char *token
= strsep(&annotated
, ", {}");
103 if (*token
== '\0') {
107 values
[cnt
++] = token
;
110 if ((cnt
> 0) && (!strcmp(values
[0], "public") || !strcmp(values
[0], "private"))) {
111 if (visibility
!= NULL
) {
112 (*visibility
) = values
[0];
118 if (idx
< cnt
&& (library
!= NULL
) && (type
!= NULL
)) {
119 char *decoder
= values
[idx
];
121 for (cnt
= 0; cnt
< 3;) {
122 char *token
= strsep(&decoder
, ": {}");
127 if (*token
== '\0') {
131 values
[cnt
++] = token
;
135 (*library
) = values
[0];
140 (*library
) = "builtin";
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
)
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
] = {};
155 bool obj_private
= true;
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
);
166 #if __has_feature(ptrauth_calls)
168 * Strip out the pointer authentication code before
169 * checking whether the pointer is a kernel address.
171 value
= (unsigned long long)VM_KERNEL_STRIP_PTR(value
);
172 #endif /* __has_feature(ptrauth_calls) */
174 if (value
>= VM_MIN_KERNEL_AND_KEXT_ADDRESS
&& value
<= VM_MAX_KERNEL_ADDRESS
) {
181 content
->type
= ctype
;
182 content
->flags
= (is_private
? OS_LOG_CONTENT_FLAG_PRIVATE
: 0);
185 if (context
->annotated
!= NULL
) {
186 const char *visibility
= NULL
;
188 _os_log_parse_annotated(context
->annotated
, &visibility
, NULL
, NULL
);
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
;
197 context
->annotated
= NULL
;
202 case OS_LOG_BUFFER_VALUE_TYPE_COUNT
:
203 case OS_LOG_BUFFER_VALUE_TYPE_SCALAR
:
205 _encode_data(content
, tempString
, strlen(tempString
) + 1, context
);
207 if ((context
->content_off
+ content_sz
) > context
->content_sz
) {
211 memcpy(content
->value
, arg
, arg_len
);
212 content
->size
= (uint8_t)arg_len
;
213 context
->content_off
+= content_sz
;
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
;
223 _encode_data(content
, arg
, arg_len
, context
);
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
);
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';
239 content
->flags
&= ~OS_LOG_CONTENT_FLAG_PRIVATE
;
242 _encode_data(content
, tempString
, strlen(tempString
) + 1, context
);
247 if (content
->flags
& OS_LOG_CONTENT_FLAG_PRIVATE
) {
248 context
->buffer
->flags
|= OS_LOG_BUFFER_HAS_PRIVATE
;
257 _os_log_encode(const char *format
, va_list args
, int saved_errno
, os_log_buffer_context_t context
)
259 const char *percent
= strchr(format
, '%');
264 while (percent
!= NULL
) {
266 if (percent
[0] != '%') {
267 struct os_log_format_value_s value
;
270 bool long_double
= false;
275 for (bool done
= false; !done
; percent
++) {
276 switch (ch
= percent
[0]) {
277 /* type of types or other */
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
);
305 // we have to read the precision and do the right thing
306 const char *fmt
= percent
+ 1;
308 while (isdigit(ch
= *fmt
++)) {
309 prec
= 10 * prec
+ (ch
- '0');
316 _os_log_encode_arg(&prec
, sizeof(prec
), OS_LOG_BUFFER_VALUE_TYPE_COUNT
, false, context
);
320 case '-': // left-align
321 case '+': // force sign
322 case ' ': // prefix non-negative with space
323 case '#': // alternate
324 case '\'': // group by thousands
331 case 'u': // unsigned
333 case 'X': // upper-hex
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
383 // we do not support this for shimmed code
384 if (context
->shimmed
) {
388 for (const char *curr2
= percent
+ 1; (ch
= (*curr2
)) != NUL
; curr2
++) {
390 strlcpy(annotated
, percent
, MIN(curr2
- (percent
+ 1), sizeof(annotated
)));
391 context
->annotated
= annotated
;
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
);
406 case 'P': // pointer data
407 if (context
->shimmed
) { // we do not support this for shimmed code
411 context
->buffer
->flags
|= OS_LOG_BUFFER_HAS_NON_SCALAR
;
412 value
.type
.p
= va_arg(args
, void *);
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
;
420 _os_log_encode_arg(value
.type
.p
, prec
, OS_LOG_BUFFER_VALUE_TYPE_POINTER
, false, context
);
427 case 'L': // long double
431 case 'a': case 'A': case 'e': case 'E': // floating types
432 case 'f': case 'F': case 'g': case 'G':
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
);
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
);
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
);
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
);
459 value
.type
.pch
= va_arg(args
, char *);
460 if (!prec
&& value
.type
.pch
!= NULL
) {
461 prec
= (int) strlen(value
.type
.pch
) + 1;
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
;
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
);
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;
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
);
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
);
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
);
506 if (isdigit(ch
)) { // [0-9]
513 percent
= strchr(percent
, '%'); // Find next format
518 percent
= strchr(percent
+ 1, '%'); // Find next format after %%
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;
531 #endif /* log_encode_h */