Skip to content

Commit 548acf1

Browse files
aeglIngo Molnar
authored andcommitted
x86/mm: Expand the exception table logic to allow new handling options
Huge amounts of help from Andy Lutomirski and Borislav Petkov to produce this. Andy provided the inspiration to add classes to the exception table with a clever bit-squeezing trick, Boris pointed out how much cleaner it would all be if we just had a new field. Linus Torvalds blessed the expansion with: ' I'd rather not be clever in order to save just a tiny amount of space in the exception table, which isn't really criticial for anybody. ' The third field is another relative function pointer, this one to a handler that executes the actions. We start out with three handlers: 1: Legacy - just jumps the to fixup IP 2: Fault - provide the trap number in %ax to the fixup code 3: Cleaned up legacy for the uaccess error hack Signed-off-by: Tony Luck <tony.luck@intel.com> Reviewed-by: Borislav Petkov <bp@suse.de> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Thomas Gleixner <tglx@linutronix.de> Link: http://lkml.kernel.org/r/f6af78fcbd348cf4939875cfda9c19689b5e50b8.1455732970.git.tony.luck@intel.com Signed-off-by: Ingo Molnar <mingo@kernel.org>
1 parent 061f817 commit 548acf1

File tree

8 files changed

+176
-57
lines changed

8 files changed

+176
-57
lines changed

Documentation/x86/exception-tables.txt

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,3 +290,38 @@ Due to the way that the exception table is built and needs to be ordered,
290290
only use exceptions for code in the .text section. Any other section
291291
will cause the exception table to not be sorted correctly, and the
292292
exceptions will fail.
293+
294+
Things changed when 64-bit support was added to x86 Linux. Rather than
295+
double the size of the exception table by expanding the two entries
296+
from 32-bits to 64 bits, a clever trick was used to store addresses
297+
as relative offsets from the table itself. The assembly code changed
298+
from:
299+
.long 1b,3b
300+
to:
301+
.long (from) - .
302+
.long (to) - .
303+
304+
and the C-code that uses these values converts back to absolute addresses
305+
like this:
306+
307+
ex_insn_addr(const struct exception_table_entry *x)
308+
{
309+
return (unsigned long)&x->insn + x->insn;
310+
}
311+
312+
In v4.6 the exception table entry was expanded with a new field "handler".
313+
This is also 32-bits wide and contains a third relative function
314+
pointer which points to one of:
315+
316+
1) int ex_handler_default(const struct exception_table_entry *fixup)
317+
This is legacy case that just jumps to the fixup code
318+
2) int ex_handler_fault(const struct exception_table_entry *fixup)
319+
This case provides the fault number of the trap that occurred at
320+
entry->insn. It is used to distinguish page faults from machine
321+
check.
322+
3) int ex_handler_ext(const struct exception_table_entry *fixup)
323+
This case is used for uaccess_err ... we need to set a flag
324+
in the task structure. Before the handler functions existed this
325+
case was handled by adding a large offset to the fixup to tag
326+
it as special.
327+
More functions can easily be added.

arch/x86/include/asm/asm.h

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -44,19 +44,22 @@
4444

4545
/* Exception table entry */
4646
#ifdef __ASSEMBLY__
47-
# define _ASM_EXTABLE(from,to) \
47+
# define _ASM_EXTABLE_HANDLE(from, to, handler) \
4848
.pushsection "__ex_table","a" ; \
49-
.balign 8 ; \
49+
.balign 4 ; \
5050
.long (from) - . ; \
5151
.long (to) - . ; \
52+
.long (handler) - . ; \
5253
.popsection
5354

54-
# define _ASM_EXTABLE_EX(from,to) \
55-
.pushsection "__ex_table","a" ; \
56-
.balign 8 ; \
57-
.long (from) - . ; \
58-
.long (to) - . + 0x7ffffff0 ; \
59-
.popsection
55+
# define _ASM_EXTABLE(from, to) \
56+
_ASM_EXTABLE_HANDLE(from, to, ex_handler_default)
57+
58+
# define _ASM_EXTABLE_FAULT(from, to) \
59+
_ASM_EXTABLE_HANDLE(from, to, ex_handler_fault)
60+
61+
# define _ASM_EXTABLE_EX(from, to) \
62+
_ASM_EXTABLE_HANDLE(from, to, ex_handler_ext)
6063

