1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Based on arch/arm/mm/extable.c
4 */
5
6 #include <linux/bitfield.h>
7 #include <linux/extable.h>
8 #include <linux/uaccess.h>
9
10 #include <asm/asm-extable.h>
11 #include <asm/esr.h>
12 #include <asm/ptrace.h>
13
cpy_faulted_on_uaccess(const struct exception_table_entry * ex,unsigned long esr)14 static bool cpy_faulted_on_uaccess(const struct exception_table_entry *ex,
15 unsigned long esr)
16 {
17 bool uaccess_is_write = FIELD_GET(EX_DATA_UACCESS_WRITE, ex->data);
18 bool fault_on_write = esr & ESR_ELx_WNR;
19
20 return uaccess_is_write == fault_on_write;
21 }
22
insn_may_access_user(unsigned long addr,unsigned long esr)23 bool insn_may_access_user(unsigned long addr, unsigned long esr)
24 {
25 const struct exception_table_entry *ex = search_exception_tables(addr);
26
27 if (!ex)
28 return false;
29
30 switch (ex->type) {
31 case EX_TYPE_UACCESS_CPY:
32 return cpy_faulted_on_uaccess(ex, esr);
33 default:
34 return true;
35 }
36 }
37
38 static inline unsigned long
get_ex_fixup(const struct exception_table_entry * ex)39 get_ex_fixup(const struct exception_table_entry *ex)
40 {
41 return ((unsigned long)&ex->fixup + ex->fixup);
42 }
43
ex_handler_uaccess_err_zero(const struct exception_table_entry * ex,struct pt_regs * regs)44 static bool ex_handler_uaccess_err_zero(const struct exception_table_entry *ex,
45 struct pt_regs *regs)
46 {
47 int reg_err = FIELD_GET(EX_DATA_REG_ERR, ex->data);
48 int reg_zero = FIELD_GET(EX_DATA_REG_ZERO, ex->data);
49
50 pt_regs_write_reg(regs, reg_err, -EFAULT);
51 pt_regs_write_reg(regs, reg_zero, 0);
52
53 regs->pc = get_ex_fixup(ex);
54 return true;
55 }
56
ex_handler_uaccess_cpy(const struct exception_table_entry * ex,struct pt_regs * regs,unsigned long esr)57 static bool ex_handler_uaccess_cpy(const struct exception_table_entry *ex,
58 struct pt_regs *regs, unsigned long esr)
59 {
60 /* Do not fix up faults on kernel memory accesses */
61 if (!cpy_faulted_on_uaccess(ex, esr))
62 return false;
63
64 regs->pc = get_ex_fixup(ex);
65 return true;
66 }
67
68 static bool
ex_handler_load_unaligned_zeropad(const struct exception_table_entry * ex,struct pt_regs * regs)69 ex_handler_load_unaligned_zeropad(const struct exception_table_entry *ex,
70 struct pt_regs *regs)
71 {
72 int reg_data = FIELD_GET(EX_DATA_REG_DATA, ex->data);
73 int reg_addr = FIELD_GET(EX_DATA_REG_ADDR, ex->data);
74 unsigned long data, addr, offset;
75
76 addr = pt_regs_read_reg(regs, reg_addr);
77
78 offset = addr & 0x7UL;
79 addr &= ~0x7UL;
80
81 data = *(unsigned long*)addr;
82 data >>= 8 * offset;
83
84 pt_regs_write_reg(regs, reg_data, data);
85
86 regs->pc = get_ex_fixup(ex);
87 return true;
88 }
89
fixup_exception(struct pt_regs * regs,unsigned long esr)90 bool fixup_exception(struct pt_regs *regs, unsigned long esr)
91 {
92 const struct exception_table_entry *ex;
93
94 ex = search_exception_tables(instruction_pointer(regs));
95 if (!ex)
96 return false;
97
98 switch (ex->type) {
99 case EX_TYPE_BPF:
100 return ex_handler_bpf(ex, regs);
101 case EX_TYPE_UACCESS_ERR_ZERO:
102 case EX_TYPE_KACCESS_ERR_ZERO:
103 return ex_handler_uaccess_err_zero(ex, regs);
104 case EX_TYPE_UACCESS_CPY:
105 return ex_handler_uaccess_cpy(ex, regs, esr);
106 case EX_TYPE_LOAD_UNALIGNED_ZEROPAD:
107 return ex_handler_load_unaligned_zeropad(ex, regs);
108 }
109
110 BUG();
111 }
112