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
83 #ifndef __AARCH64EB__
84 data >>= 8 * offset;
85 #else
86 data <<= 8 * offset;
87 #endif
88
89 pt_regs_write_reg(regs, reg_data, data);
90
91 regs->pc = get_ex_fixup(ex);
92 return true;
93 }
94
fixup_exception(struct pt_regs * regs,unsigned long esr)95 bool fixup_exception(struct pt_regs *regs, unsigned long esr)
96 {
97 const struct exception_table_entry *ex;
98
99 ex = search_exception_tables(instruction_pointer(regs));
100 if (!ex)
101 return false;
102
103 switch (ex->type) {
104 case EX_TYPE_BPF:
105 return ex_handler_bpf(ex, regs);
106 case EX_TYPE_UACCESS_ERR_ZERO:
107 case EX_TYPE_KACCESS_ERR_ZERO:
108 return ex_handler_uaccess_err_zero(ex, regs);
109 case EX_TYPE_UACCESS_CPY:
110 return ex_handler_uaccess_cpy(ex, regs, esr);
111 case EX_TYPE_LOAD_UNALIGNED_ZEROPAD:
112 return ex_handler_load_unaligned_zeropad(ex, regs);
113 }
114
115 BUG();
116 }
117