6164
# define _ASM_NOKPROBE(entry) \
6265
.pushsection "_kprobe_blacklist","aw" ; \
@@ -89,19 +92,24 @@
8992
.endm
9093

9194
#else
92-
# define _ASM_EXTABLE(from,to) \
95+
# define _EXPAND_EXTABLE_HANDLE(x) #x
96+
# define _ASM_EXTABLE_HANDLE(from, to, handler) \
9397
" .pushsection \"__ex_table\",\"a\"\n" \
94-
" .balign 8\n" \
98+
" .balign 4\n" \
9599
" .long (" #from ") - .\n" \
96100
" .long (" #to ") - .\n" \
101+
" .long (" _EXPAND_EXTABLE_HANDLE(handler) ") - .\n" \
97102
" .popsection\n"
98103

99-
# define _ASM_EXTABLE_EX(from,to) \
100-
" .pushsection \"__ex_table\",\"a\"\n" \
101-
" .balign 8\n" \
102-
" .long (" #from ") - .\n" \
103-
" .long (" #to ") - . + 0x7ffffff0\n" \
104-
" .popsection\n"
104+
# define _ASM_EXTABLE(from, to) \
105+
_ASM_EXTABLE_HANDLE(from, to, ex_handler_default)
106+
107+
# define _ASM_EXTABLE_FAULT(from, to) \
108+
_ASM_EXTABLE_HANDLE(from, to, ex_handler_fault)
109+
110+
# define _ASM_EXTABLE_EX(from, to) \
111+
_ASM_EXTABLE_HANDLE(from, to, ex_handler_ext)
112+
105113
/* For C file, we already have NOKPROBE_SYMBOL macro */
106114
#endif
107115

