Skip to content

Commit 8b1d723

Browse files
committed
parisc: Fix random data corruption from exception handler
The current exception handler implementation, which assists when accessing user space memory, may exhibit random data corruption if the compiler decides to use a different register than the specified register %r29 (defined in ASM_EXCEPTIONTABLE_REG) for the error code. If the compiler choose another register, the fault handler will nevertheless store -EFAULT into %r29 and thus trash whatever this register is used for. Looking at the assembly I found that this happens sometimes in emulate_ldd(). To solve the issue, the easiest solution would be if it somehow is possible to tell the fault handler which register is used to hold the error code. Using %0 or %1 in the inline assembly is not posssible as it will show up as e.g. %r29 (with the "%r" prefix), which the GNU assembler can not convert to an integer. This patch takes another, better and more flexible approach: We extend the __ex_table (which is out of the execution path) by one 32-word. In this word we tell the compiler to insert the assembler instruction "or %r0,%r0,%reg", where %reg references the register which the compiler choosed for the error return code. In case of an access failure, the fault handler finds the __ex_table entry and can examine the opcode. The used register is encoded in the lowest 5 bits, and the fault handler can then store -EFAULT into this register. Since we extend the __ex_table to 3 words we can't use the BUILDTIME_TABLE_SORT config option any longer. Signed-off-by: Helge Deller <[email protected]> Cc: <[email protected]> # v6.0+
1 parent 20e08a7 commit 8b1d723

File tree

8 files changed

+108
-71
lines changed

8 files changed

+108
-71
lines changed

arch/parisc/Kconfig

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ config PARISC
2525
select RTC_DRV_GENERIC
2626
select INIT_ALL_POSSIBLE
2727
select BUG
28-
select BUILDTIME_TABLE_SORT
2928
select HAVE_KERNEL_UNCOMPRESSED
3029
select HAVE_PCI
3130
select HAVE_PERF_EVENTS

arch/parisc/include/asm/assembly.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,7 @@
576576
.section __ex_table,"aw" ! \
577577
.align 4 ! \
578578
.word (fault_addr - .), (except_addr - .) ! \
579+
or %r0,%r0,%r0 ! \
579580
.previous
580581

581582

arch/parisc/include/asm/extable.h

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
#ifndef __PARISC_EXTABLE_H
3+
#define __PARISC_EXTABLE_H
4+
5+
#include <asm/ptrace.h>
6+
#include <linux/compiler.h>
7+
8+
/*
9+
* The exception table consists of three addresses:
10+
*
11+
* - A relative address to the instruction that is allowed to fault.
12+
* - A relative address at which the program should continue (fixup routine)
13+
* - An asm statement which specifies which CPU register will
14+
* receive -EFAULT when an exception happens if the lowest bit in
15+
* the fixup address is set.
16+
*
17+
* Note: The register specified in the err_opcode instruction will be
18+
* modified at runtime if a fault happens. Register %r0 will be ignored.
19+
*
20+
* Since relative addresses are used, 32bit values are sufficient even on
21+
* 64bit kernel.
22+
*/
23+
24+
struct pt_regs;
25+
int fixup_exception(struct pt_regs *regs);
26+
27+
#define ARCH_HAS_RELATIVE_EXTABLE
28+
struct exception_table_entry {
29+
int insn; /* relative address of insn that is allowed to fault. */
30+
int fixup; /* relative address of fixup routine */
31+
int err_opcode; /* sample opcode with register which holds error code */
32+
};
33+
34+
#define ASM_EXCEPTIONTABLE_ENTRY( fault_addr, except_addr, opcode )\
35+
".section __ex_table,\"aw\"\n" \
36+
".align 4\n" \
37+
".word (" #fault_addr " - .), (" #except_addr " - .)\n" \
38+
opcode "\n" \
39+
".previous\n"
40+
41+
/*
42+
* ASM_EXCEPTIONTABLE_ENTRY_EFAULT() creates a special exception table entry
43+
* (with lowest bit set) for which the fault handler in fixup_exception() will
44+
* load -EFAULT on fault into the register specified by the err_opcode instruction,
45+
* and zeroes the target register in case of a read fault in get_user().
46+
*/
47+
#define ASM_EXCEPTIONTABLE_VAR(__err_var) \
48+
int __err_var = 0
49+
#define ASM_EXCEPTIONTABLE_ENTRY_EFAULT( fault_addr, except_addr, register )\
50+
ASM_EXCEPTIONTABLE_ENTRY( fault_addr, except_addr + 1, "or %%r0,%%r0," register)
51+
52+
static inline void swap_ex_entry_fixup(struct exception_table_entry *a,
53+
struct exception_table_entry *b,
54+
struct exception_table_entry tmp,
55+
int delta)
56+
{
57+
a->fixup = b->fixup + delta;
58+
b->fixup = tmp.fixup - delta;
59+
a->err_opcode = b->err_opcode;
60+
b->err_opcode = tmp.err_opcode;
61+
}
62+
#define swap_ex_entry_fixup swap_ex_entry_fixup
63+
64+
#endif

