xref: /xnu-12377.1.9/san/memory/ubsan_minimal.c (revision f6217f891ac0bb64f3d375211650a4c1ff8ca1ea)
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