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>
31 #define isdigit(ch) (((ch) >= '0') && ((ch) <= '9'))
32 extern boolean_t doprnt_hide_pointers
;
36 _encode_data(os_log_buffer_value_t content
, const void *arg
, uint16_t arg_len
, os_log_buffer_context_t context
)
38 struct os_log_arginfo_s arginfo
;
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
;
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
;
51 if (context
->arg_content_sz
> 0) {
52 arginfo
.length
= MIN(context
->arg_content_sz
, arginfo
.length
);
55 memcpy(content
->value
, &arginfo
, sizeof(arginfo
));
56 content
->size
= sizeof(arginfo
);
59 if (content
->type
== OS_LOG_BUFFER_VALUE_TYPE_STRING
61 || content
->type
== OS_LOG_BUFFER_VALUE_TYPE_OBJECT
64 strlcpy(databuf
, arg
, arginfo
.length
);
66 memcpy(databuf
, arg
, arginfo
.length
);
70 if (content
->flags
& OS_LOG_CONTENT_FLAG_PRIVATE
) {
71 context
->privdata_off
+= arginfo
.length
;
73 context
->pubdata_off
+= arginfo
.length
;
76 context
->content_off
+= sizeof(*content
) + content
->size
;
77 context
->arg_content_sz
= 0;
84 _os_log_parse_annotated(char *annotated
, const char **visibility
, const char **library
, const char **type
)
86 char *values
[3] = { NULL
};
91 char *token
= strsep(&annotated
, ", {}");
100 values
[cnt
++] = token
;
103 if ((cnt
> 0) && (!strcmp(values
[0], "public") || !strcmp(values
[0], "private"))) {
104 if (visibility
!= NULL
) {
105 (*visibility
) = values
[0];
111 if (idx
< cnt
&& (library
!= NULL
) && (type
!= NULL
)) {
112 char *decoder
= values
[idx
];
114 for (cnt
= 0; cnt
< 3; ) {
115 char *token
= strsep(&decoder
, ": {}");
120 if (*token
== '\0') {
124 values
[cnt
++] = token
;
128 (*library
) = values
[0];
133 (*library
) = "builtin";
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
)
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
] = {};
148 bool obj_private
= true;
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
);
159 if (value
>= VM_MIN_KERNEL_AND_KEXT_ADDRESS
&& value
<= VM_MAX_KERNEL_ADDRESS
) {
166 content
->type
= ctype
;
167 content
->flags
= (is_private
? OS_LOG_CONTENT_FLAG_PRIVATE
: 0);
170 if (context
->annotated
!= NULL
) {
171 const char *visibility
= NULL
;
173 _os_log_parse_annotated(context
->annotated
, &visibility
, NULL
, NULL
);
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
;
182 context
->annotated
= NULL
;
187 case OS_LOG_BUFFER_VALUE_TYPE_COUNT
:
188 case OS_LOG_BUFFER_VALUE_TYPE_SCALAR
:
190 _encode_data(content
, tempString
, strlen(tempString
) + 1, context
);
192 if ((context
->content_off
+ content_sz
) > context
->content_sz
) {
196 memcpy(content
->value
, arg
, arg_len
);
197 content
->size
= arg_len
;
198 context
->content_off
+= content_sz
;
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
;
208 _encode_data(content
, arg
, arg_len
, context
);
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
);
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';
224 content
->flags
&= ~OS_LOG_CONTENT_FLAG_PRIVATE
;
227 _encode_data(content
, tempString
, strlen(tempString
) + 1, context
);
232 if (content
->flags
& OS_LOG_CONTENT_FLAG_PRIVATE
) {
233 context
->buffer
->flags
|= OS_LOG_BUFFER_HAS_PRIVATE
;
242 _os_log_encode(const char *format
, va_list args
, int saved_errno
, os_log_buffer_context_t context
)
244 const char *percent
= strchr(format
, '%');
249 while (percent
!= NULL
) {
251 if (percent
[0] != '%') {
252 struct os_log_format_value_s value
;
255 bool long_double
= false;
260 for (bool done
= false; !done
; percent
++) {
261 switch (ch
= percent
[0]) {
262 /* type of types or other */
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
);
290 // we have to read the precision and do the right thing
291 const char *fmt
= percent
+ 1;
293 while (isdigit(ch
= *fmt
++)) {
294 prec
= 10 * prec
+ (ch
- '0');
301 _os_log_encode_arg(&prec
, sizeof(prec
), OS_LOG_BUFFER_VALUE_TYPE_COUNT
, false, context
);
305 case '-': // left-align
306 case '+': // force sign
307 case ' ': // prefix non-negative with space
308 case '#': // alternate
309 case '\'': // group by thousands
316 case 'u': // unsigned
318 case 'X': // upper-hex
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
368 // we do not support this for shimmed code
369 if (context
->shimmed
) {
373 for (const char *curr2
= percent
+ 1; (ch
= (*curr2
)) != NUL
; curr2
++) {
375 strlcpy(annotated
, percent
, MIN(curr2
- (percent
+ 1), sizeof(annotated
)));
376 context
->annotated
= annotated
;
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
);
391 case 'P': // pointer data
392 if (context
->shimmed
) { // we do not support this for shimmed code
396 context
->buffer
->flags
|= OS_LOG_BUFFER_HAS_NON_SCALAR
;
397 value
.type
.p
= va_arg(args
, void *);
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
;
405 _os_log_encode_arg(value
.type
.p
, prec
, OS_LOG_BUFFER_VALUE_TYPE_POINTER
, false, context
);
412 case 'L': // long double
416 case 'a': case 'A': case 'e': case 'E': // floating types
417 case 'f': case 'F': case 'g': case 'G':
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
);
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
);
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
);
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
);
444 value
.type
.pch
= va_arg(args
, char *);
445 if (!prec
&& value
.type
.pch
!= NULL
) {
446 prec
= (int) strlen(value
.type
.pch
) + 1;
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
;
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
);
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;
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
);
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
);
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
);
491 if (isdigit(ch
)) { // [0-9]
498 percent
= strchr(percent
, '%'); // Find next format
503 percent
= strchr(percent
+1, '%'); // Find next format after %%
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;
516 #endif /* log_encode_h */