arch/parisc/include/asm/special_insns.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
"copy %%r0,%0\n" \
99
"8:\tlpa %%r0(%1),%0\n" \
1010
"9:\n" \
11-
ASM_EXCEPTIONTABLE_ENTRY(8b, 9b) \
11+
ASM_EXCEPTIONTABLE_ENTRY(8b, 9b, \
12+
"or %%r0,%%r0,%%r0") \
1213
: "=&r" (pa) \
1314
: "r" (va) \
1415
: "memory" \
@@ -22,7 +23,8 @@
2223
"copy %%r0,%0\n" \
2324
"8:\tlpa %%r0(%%sr3,%1),%0\n" \
2425
"9:\n" \
25-
ASM_EXCEPTIONTABLE_ENTRY(8b, 9b) \
26+
ASM_EXCEPTIONTABLE_ENTRY(8b, 9b, \
27+
"or %%r0,%%r0,%%r0") \
2628
: "=&r" (pa) \
2729
: "r" (va) \
2830
: "memory" \

arch/parisc/include/asm/uaccess.h

Lines changed: 7 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
*/
88
#include <asm/page.h>
99
#include <asm/cache.h>
10+
#include <asm/extable.h>
1011

1112
#include <linux/bug.h>
1213
#include <linux/string.h>
@@ -26,37 +27,6 @@
2627
#define STD_USER(sr, x, ptr) __put_user_asm(sr, "std", x, ptr)
2728
#endif
2829

29-
/*
30-
* The exception table contains two values: the first is the relative offset to
31-
* the address of the instruction that is allowed to fault, and the second is
32-
* the relative offset to the address of the fixup routine. Since relative
33-
* addresses are used, 32bit values are sufficient even on 64bit kernel.
34-
*/
35-
36-
#define ARCH_HAS_RELATIVE_EXTABLE
37-
struct exception_table_entry {
38-
int insn; /* relative address of insn that is allowed to fault. */
39-
int fixup; /* relative address of fixup routine */
40-
};
41-
42-
#define ASM_EXCEPTIONTABLE_ENTRY( fault_addr, except_addr )\
43-
".section __ex_table,\"aw\"\n" \
44-
".align 4\n" \
45-
".word (" #fault_addr " - .), (" #except_addr " - .)\n\t" \
46-
".previous\n"
47-
48-
/*
49-
* ASM_EXCEPTIONTABLE_ENTRY_EFAULT() creates a special exception table entry
50-
* (with lowest bit set) for which the fault handler in fixup_exception() will
51-
* load -EFAULT into %r29 for a read or write fault, and zeroes the target
52-
* register in case of a read fault in get_user().
53-
*/
54-
#define ASM_EXCEPTIONTABLE_REG 29
55-
#define ASM_EXCEPTIONTABLE_VAR(__variable) \
56-
register long __variable __asm__ ("r29") = 0
57-
#define ASM_EXCEPTIONTABLE_ENTRY_EFAULT( fault_addr, except_addr )\
58-
ASM_EXCEPTIONTABLE_ENTRY( fault_addr, except_addr + 1)
59-
6030
#define __get_user_internal(sr, val, ptr) \
6131
({ \
6232
ASM_EXCEPTIONTABLE_VAR(__gu_err); \
@@ -83,7 +53,7 @@ struct exception_table_entry {
8353
\
8454
__asm__("1: " ldx " 0(%%sr%2,%3),%0\n" \
8555
"9:\n" \
86-
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 9b) \
56+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 9b, "%1") \
8757
: "=r"(__gu_val), "+r"(__gu_err) \
8858
: "i"(sr), "r"(ptr)); \
8959
\
@@ -115,8 +85,8 @@ struct exception_table_entry {
11585
"1: ldw 0(%%sr%2,%3),%0\n" \
11686
"2: ldw 4(%%sr%2,%3),%R0\n" \
11787
"9:\n" \
118-
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 9b) \
119-
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 9b) \
88+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 9b, "%1") \
89+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 9b, "%1") \
12090
: "=&r"(__gu_tmp.l), "+r"(__gu_err) \
12191
: "i"(sr), "r"(ptr)); \
12292
\
@@ -174,7 +144,7 @@ struct exception_table_entry {
174144
__asm__ __volatile__ ( \
175145
"1: " stx " %1,0(%%sr%2,%3)\n" \
176146
"9:\n" \
177-
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 9b) \
147+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 9b, "%0") \
178148
: "+r"(__pu_err) \
179149
: "r"(x), "i"(sr), "r"(ptr))
180150

