1 /*
2 * Copyright (c) 2021 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_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. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29 /*
30 * UBSan minimal runtime for production environments
31 * This runtime emulates an inlined trap model, but gated through the
32 * logging functions, so that fix and continue is always possible just by
33 * advancing the program counter.
34 * NOTE: this file is regularly instrumented, since all helpers must inline
35 * correctly to a trap.
36 */
37 #include <sys/sysctl.h>
38 #include <kern/kalloc.h>
39 #include <kern/trap_telemetry.h>
40 #include <machine/machine_routines.h>
41 #include <san/ubsan_minimal.h>
42
43 #define UBSAN_M_NONE (0x0000)
44 #define UBSAN_M_PANIC (0x0001)
45
46 #define UBSAN_MINIMAL_TRAPS_START UBSAN_SOFT_TRAP_SIGNED_OF
47 #define UBSAN_MINIMAL_TRAPS_END UBSAN_SOFT_TRAP_SIGNED_OF
48
49 struct ubsan_minimal_trap_desc {
50 uint16_t id;
51 uint32_t flags;
52 char str[16];
53 };
54
55 #if RELEASE
56 static __security_const_late
57 #endif /* RELEASE */
58 struct ubsan_minimal_trap_desc ubsan_traps[] = {
59 { UBSAN_SOFT_TRAP_SIGNED_OF, UBSAN_M_NONE, "signed-overflow" },
60 };
61
62 static SECURITY_READ_ONLY_LATE(bool) ubsan_minimal_enabled = false;
63
64 /* Helper counters for fixup/drop operations */
65 static uint32_t ubsan_minimal_fixup_events;
66
67 static char *
ubsan_minimal_trap_to_str(uint16_t trap)68 ubsan_minimal_trap_to_str(uint16_t trap)
69 {
70 return ubsan_traps[trap - UBSAN_MINIMAL_TRAPS_START].str;
71 }
72
73 void
ubsan_handle_brk_trap(__unused void * state,uint16_t trap)74 ubsan_handle_brk_trap(
75 __unused void *state,
76 uint16_t trap)
77 {
78 if (!ubsan_minimal_enabled) {
79 #if DEVELOPMENT
80 /* We want to know about those sooner than later... */
81 panic("UBSAN trap taken in early startup code");
82 #endif /* DEVELOPMENT */
83 return;
84 }
85
86 uint32_t trap_idx = trap - UBSAN_MINIMAL_TRAPS_START;
87
88 if (ubsan_traps[trap_idx].flags & UBSAN_M_PANIC) {
89 panic("UBSAN trap for %s detected\n", ubsan_minimal_trap_to_str(trap));
90 __builtin_unreachable();
91 }
92
93 ubsan_minimal_fixup_events++;
94 }
95
96 __startup_func
97 void
ubsan_minimal_init(void)98 ubsan_minimal_init(void)
99 {
100 #if DEVELOPMENT || DEBUG
101 bool force_panic = false;
102 PE_parse_boot_argn("-ubsan_force_panic", &force_panic, sizeof(force_panic));
103 if (force_panic) {
104 for (int i = UBSAN_MINIMAL_TRAPS_START; i <= UBSAN_MINIMAL_TRAPS_END; i++) {
105 size_t idx = i - UBSAN_MINIMAL_TRAPS_START;
106 ubsan_traps[idx].flags |= UBSAN_M_PANIC;
107 }
108 }
109 #endif /* DEVELOPMENT || DEBUG */
110
111 ubsan_minimal_enabled = true;
112 }
113
114 #if DEVELOPMENT || DEBUG
115
116 /* Add a simple testing path that explicitly triggers a signed int overflow */
117 static int
118 sysctl_ubsan_test SYSCTL_HANDLER_ARGS
119 {
120 #pragma unused(oidp, arg1, arg2)
121 int err, incr = 0;
122 err = sysctl_io_number(req, 0, sizeof(int), &incr, NULL);
123
124 int k = 0x7fffffff;
125
126 for (int i = 0; i < 3; i++) {
127 int a = k;
128 if (incr != 0) {
129 k += incr;
130 }
131
132 k = a;
133 }
134
135 return err;
136 }
137
138 SYSCTL_DECL(kern_ubsan);
139 SYSCTL_NODE(_kern, OID_AUTO, ubsan, CTLFLAG_RW | CTLFLAG_LOCKED, 0, "ubsan");
140 SYSCTL_NODE(_kern_ubsan, OID_AUTO, minimal, CTLFLAG_RW | CTLFLAG_LOCKED, 0, "minimal runtime");
141 SYSCTL_INT(_kern_ubsan_minimal, OID_AUTO, fixups, CTLFLAG_RW | CTLFLAG_LOCKED, &ubsan_minimal_fixup_events, 0, "");
142 SYSCTL_INT(_kern_ubsan_minimal, OID_AUTO, signed_ovf_flags, CTLFLAG_RW | CTLFLAG_LOCKED, &(ubsan_traps[0].flags), 0, "");
143 SYSCTL_PROC(_kern_ubsan_minimal, OID_AUTO, test, CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_LOCKED,
144 0, 0, sysctl_ubsan_test, "I", "Test signed overflow detection");
145
146 #endif /* DEVELOPMENT || DEBUG */
147
148 #define UBSAN_M_ATTR __attribute__((always_inline, cold))
149
150 UBSAN_M_ATTR void
__ubsan_handle_divrem_overflow_minimal(void)151 __ubsan_handle_divrem_overflow_minimal(void)
152 {
153 asm volatile ("brk #%0" : : "i"(UBSAN_SOFT_TRAP_SIGNED_OF));
154 }
155
156 UBSAN_M_ATTR void
__ubsan_handle_negate_overflow_minimal(void)157 __ubsan_handle_negate_overflow_minimal(void)
158 {
159 asm volatile ("brk #%0" : : "i"(UBSAN_SOFT_TRAP_SIGNED_OF));
160 }
161
162 UBSAN_M_ATTR void
__ubsan_handle_mul_overflow_minimal(void)163 __ubsan_handle_mul_overflow_minimal(void)
164 {
165 asm volatile ("brk #%0" : : "i"(UBSAN_SOFT_TRAP_SIGNED_OF));
166 }
167
168 UBSAN_M_ATTR void
__ubsan_handle_sub_overflow_minimal(void)169 __ubsan_handle_sub_overflow_minimal(void)
170 {
171 asm volatile ("brk #%0" : : "i"(UBSAN_SOFT_TRAP_SIGNED_OF));
172 }
173
174 UBSAN_M_ATTR void
__ubsan_handle_add_overflow_minimal(void)175 __ubsan_handle_add_overflow_minimal(void)
176 {
177 asm volatile ("brk #%0" : : "i"(UBSAN_SOFT_TRAP_SIGNED_OF));
178 }
179