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
, uint16_t arg_len
, os_log_buffer_context_t context
)
43 struct os_log_arginfo_s arginfo
;
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
;
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
;
56 if (context
->arg_content_sz
> 0) {
57 arginfo
.length
= MIN(context
->arg_content_sz
, arginfo
.length
);
60 memcpy(content
->value
, &arginfo
, sizeof(arginfo
));
61 content
->size
= sizeof(arginfo
);
64 if (content
->type
== OS_LOG_BUFFER_VALUE_TYPE_STRING
66 || content
->type
== OS_LOG_BUFFER_VALUE_TYPE_OBJECT
69 strlcpy(databuf
, arg
, arginfo
.length
);
71 memcpy(databuf
, arg
, arginfo
.length
);
75 if (content
->flags
& OS_LOG_CONTENT_FLAG_PRIVATE
) {
76 context
->privdata_off
+= arginfo
.length
;
78 context
->pubdata_off
+= arginfo
.length
;
81 context
->content_off
+= sizeof(*content
) + content
->size
;
82 context
->arg_content_sz
= 0;
89 _os_log_parse_annotated(char *annotated
, const char **visibility
, const char **library
, const char **type
)
91 char *values
[3] = { NULL
};
96 char *token
= strsep(&annotated
, ", {}");
101 if (*token
== '\0') {
105 values
[cnt
++] = token
;
108 if ((cnt
> 0) && (!strcmp(values
[0], "public") || !strcmp(values
[0], "private"))) {
109 if (visibility
!= NULL
) {
110 (*visibility
) = values
[0];
116 if (idx
< cnt
&& (library
!= NULL
) && (type
!= NULL
)) {
117 char *decoder
= values
[idx
];
119 for (cnt
= 0; cnt
< 3;) {
120 char *token
= strsep(&decoder
, ": {}");
125 if (*token
== '\0') {
129 values
[cnt
++] = token
;
133 (*library
) = values
[0];
138 (*library
) = "builtin";
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
)
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
] = {};
153 bool obj_private
= true;
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
);
164 #if __has_feature(ptrauth_calls)
166 * Strip out the pointer authentication code before
167 * checking whether the pointer is a kernel address.
169 value
= (unsigned long long)VM_KERNEL_STRIP_PTR(value
);
170 #endif /* __has_feature(ptrauth_calls) */
172 if (value
>= VM_MIN_KERNEL_AND_KEXT_ADDRESS
&& value
<= VM_MAX_KERNEL_ADDRESS
) {
179 content
->type
= ctype
;
180 content
->flags
= (is_private
? OS_LOG_CONTENT_FLAG_PRIVATE
: 0);
183 if (context
->annotated
!= NULL
) {
184 const char *visibility
= NULL
;
186 _os_log_parse_annotated(context
->annotated
, &visibility
, NULL
, NULL
);
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
;
195 context
->annotated
= NULL
;
200 case OS_LOG_BUFFER_VALUE_TYPE_COUNT
:
201 case OS_LOG_BUFFER_VALUE_TYPE_SCALAR
:
203 _encode_data(content
, tempString
, strlen(tempString
) + 1, context
);
205 if ((context
->content_off
+ content_sz
) > context
->content_sz
) {
209 memcpy(content
->value
, arg
, arg_len
);
210 content
->size
= arg_len
;
211 context
->content_off
+= content_sz
;
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
;
221 _encode_data(content
, arg
, arg_len
, context
);
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
);
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';
237 content
->flags
&= ~OS_LOG_CONTENT_FLAG_PRIVATE
;
240 _encode_data(content
, tempString
, strlen(tempString
) + 1, context
);
245 if (content
->flags
& OS_LOG_CONTENT_FLAG_PRIVATE
) {
246 context
->buffer
->flags
|= OS_LOG_BUFFER_HAS_PRIVATE
;
255 _os_log_encode(const char *format
, va_list args
, int saved_errno
, os_log_buffer_context_t context
)
257 const char *percent
= strchr(format
, '%');
262 while (percent
!= NULL
) {
264 if (percent
[0] != '%') {
265 struct os_log_format_value_s value
;
268 bool long_double
= false;
273 for (bool done
= false; !done
; percent
++) {
274 switch (ch
= percent
[0]) {
275 /* type of types or other */
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
);
303 // we have to read the precision and do the right thing
304 const char *fmt
= percent
+ 1;
306 while (isdigit(ch
= *fmt
++)) {
307 prec
= 10 * prec
+ (ch
- '0');
314 _os_log_encode_arg(&prec
, sizeof(prec
), OS_LOG_BUFFER_VALUE_TYPE_COUNT
, false, context
);
318 case '-': // left-align
319 case '+': // force sign
320 case ' ': // prefix non-negative with space
321 case '#': // alternate
322 case '\'': // group by thousands
329 case 'u': // unsigned
331 case 'X': // upper-hex
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
381 // we do not support this for shimmed code
382 if (context
->shimmed
) {
386 for (const char *curr2
= percent
+ 1; (ch
= (*curr2
)) != NUL
; curr2
++) {
388 strlcpy(annotated
, percent
, MIN(curr2
- (percent
+ 1), sizeof(annotated
)));
389 context
->annotated
= annotated
;
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
);
404 case 'P': // pointer data
405 if (context
->shimmed
) { // we do not support this for shimmed code
409 context
->buffer
->flags
|= OS_LOG_BUFFER_HAS_NON_SCALAR
;
410 value
.type
.p
= va_arg(args
, void *);
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
;
418 _os_log_encode_arg(value
.type
.p
, prec
, OS_LOG_BUFFER_VALUE_TYPE_POINTER
, false, context
);
425 case 'L': // long double
429 case 'a': case 'A': case 'e': case 'E': // floating types
430 case 'f': case 'F': case 'g': case 'G':
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
);
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
);
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
);
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
);
457 value
.type
.pch
= va_arg(args
, char *);
458 if (!prec
&& value
.type
.pch
!= NULL
) {
459 prec
= (int) strlen(value
.type
.pch
) + 1;
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
;
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
);
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;
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
);
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
);
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
);
504 if (isdigit(ch
)) { // [0-9]
511 percent
= strchr(percent
, '%'); // Find next format
516 percent
= strchr(percent
+ 1, '%'); // Find next format after %%
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;
529 #endif /* log_encode_h */