@@ -186,15 +156,14 @@ struct exception_table_entry {
186156
"1: stw %1,0(%%sr%2,%3)\n" \
187157
"2: stw %R1,4(%%sr%2,%3)\n" \
188158
"9:\n" \
189-
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 9b) \
190-
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 9b) \
159+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 9b, "%0") \
160+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 9b, "%0") \
191161
: "+r"(__pu_err) \
192162
: "r"(__val), "i"(sr), "r"(ptr)); \
193163
} while (0)
194164

195165
#endif /* !defined(CONFIG_64BIT) */
196166

197-
198167
/*
199168
* Complex access routines -- external declarations
200169
*/
@@ -216,7 +185,4 @@ unsigned long __must_check raw_copy_from_user(void *dst, const void __user *src,
216185
#define INLINE_COPY_TO_USER
217186
#define INLINE_COPY_FROM_USER
218187

219-
struct pt_regs;
220-
int fixup_exception(struct pt_regs *regs);
221-
222188
#endif /* __PARISC_UACCESS_H */

arch/parisc/kernel/cache.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -854,7 +854,7 @@ SYSCALL_DEFINE3(cacheflush, unsigned long, addr, unsigned long, bytes,
854854
#endif
855855
" fic,m %3(%4,%0)\n"
856856
"2: sync\n"
857-
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 2b)
857+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 2b, "%1")
858858
: "+r" (start), "+r" (error)
859859
: "r" (end), "r" (dcache_stride), "i" (SR_USER));
860860
}
@@ -869,7 +869,7 @@ SYSCALL_DEFINE3(cacheflush, unsigned long, addr, unsigned long, bytes,
869869
#endif
870870
" fdc,m %3(%4,%0)\n"
871871
"2: sync\n"
872-
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 2b)
872+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 2b, "%1")
873873
: "+r" (start), "+r" (error)
874874
: "r" (end), "r" (icache_stride), "i" (SR_USER));
875875
}

arch/parisc/kernel/unaligned.c

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,8 @@ static int emulate_ldh(struct pt_regs *regs, int toreg)
120120
"2: ldbs 1(%%sr1,%3), %0\n"
121121
" depw %2, 23, 24, %0\n"
122122
"3: \n"
123-
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 3b)
124-
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 3b)
123+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 3b, "%1")
124+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 3b, "%1")
125125
: "+r" (val), "+r" (ret), "=&r" (temp1)
126126
: "r" (saddr), "r" (regs->isr) );
127127

@@ -152,8 +152,8 @@ static int emulate_ldw(struct pt_regs *regs, int toreg, int flop)
152152
" mtctl %2,11\n"
153153
" vshd %0,%3,%0\n"
154154
"3: \n"
155-
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 3b)
156-
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 3b)
155+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 3b, "%1")
156+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 3b, "%1")
157157
: "+r" (val), "+r" (ret), "=&r" (temp1), "=&r" (temp2)
158158
: "r" (saddr), "r" (regs->isr) );
159159

