2 * Copyright (c) 2015-2020 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@
25 #include <firehose/tracepoint_private.h>
26 #include <kern/assert.h>
27 #include <kern/counter.h>
28 #include <kern/locks.h>
29 #include <pexpert/pexpert.h>
30 #include <sys/param.h>
32 #if __has_feature(ptrauth_calls)
33 #include <mach/vm_param.h>
35 #endif /* __has_feature(ptrauth_calls) */
37 #include "log_encode.h"
40 #define isdigit(ch) (((ch) >= '0') && ((ch) <= '9'))
41 #define log_context_cursor(ctx) &(ctx)->ctx_hdr->hdr_data[(ctx)->ctx_content_off]
43 extern boolean_t doprnt_hide_pointers
;
45 SCALABLE_COUNTER_DEFINE(oslog_p_fmt_invalid_msgcount
);
46 SCALABLE_COUNTER_DEFINE(oslog_p_fmt_max_args_msgcount
);
47 SCALABLE_COUNTER_DEFINE(oslog_p_truncated_msgcount
);
50 is_kernel_pointer(void *arg
, size_t arg_len
)
52 if (arg_len
< sizeof(void *)) {
56 unsigned long long value
= 0;
57 assert(arg_len
<= sizeof(value
));
58 (void) memcpy(&value
, arg
, arg_len
);
60 #if __has_feature(ptrauth_calls)
62 * Strip out the pointer authentication code before
63 * checking whether the pointer is a kernel address.
65 value
= (unsigned long long)VM_KERNEL_STRIP_PTR(value
);
66 #endif /* __has_feature(ptrauth_calls) */
68 return value
>= VM_MIN_KERNEL_AND_KEXT_ADDRESS
&& value
<= VM_MAX_KERNEL_ADDRESS
;
72 log_context_cursor_advance(os_log_context_t ctx
, size_t amount
)
74 ctx
->ctx_content_off
+= amount
;
75 assert(log_context_cursor(ctx
) <= (ctx
->ctx_buffer
+ ctx
->ctx_buffer_sz
));
79 log_fits(os_log_context_t ctx
, size_t data_size
)
81 return (ctx
->ctx_content_off
+ data_size
) <= ctx
->ctx_content_sz
;
85 log_fits_cmd(os_log_context_t ctx
, size_t data_size
)
87 return log_fits(ctx
, sizeof(*ctx
->ctx_hdr
) + data_size
);
91 log_range_update(os_log_fmt_range_t range
, uint16_t offset
, uint16_t length
)
93 range
->offset
= offset
;
95 * Truncated flag may have already been set earlier, hence do not
96 * overwrite it blindly.
98 if (length
< range
->length
) {
99 range
->truncated
= true;
101 range
->length
= length
;
105 * Stores a command in the main section. The value itself is wrapped in
106 * the os_log_fmt_cmd_t struct.
109 log_add_cmd(os_log_context_t ctx
, os_log_fmt_cmd_type_t type
, uint8_t flags
,
110 void *arg
, size_t arg_size
)
112 os_log_fmt_cmd_t cmd
;
113 const size_t cmd_sz
= sizeof(*cmd
) + arg_size
;
115 assert(log_fits_cmd(ctx
, cmd_sz
));
116 assert(arg_size
<= UINT8_MAX
);
118 cmd
= (os_log_fmt_cmd_t
)log_context_cursor(ctx
);
119 cmd
->cmd_type
= type
;
120 cmd
->cmd_flags
= flags
;
121 cmd
->cmd_size
= (uint8_t)arg_size
;
122 (void) memcpy(cmd
->cmd_data
, arg
, cmd
->cmd_size
);
124 assert(cmd_sz
== sizeof(*cmd
) + cmd
->cmd_size
);
125 log_context_cursor_advance(ctx
, cmd_sz
);
129 * Collect details about argument which needs to be stored in the pubdata
133 log_collect_public_range_data(os_log_context_t ctx
, os_log_fmt_range_t range
, void *arg
)
135 ctx
->ctx_pubdata
[ctx
->ctx_pubdata_cnt
++] = (char *)arg
;
136 ctx
->ctx_pubdata_sz
+= range
->length
;
140 log_add_range_data(os_log_context_t ctx
, os_log_fmt_range_t range
, void *arg
)
142 assert(log_fits(ctx
, range
->length
));
143 (void) memcpy(log_context_cursor(ctx
), arg
, range
->length
);
144 log_context_cursor_advance(ctx
, range
->length
);
147 static struct os_log_fmt_range_s
148 log_create_range(os_log_context_t ctx
, size_t arg_len
)
150 const size_t final_arg_len
= MIN(arg_len
, UINT16_MAX
);
152 return (struct os_log_fmt_range_s
) {
153 .offset
= ctx
->ctx_pubdata_sz
,
154 .length
= (uint16_t)final_arg_len
,
155 .truncated
= (final_arg_len
< arg_len
)
160 log_add_range_arg(os_log_context_t ctx
, os_log_fmt_cmd_type_t type
, os_log_fmt_cmd_flags_t flags
,
161 void *arg
, size_t arg_len
)
163 struct os_log_fmt_range_s range
;
165 if (!log_fits_cmd(ctx
, sizeof(range
))) {
169 range
= log_create_range(ctx
, arg_len
);
171 if (flags
== OSLF_CMD_FLAG_PUBLIC
) {
172 if (ctx
->ctx_pubdata_cnt
== OS_LOG_MAX_PUB_ARGS
) {
175 assert(ctx
->ctx_pubdata_cnt
< OS_LOG_MAX_PUB_ARGS
);
176 log_collect_public_range_data(ctx
, &range
, arg
);
178 log_add_cmd(ctx
, type
, flags
, &range
, sizeof(range
));
179 ctx
->ctx_hdr
->hdr_cmd_cnt
++;
185 * Adds a scalar argument value to the main section.
188 log_add_arg(os_log_context_t ctx
, os_log_fmt_cmd_type_t type
, void *arg
, size_t arg_len
)
190 assert(type
== OSLF_CMD_TYPE_COUNT
|| type
== OSLF_CMD_TYPE_SCALAR
);
191 assert(arg_len
< UINT16_MAX
);
193 if (log_fits_cmd(ctx
, arg_len
)) {
194 log_add_cmd(ctx
, type
, OSLF_CMD_FLAG_PUBLIC
, arg
, arg_len
);
195 ctx
->ctx_hdr
->hdr_cmd_cnt
++;
203 log_encode_public_data(os_log_context_t ctx
)
205 const uint16_t orig_content_off
= ctx
->ctx_content_off
;
206 os_log_fmt_hdr_t
const hdr
= ctx
->ctx_hdr
;
207 os_log_fmt_cmd_t cmd
= (os_log_fmt_cmd_t
)hdr
->hdr_data
;
209 assert(ctx
->ctx_pubdata_cnt
<= hdr
->hdr_cmd_cnt
);
211 for (int i
= 0, pub_i
= 0; i
< hdr
->hdr_cmd_cnt
; i
++, cmd
= (os_log_fmt_cmd_t
)(cmd
->cmd_data
+ cmd
->cmd_size
)) {
212 if (cmd
->cmd_type
!= OSLF_CMD_TYPE_STRING
) {
216 os_log_fmt_range_t
const range
__attribute__((aligned(8))) = (os_log_fmt_range_t
)&cmd
->cmd_data
;
218 // Fix offset and length of the argument data in the hdr.
219 log_range_update(range
, ctx
->ctx_content_off
- orig_content_off
,
220 MIN(range
->length
, ctx
->ctx_content_sz
- ctx
->ctx_content_off
));
222 if (range
->truncated
) {
223 ctx
->ctx_truncated
= true;
226 assert(pub_i
< ctx
->ctx_pubdata_cnt
);
227 log_add_range_data(ctx
, range
, ctx
->ctx_pubdata
[pub_i
++]);
232 log_expand(os_log_context_t ctx
, size_t new_size
)
234 assert(new_size
> ctx
->ctx_buffer_sz
);
236 if (!oslog_is_safe()) {
240 size_t final_size
= new_size
;
242 void *buf
= logmem_alloc(ctx
->ctx_logmem
, &final_size
);
246 assert(final_size
>= new_size
);
248 // address length header + already stored data
249 const size_t hdr_size
= (uint8_t *)ctx
->ctx_hdr
- ctx
->ctx_buffer
;
250 const size_t copy_size
= hdr_size
+ sizeof(*ctx
->ctx_hdr
) + ctx
->ctx_content_sz
;
251 assert(copy_size
<= new_size
);
252 (void) memcpy(buf
, ctx
->ctx_buffer
, copy_size
);
254 if (ctx
->ctx_allocated
) {
255 logmem_free(ctx
->ctx_logmem
, ctx
->ctx_buffer
, ctx
->ctx_buffer_sz
);
258 ctx
->ctx_buffer
= buf
;
259 ctx
->ctx_buffer_sz
= final_size
;
260 ctx
->ctx_content_sz
= (uint16_t)(ctx
->ctx_buffer_sz
- hdr_size
- sizeof(*ctx
->ctx_hdr
));
261 ctx
->ctx_hdr
= (os_log_fmt_hdr_t
)&ctx
->ctx_buffer
[hdr_size
];
262 ctx
->ctx_allocated
= true;
268 log_encode_fmt_arg(void *arg
, size_t arg_len
, os_log_fmt_cmd_type_t type
, os_log_context_t ctx
)
273 case OSLF_CMD_TYPE_COUNT
:
274 case OSLF_CMD_TYPE_SCALAR
:
275 // Scrub kernel pointers.
276 if (doprnt_hide_pointers
&& is_kernel_pointer(arg
, arg_len
)) {
277 rc
= log_add_range_arg(ctx
, type
, OSLF_CMD_FLAG_PRIVATE
, NULL
, 0);
278 ctx
->ctx_hdr
->hdr_flags
|= OSLF_HDR_FLAG_HAS_PRIVATE
;
280 rc
= log_add_arg(ctx
, type
, arg
, arg_len
);
283 case OSLF_CMD_TYPE_STRING
:
284 rc
= log_add_range_arg(ctx
, type
, OSLF_CMD_FLAG_PUBLIC
, arg
, arg_len
);
285 ctx
->ctx_hdr
->hdr_flags
|= OSLF_HDR_FLAG_HAS_NON_SCALAR
;
288 panic("Unsupported log value type");
295 log_encode_fmt(os_log_context_t ctx
, const char *format
, va_list args
)
297 const char *percent
= strchr(format
, '%');
299 while (percent
!= NULL
) {
302 if (percent
[0] == '%') {
303 percent
= strchr(percent
+ 1, '%'); // Find next format after %%
307 struct os_log_format_value_s value
;
312 for (bool done
= false; !done
; percent
++) {
315 switch (ch
= percent
[0]) {
316 /* type of types or other */
341 case '.': // precision
342 if ((percent
[1]) == '*') {
343 prec
= va_arg(args
, int);
344 err
= log_encode_fmt_arg(&prec
, sizeof(prec
), OSLF_CMD_TYPE_COUNT
, ctx
);
351 // we have to read the precision and do the right thing
352 const char *fmt
= percent
+ 1;
354 while (isdigit(ch
= *fmt
++)) {
355 prec
= 10 * prec
+ (ch
- '0');
362 err
= log_encode_fmt_arg(&prec
, sizeof(prec
), OSLF_CMD_TYPE_COUNT
, ctx
);
366 case '-': // left-align
367 case '+': // force sign
368 case ' ': // prefix non-negative with space
369 case '#': // alternate
370 case '\'': // group by thousands
377 case 'u': // unsigned
379 case 'X': // upper-hex
382 value
.type
.ch
= (char) va_arg(args
, int);
383 err
= log_encode_fmt_arg(&value
.type
.ch
, sizeof(value
.type
.ch
), OSLF_CMD_TYPE_SCALAR
, ctx
);
387 value
.type
.s
= (short) va_arg(args
, int);
388 err
= log_encode_fmt_arg(&value
.type
.s
, sizeof(value
.type
.s
), OSLF_CMD_TYPE_SCALAR
, ctx
);
392 value
.type
.i
= va_arg(args
, int);
393 err
= log_encode_fmt_arg(&value
.type
.i
, sizeof(value
.type
.i
), OSLF_CMD_TYPE_SCALAR
, ctx
);
397 value
.type
.l
= va_arg(args
, long);
398 err
= log_encode_fmt_arg(&value
.type
.l
, sizeof(value
.type
.l
), OSLF_CMD_TYPE_SCALAR
, ctx
);
402 value
.type
.ll
= va_arg(args
, long long);
403 err
= log_encode_fmt_arg(&value
.type
.ll
, sizeof(value
.type
.ll
), OSLF_CMD_TYPE_SCALAR
, ctx
);
407 value
.type
.z
= va_arg(args
, size_t);
408 err
= log_encode_fmt_arg(&value
.type
.z
, sizeof(value
.type
.z
), OSLF_CMD_TYPE_SCALAR
, ctx
);
412 value
.type
.im
= va_arg(args
, intmax_t);
413 err
= log_encode_fmt_arg(&value
.type
.im
, sizeof(value
.type
.im
), OSLF_CMD_TYPE_SCALAR
, ctx
);
417 value
.type
.pd
= va_arg(args
, ptrdiff_t);
418 err
= log_encode_fmt_arg(&value
.type
.pd
, sizeof(value
.type
.pd
), OSLF_CMD_TYPE_SCALAR
, ctx
);
428 value
.type
.p
= va_arg(args
, void *);
429 err
= log_encode_fmt_arg(&value
.type
.p
, sizeof(value
.type
.p
), OSLF_CMD_TYPE_SCALAR
, ctx
);
434 value
.type
.ch
= (char) va_arg(args
, int);
435 err
= log_encode_fmt_arg(&value
.type
.ch
, sizeof(value
.type
.ch
), OSLF_CMD_TYPE_SCALAR
, ctx
);
440 value
.type
.pch
= va_arg(args
, char *);
441 if (prec
== 0 && value
.type
.pch
) {
442 prec
= (int) strlen(value
.type
.pch
) + 1;
444 err
= log_encode_fmt_arg(value
.type
.pch
, prec
, OSLF_CMD_TYPE_STRING
, ctx
);
450 value
.type
.i
= 0; // Does %m make sense in the kernel?
451 err
= log_encode_fmt_arg(&value
.type
.i
, sizeof(value
.type
.i
), OSLF_CMD_TYPE_SCALAR
, ctx
);
456 if (isdigit(ch
)) { // [0-9]
467 percent
= strchr(percent
, '%'); // Find next format
477 write_address_location(uint8_t buf
[static sizeof(uint64_t)],
478 void *dso
, const void *address
, firehose_tracepoint_flags_t
*flags
, bool driverKit
)
480 uintptr_t shift_addr
= (uintptr_t)address
- (uintptr_t)dso
;
482 kc_format_t kcformat
= KCFormatUnknown
;
483 __assert_only
bool result
= PE_get_primary_kc_format(&kcformat
);
486 if (kcformat
== KCFormatStatic
|| kcformat
== KCFormatKCGEN
) {
487 *flags
= _firehose_tracepoint_flags_pc_style_shared_cache
;
488 memcpy(buf
, (uint32_t[]){ (uint32_t)shift_addr
}, sizeof(uint32_t));
489 return sizeof(uint32_t);
493 * driverKit will have the dso set as MH_EXECUTE (it is logging from a
494 * syscall in the kernel) but needs logd to parse the address as an
497 kernel_mach_header_t
*mh
= dso
;
498 if (mh
->filetype
== MH_EXECUTE
&& !driverKit
) {
499 *flags
= _firehose_tracepoint_flags_pc_style_main_exe
;
500 memcpy(buf
, (uint32_t[]){ (uint32_t)shift_addr
}, sizeof(uint32_t));
501 return sizeof(uint32_t);
504 *flags
= _firehose_tracepoint_flags_pc_style_absolute
;
505 shift_addr
= driverKit
? (uintptr_t)address
: VM_KERNEL_UNSLIDE(address
);
506 size_t len
= sizeof(uintptr_t);
509 len
= 6; // 48 bits are enough
511 memcpy(buf
, (uintptr_t[]){ shift_addr
}, len
);
517 os_log_encode_location(os_log_context_t ctx
, void *addr
, void *dso
, bool driverKit
,
518 firehose_tracepoint_flags_t
*ft_flags
)
520 const size_t hdr_size
= write_address_location(ctx
->ctx_buffer
, dso
, addr
, ft_flags
, driverKit
);
521 ctx
->ctx_hdr
= (os_log_fmt_hdr_t
)&ctx
->ctx_buffer
[hdr_size
];
522 ctx
->ctx_content_sz
= (uint16_t)(ctx
->ctx_buffer_sz
- hdr_size
- sizeof(*ctx
->ctx_hdr
));
526 * Encodes argument (meta)data into a format consumed by libtrace. Stores
527 * metadada for all arguments first. Metadata also include scalar argument
528 * values. Second step saves data which are encoded separately from respective
529 * metadata (like strings).
532 os_log_context_encode(os_log_context_t ctx
, const char *fmt
, va_list args
, void *addr
, void *dso
, bool driverKit
)
534 os_log_encode_location(ctx
, addr
, dso
, driverKit
, &ctx
->ctx_ft_flags
);
537 va_copy(args_copy
, args
);
539 int rc
= log_encode_fmt(ctx
, fmt
, args
);
545 // Bogus/Unsupported fmt string
546 counter_inc(&oslog_p_fmt_invalid_msgcount
);
550 * The fmt contains unreasonable number of arguments (> 32) and
551 * we ran out of space. We could call log_expand()
552 * here and retry. However, using such formatting strings rather
553 * seem like a misuse of the logging system, hence error.
555 counter_inc(&oslog_p_fmt_max_args_msgcount
);
560 panic("unhandled return value");
563 if (ctx
->ctx_pubdata_sz
== 0) {
567 if (!log_fits(ctx
, ctx
->ctx_pubdata_sz
)) {
568 size_t space_needed
= log_context_cursor(ctx
) + ctx
->ctx_pubdata_sz
- ctx
->ctx_buffer
;
569 space_needed
= MIN(space_needed
, logmem_max_size(ctx
->ctx_logmem
));
570 (void) log_expand(ctx
, space_needed
);
573 log_encode_public_data(ctx
);
575 if (ctx
->ctx_truncated
) {
576 counter_inc(&oslog_p_truncated_msgcount
);
579 ctx
->ctx_content_sz
= (uint16_t)(log_context_cursor(ctx
) - ctx
->ctx_buffer
);
580 ctx
->ctx_content_off
= 0;
585 os_log_context_init(os_log_context_t ctx
, logmem_t
*logmem
, uint8_t *buffer
, size_t buffer_sz
)
589 assert(buffer_sz
> 0);
591 bzero(ctx
, sizeof(*ctx
));
592 ctx
->ctx_logmem
= logmem
;
593 ctx
->ctx_buffer
= buffer
;
594 ctx
->ctx_buffer_sz
= buffer_sz
;
598 os_log_context_free(os_log_context_t ctx
)
600 if (ctx
->ctx_allocated
) {
601 logmem_free(ctx
->ctx_logmem
, ctx
->ctx_buffer
, ctx
->ctx_buffer_sz
);