arch/x86/include/asm/uaccess.h

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -90,12 +90,11 @@ static inline bool __chk_range_not_ok(unsigned long addr, unsigned long size, un
9090
likely(!__range_not_ok(addr, size, user_addr_max()))
9191

9292
/*
93-
* The exception table consists of pairs of addresses relative to the
94-
* exception table enty itself: the first is the address of an
95-
* instruction that is allowed to fault, and the second is the address
96-
* at which the program should continue. No registers are modified,
97-
* so it is entirely up to the continuation code to figure out what to
98-
* do.
93+
* The exception table consists of triples of addresses relative to the
94+
* exception table entry itself. The first address is of an instruction
95+
* that is allowed to fault, the second is the target at which the program
96+
* should continue. The third is a handler function to deal with the fault
97+
* caused by the instruction in the first field.
9998
*
10099
* All the routines below use bits of fixup code that are out of line
101100
* with the main instruction path. This means when everything is well,
@@ -104,13 +103,14 @@ static inline bool __chk_range_not_ok(unsigned long addr, unsigned long size, un
104103
*/
105104

106105
struct exception_table_entry {
107-
int insn, fixup;
106+
int insn, fixup, handler;
108107
};
109108
/* This is not the generic standard exception_table_entry format */
110109
#define ARCH_HAS_SORT_EXTABLE
111110
#define ARCH_HAS_SEARCH_EXTABLE
112111

113-
extern int fixup_exception(struct pt_regs *regs);
112+
extern int fixup_exception(struct pt_regs *regs, int trapnr);
113+
extern bool ex_has_fault_handler(unsigned long ip);
114114
extern int early_fixup_exception(unsigned long *ip);
115115

116116
/*

arch/x86/kernel/kprobes/core.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -988,7 +988,7 @@ int kprobe_fault_handler(struct pt_regs *regs, int trapnr)
988988
* In case the user-specified fault handler returned
989989
* zero, try to fix up.
990990
*/
991-
if (fixup_exception(regs))
991+
if (fixup_exception(regs, trapnr))
992992
return 1;
993993

994994
/*

arch/x86/kernel/traps.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ do_trap_no_signal(struct task_struct *tsk, int trapnr, char *str,
199199
}
200200

201201
if (!user_mode(regs)) {
202-
if (!fixup_exception(regs)) {
202+
if (!fixup_exception(regs, trapnr)) {
203203
tsk->thread.error_code = error_code;
204204
tsk->thread.trap_nr = trapnr;
205205
die(str, regs, error_code);
@@ -453,7 +453,7 @@ do_general_protection(struct pt_regs *regs, long error_code)
453453

454454
tsk = current;
455455
if (!user_mode(regs)) {
456-
if (fixup_exception(regs))
456+
if (fixup_exception(regs, X86_TRAP_GP))
457457
return;
458458

459459
tsk->thread.error_code = error_code;
@@ -699,7 +699,7 @@ static void math_error(struct pt_regs *regs, int error_code, int trapnr)
699699
conditional_sti(regs);
700700

701701
if (!user_mode(regs)) {
702-
if (!fixup_exception(regs)) {
702+
if (!fixup_exception(regs, trapnr)) {
703703
task->thread.error_code = error_code;
704704
task->thread.trap_nr = trapnr;
705705
die(str, regs, error_code);

arch/x86/mm/extable.c

Lines changed: 72 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
#include <linux/sort.h>
44
#include <asm/uaccess.h>
55

6+
typedef bool (*ex_handler_t)(const struct exception_table_entry *,
7+
struct pt_regs *, int);
8+
69
static inline unsigned long
710
ex_insn_addr(const struct exception_table_entry *x)
811
{
@@ -13,11 +16,56 @@ ex_fixup_addr(const struct exception_table_entry *x)
1316
{
1417
return (unsigned long)&x->fixup + x->fixup;
1518
}
19+
static inline ex_handler_t
20+
ex_fixup_handler(const struct exception_table_entry *x)
21+
{
22+
return (ex_handler_t)((unsigned long)&x->handler + x->handler);
23+
}
1624

17-
int fixup_exception(struct pt_regs *regs)
25+
bool ex_handler_default(const struct exception_table_entry *fixup,
26+
struct pt_regs *regs, int trapnr)
1827
{
19-
const struct exception_table_entry *fixup;
20-
unsigned long new_ip;
28+
regs->ip = ex_fixup_addr(fixup);
29+
return true;
30+
}
31+
EXPORT_SYMBOL(ex_handler_default);
32+
33+
bool ex_handler_fault(const struct exception_table_entry *fixup,
34+
struct pt_regs *regs, int trapnr)
35+
{
36+
regs->ip = ex_fixup_addr(fixup);
37+
regs->ax = trapnr;
38+
return true;
39+
}
40+
EXPORT_SYMBOL_GPL(ex_handler_fault);
41+
42+
bool ex_handler_ext(const struct exception_table_entry *fixup,
43+
struct pt_regs *regs, int trapnr)
44+
{
45+
/* Special hack for uaccess_err */
46+
current_thread_info()->uaccess_err = 1;
47+
regs->ip = ex_fixup_addr(fixup);
48+
return true;
49+
}
50+
EXPORT_SYMBOL(ex_handler_ext);
51+
52+
bool ex_has_fault_handler(unsigned long ip)
53+
{
54+
const struct exception_table_entry *e;
55+
ex_handler_t handler;
56+
57+
e = search_exception_tables(ip);
58+
if (!e)
59+
return false;
60+
handler = ex_fixup_handler(e);
61+
62+
return handler == ex_handler_fault;
63+
}
64+
65+
int fixup_exception(struct pt_regs *regs, int trapnr)
66+
{
67+
const struct exception_table_entry *e;
68+
ex_handler_t handler;
2169

2270
#ifdef CONFIG_PNPBIOS
2371
if (unlikely(SEGMENT_IS_PNP_CODE(regs->cs))) {
@@ -33,42 +81,34 @@ int fixup_exception(struct pt_regs *regs)
3381
}
3482
#endif
3583

36-
fixup = search_exception_tables(regs->ip);
37-
if (fixup) {
38-
new_ip = ex_fixup_addr(fixup);
39-
40-
if (fixup->fixup - fixup->insn >= 0x7ffffff0 - 4) {
41-
/* Special hack for uaccess_err */
42-
current_thread_info()->uaccess_err = 1;
43-
new_ip -= 0x7ffffff0;
44-
}
45-
regs->ip = new_ip;
46-
return 1;
47-
}
84+
e = search_exception_tables(regs->ip);
85+
if (!e)
86+
return 0;
4887

49-
return 0;
88+
handler = ex_fixup_handler(e);
89+
return handler(e, regs, trapnr);
5090
}
5191