@@ -189,8 +189,8 @@ static int emulate_ldd(struct pt_regs *regs, int toreg, int flop)
189189
" mtsar %%r19\n"
190190
" shrpd %0,%%r20,%%sar,%0\n"
191191
"3: \n"
192-
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 3b)
193-
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 3b)
192+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 3b, "%1")
193+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 3b, "%1")
194194
: "=r" (val), "+r" (ret)
195195
: "0" (val), "r" (saddr), "r" (regs->isr)
196196
: "r19", "r20" );
@@ -209,9 +209,9 @@ static int emulate_ldd(struct pt_regs *regs, int toreg, int flop)
209209
" vshd %0,%R0,%0\n"
210210
" vshd %R0,%4,%R0\n"
211211
"4: \n"
212-
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 4b)
213-
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 4b)
214-
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(3b, 4b)
212+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 4b, "%1")
213+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 4b, "%1")
214+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(3b, 4b, "%1")
215215
: "+r" (val), "+r" (ret), "+r" (saddr), "=&r" (shift), "=&r" (temp1)
216216
: "r" (regs->isr) );
217217
}
@@ -244,8 +244,8 @@ static int emulate_sth(struct pt_regs *regs, int frreg)
244244
"1: stb %1, 0(%%sr1, %3)\n"
245245
"2: stb %2, 1(%%sr1, %3)\n"
246246
"3: \n"
247-
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 3b)
248-
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 3b)
247+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 3b, "%0")
248+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 3b, "%0")
249249
: "+r" (ret), "=&r" (temp1)
250250
: "r" (val), "r" (regs->ior), "r" (regs->isr) );
251251

@@ -285,8 +285,8 @@ static int emulate_stw(struct pt_regs *regs, int frreg, int flop)
285285
" stw %%r20,0(%%sr1,%2)\n"
286286
" stw %%r21,4(%%sr1,%2)\n"
287287
"3: \n"
288-
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 3b)
289-
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 3b)
288+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 3b, "%0")
289+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 3b, "%0")
290290
: "+r" (ret)
291291
: "r" (val), "r" (regs->ior), "r" (regs->isr)
292292
: "r19", "r20", "r21", "r22", "r1" );
@@ -329,10 +329,10 @@ static int emulate_std(struct pt_regs *regs, int frreg, int flop)
329329
"3: std %%r20,0(%%sr1,%2)\n"
330330
"4: std %%r21,8(%%sr1,%2)\n"
331331
"5: \n"
332-
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 5b)
333-
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 5b)
334-
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(3b, 5b)
335-
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(4b, 5b)
332+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 5b, "%0")
333+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 5b, "%0")
334+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(3b, 5b, "%0")
335+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(4b, 5b, "%0")
336336
: "+r" (ret)
337337
: "r" (val), "r" (regs->ior), "r" (regs->isr)
338338
: "r19", "r20", "r21", "r22", "r1" );
@@ -357,11 +357,11 @@ static int emulate_std(struct pt_regs *regs, int frreg, int flop)
357357
"4: stw %%r1,4(%%sr1,%2)\n"
358358
"5: stw %R1,8(%%sr1,%2)\n"
359359
"6: \n"
360-
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 6b)
361-
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 6b)
362-
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(3b, 6b)
363-
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(4b, 6b)
364-
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(5b, 6b)
360+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 6b, "%0")
361+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 6b, "%0")
362+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(3b, 6b, "%0")
363+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(4b, 6b, "%0")
364+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(5b, 6b, "%0")
365365
: "+r" (ret)
366366
: "r" (val), "r" (regs->ior), "r" (regs->isr)
367367
: "r19", "r20", "r21", "r1" );

arch/parisc/mm/fault.c

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -150,11 +150,16 @@ int fixup_exception(struct pt_regs *regs)
150150
* Fix up get_user() and put_user().
151151
* ASM_EXCEPTIONTABLE_ENTRY_EFAULT() sets the least-significant
152152
* bit in the relative address of the fixup routine to indicate
153-
* that gr[ASM_EXCEPTIONTABLE_REG] should be loaded with
154-
* -EFAULT to report a userspace access error.
153+
* that the register encoded in the "or %r0,%r0,register"
154+
* opcode should be loaded with -EFAULT to report a userspace
155+
* access error.
155156
*/
156157
if (fix->fixup & 1) {
157-
regs->gr[ASM_EXCEPTIONTABLE_REG] = -EFAULT;
158+
int fault_error_reg = fix->err_opcode & 0x1f;
159+
if (!WARN_ON(!fault_error_reg))
160+
regs->gr[fault_error_reg] = -EFAULT;
161+
pr_debug("Unalignment fixup of register %d at %pS\n",
162+
fault_error_reg, (void*)regs->iaoq[0]);
158163

159164
/* zero target register for get_user() */
160165
if (parisc_acctyp(0, regs->iir) == VM_READ) {

0 commit comments

Comments
 (0)