42
42
#include <linux/hardirq.h>
43
43
#include <linux/atomic.h>
44
44
#include <linux/iommu.h>
45
+ #include <linux/ubsan.h>
45
46
46
47
#include <asm/stacktrace.h>
47
48
#include <asm/processor.h>
@@ -91,6 +92,47 @@ __always_inline int is_valid_bugaddr(unsigned long addr)
91
92
return * (unsigned short * )addr == INSN_UD2 ;
92
93
}
93
94
95
+ /*
96
+ * Check for UD1 or UD2, accounting for Address Size Override Prefixes.
97
+ * If it's a UD1, get the ModRM byte to pass along to UBSan.
98
+ */
99
+ __always_inline int decode_bug (unsigned long addr , u32 * imm )
100
+ {
101
+ u8 v ;
102
+
103
+ if (addr < TASK_SIZE_MAX )
104
+ return BUG_NONE ;
105
+
106
+ v = * (u8 * )(addr ++ );
107
+ if (v == INSN_ASOP )
108
+ v = * (u8 * )(addr ++ );
109
+ if (v != OPCODE_ESCAPE )
110
+ return BUG_NONE ;
111
+
112
+ v = * (u8 * )(addr ++ );
113
+ if (v == SECOND_BYTE_OPCODE_UD2 )
114
+ return BUG_UD2 ;
115
+
116
+ if (!IS_ENABLED (CONFIG_UBSAN_TRAP ) || v != SECOND_BYTE_OPCODE_UD1 )
117
+ return BUG_NONE ;
118
+
119
+ /* Retrieve the immediate (type value) for the UBSAN UD1 */
120
+ v = * (u8 * )(addr ++ );
121
+ if (X86_MODRM_RM (v ) == 4 )
122
+ addr ++ ;
123
+
124
+ * imm = 0 ;
125
+ if (X86_MODRM_MOD (v ) == 1 )
126
+ * imm = * (u8 * )addr ;
127
+ else if (X86_MODRM_MOD (v ) == 2 )
128
+ * imm = * (u32 * )addr ;
129
+ else
130
+ WARN_ONCE (1 , "Unexpected MODRM_MOD: %u\n" , X86_MODRM_MOD (v ));
131
+
132
+ return BUG_UD1 ;
133
+ }
134
+
135
+
94
136
static nokprobe_inline int
95
137
do_trap_no_signal (struct task_struct * tsk , int trapnr , const char * str ,
96
138
struct pt_regs * regs , long error_code )
@@ -216,14 +258,17 @@ static inline void handle_invalid_op(struct pt_regs *regs)
216
258
static noinstr bool handle_bug (struct pt_regs * regs )
217
259
{
218
260
bool handled = false;
261
+ int ud_type ;
262
+ u32 imm ;
219
263
220
264
/*
221
265
* Normally @regs are unpoisoned by irqentry_enter(), but handle_bug()
222
266
* is a rare case that uses @regs without passing them to
223
267
* irqentry_enter().
224
268
*/
225
269
kmsan_unpoison_entry_regs (regs );
226
- if (!is_valid_bugaddr (regs -> ip ))
270
+ ud_type = decode_bug (regs -> ip , & imm );
271
+ if (ud_type == BUG_NONE )
227
272
return handled ;
228
273
229
274
/*
@@ -236,10 +281,14 @@ static noinstr bool handle_bug(struct pt_regs *regs)
236
281
*/
237
282
if (regs -> flags & X86_EFLAGS_IF )
238
283
raw_local_irq_enable ();
239
- if (report_bug (regs -> ip , regs ) == BUG_TRAP_TYPE_WARN ||
240
- handle_cfi_failure (regs ) == BUG_TRAP_TYPE_WARN ) {
241
- regs -> ip += LEN_UD2 ;
242
- handled = true;
284
+ if (ud_type == BUG_UD2 ) {
285
+ if (report_bug (regs -> ip , regs ) == BUG_TRAP_TYPE_WARN ||
286
+ handle_cfi_failure (regs ) == BUG_TRAP_TYPE_WARN ) {
287
+ regs -> ip += LEN_UD2 ;
288
+ handled = true;
289
+ }
290
+ } else if (IS_ENABLED (CONFIG_UBSAN_TRAP )) {
291
+ pr_crit ("%s at %pS\n" , report_ubsan_failure (regs , imm ), (void * )regs -> ip );
243
292
}
244
293
if (regs -> flags & X86_EFLAGS_IF )
245
294
raw_local_irq_disable ();
0 commit comments