5292
/* Restricted version used during very early boot */
5393
int __init early_fixup_exception(unsigned long *ip)
5494
{
55-
const struct exception_table_entry *fixup;
95+
const struct exception_table_entry *e;
5696
unsigned long new_ip;
97+
ex_handler_t handler;
5798

58-
fixup = search_exception_tables(*ip);
59-
if (fixup) {
60-
new_ip = ex_fixup_addr(fixup);
99+
e = search_exception_tables(*ip);
100+
if (!e)
101+
return 0;
61102

62-
if (fixup->fixup - fixup->insn >= 0x7ffffff0 - 4) {
63-
/* uaccess handling not supported during early boot */
64-
return 0;
65-
}
103+
new_ip = ex_fixup_addr(e);
104+
handler = ex_fixup_handler(e);
66105

67-
*ip = new_ip;
68-
return 1;
69-
}
106+
/* special handling not supported during early boot */
107+
if (handler != ex_handler_default)
108+
return 0;
70109

71-
return 0;
110+
*ip = new_ip;
111+
return 1;
72112
}
73113

74114
/*
@@ -133,6 +173,8 @@ void sort_extable(struct exception_table_entry *start,
133173
i += 4;
134174
p->fixup += i;
135175
i += 4;
176+
p->handler += i;
177+
i += 4;
136178
}
137179

138180
sort(start, finish - start, sizeof(struct exception_table_entry),
@@ -145,6 +187,8 @@ void sort_extable(struct exception_table_entry *start,
145187
i += 4;
146188
p->fixup -= i;
147189
i += 4;
190+
p->handler -= i;
191+
i += 4;
148192
}
149193
}
150194

arch/x86/mm/fault.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -656,7 +656,7 @@ no_context(struct pt_regs *regs, unsigned long error_code,
656656
int sig;
657657

658658
/* Are we prepared to handle this kernel fault? */
659-
if (fixup_exception(regs)) {
659+
if (fixup_exception(regs, X86_TRAP_PF)) {
660660
/*
661661
* Any interrupt that takes a fault gets the fixup. This makes
662662
* the below recursive fault logic only apply to a faults from

scripts/sortextable.c

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,35 @@ static int compare_relative_table(const void *a, const void *b)
209209
return 0;
210210
}
211211

212+
static void x86_sort_relative_table(char *extab_image, int image_size)
213+
{
214+
int i;
215+
216+
i = 0;
217+
while (i < image_size) {
218+
uint32_t *loc = (uint32_t *)(extab_image + i);
219+
220+
w(r(loc) + i, loc);
221+
w(r(loc + 1) + i + 4, loc + 1);
222+
w(r(loc + 2) + i + 8, loc + 2);
223+
224+
i += sizeof(uint32_t) * 3;
225+
}
226+
227+
qsort(extab_image, image_size / 12, 12, compare_relative_table);
228+
229+
i = 0;
230+
while (i < image_size) {
231+
uint32_t *loc = (uint32_t *)(extab_image + i);
232+
233+
w(r(loc) - i, loc);
234+
w(r(loc + 1) - (i + 4), loc + 1);
235+
w(r(loc + 2) - (i + 8), loc + 2);
236+
237+
i += sizeof(uint32_t) * 3;
238+
}
239+
}
240+
212241
static void sort_relative_table(char *extab_image, int image_size)
213242
{
214243
int i;
@@ -281,6 +310,9 @@ do_file(char const *const fname)
281310
break;
282311
case EM_386:
283312
case EM_X86_64:
313+
custom_sort = x86_sort_relative_table;
314+
break;
315+
284316
case EM_S390:
285317
custom_sort = sort_relative_table;
286318
break;

0 commit comments

Comments
 (0)