Commit 8a7f3127 authored by Russell King's avatar Russell King

Fix a bug where a user-generated unaligned load could read or write kernel

data.
parent a7c2267f
...@@ -30,6 +30,10 @@ ...@@ -30,6 +30,10 @@
#include <asm/pgtable.h> #include <asm/pgtable.h>
#include <asm/unaligned.h> #include <asm/unaligned.h>
extern void
do_bad_area(struct task_struct *tsk, struct mm_struct *mm, unsigned long addr,
int error_code, struct pt_regs *regs);
/* /*
* 32-bit misaligned trap handler (c) 1998 San Mehat (CCC) -July 1998 * 32-bit misaligned trap handler (c) 1998 San Mehat (CCC) -July 1998
* /proc/sys/debug/alignment, modified and integrated into * /proc/sys/debug/alignment, modified and integrated into
...@@ -163,9 +167,9 @@ union offset_union { ...@@ -163,9 +167,9 @@ union offset_union {
#define TYPE_LDST 2 #define TYPE_LDST 2
#define TYPE_DONE 3 #define TYPE_DONE 3
#define get8_unaligned_check(val,addr,err) \ #define __get8_unaligned_check(ins,val,addr,err) \
__asm__( \ __asm__( \
"1: ldrb %1, [%2], #1\n" \ "1: "ins" %1, [%2], #1\n" \
"2:\n" \ "2:\n" \
" .section .fixup,\"ax\"\n" \ " .section .fixup,\"ax\"\n" \
" .align 2\n" \ " .align 2\n" \
...@@ -179,39 +183,49 @@ union offset_union { ...@@ -179,39 +183,49 @@ union offset_union {
: "=r" (err), "=&r" (val), "=r" (addr) \ : "=r" (err), "=&r" (val), "=r" (addr) \
: "0" (err), "2" (addr)) : "0" (err), "2" (addr))
#define get8t_unaligned_check(val,addr,err) \ #define __get16_unaligned_check(ins,val,addr) \
__asm__( \ do { \
"1: ldrbt %1, [%2], #1\n" \ unsigned int err = 0, v, a = addr; \
"2:\n" \ __get8_unaligned_check(ins,val,a,err); \
" .section .fixup,\"ax\"\n" \ __get8_unaligned_check(ins,v,a,err); \
" .align 2\n" \ val |= v << 8; \
"3: mov %0, #1\n" \ if (err) \
" b 2b\n" \ goto fault; \
" .previous\n" \ } while (0)
" .section __ex_table,\"a\"\n" \
" .align 3\n" \ #define get16_unaligned_check(val,addr) \
" .long 1b, 3b\n" \ __get16_unaligned_check("ldrb",val,addr)
" .previous\n" \
: "=r" (err), "=&r" (val), "=r" (addr) \ #define get16t_unaligned_check(val,addr) \
: "0" (err), "2" (addr)) __get16_unaligned_check("ldrbt",val,addr)
#define get16_unaligned_check(val,addr) \ #define __get32_unaligned_check(ins,val,addr) \
do { \ do { \
unsigned int err = 0, v, a = addr; \ unsigned int err = 0, v, a = addr; \
get8_unaligned_check(val,a,err); \ __get8_unaligned_check(ins,val,a,err); \
get8_unaligned_check(v,a,err); \ __get8_unaligned_check(ins,v,a,err); \
val |= v << 8; \ val |= v << 8; \
__get8_unaligned_check(ins,v,a,err); \
val |= v << 16; \
__get8_unaligned_check(ins,v,a,err); \
val |= v << 24; \
if (err) \ if (err) \
goto fault; \ goto fault; \
} while (0) } while (0)
#define put16_unaligned_check(val,addr) \ #define get32_unaligned_check(val,addr) \
__get32_unaligned_check("ldrb",val,addr)
#define get32t_unaligned_check(val,addr) \
__get32_unaligned_check("ldrbt",val,addr)
#define __put16_unaligned_check(ins,val,addr) \
do { \ do { \
unsigned int err = 0, v = val, a = addr; \ unsigned int err = 0, v = val, a = addr; \
__asm__( \ __asm__( \
"1: strb %1, [%2], #1\n" \ "1: "ins" %1, [%2], #1\n" \
" mov %1, %1, lsr #8\n" \ " mov %1, %1, lsr #8\n" \
"2: strb %1, [%2]\n" \ "2: "ins" %1, [%2]\n" \
"3:\n" \ "3:\n" \
" .section .fixup,\"ax\"\n" \ " .section .fixup,\"ax\"\n" \
" .align 2\n" \ " .align 2\n" \
...@@ -229,6 +243,12 @@ union offset_union { ...@@ -229,6 +243,12 @@ union offset_union {
goto fault; \ goto fault; \
} while (0) } while (0)
#define put16_unaligned_check(val,addr) \
__put16_unaligned_check("strb",val,addr)
#define put16t_unaligned_check(val,addr) \
__put16_unaligned_check("strbt",val,addr)
#define __put32_unaligned_check(ins,val,addr) \ #define __put32_unaligned_check(ins,val,addr) \
do { \ do { \
unsigned int err = 0, v = val, a = addr; \ unsigned int err = 0, v = val, a = addr; \
...@@ -259,37 +279,9 @@ union offset_union { ...@@ -259,37 +279,9 @@ union offset_union {
goto fault; \ goto fault; \
} while (0) } while (0)
#define get32_unaligned_check(val,addr) \
do { \
unsigned int err = 0, v, a = addr; \
get8_unaligned_check(val,a,err); \
get8_unaligned_check(v,a,err); \
val |= v << 8; \
get8_unaligned_check(v,a,err); \
val |= v << 16; \
get8_unaligned_check(v,a,err); \
val |= v << 24; \
if (err) \
goto fault; \
} while (0)
#define put32_unaligned_check(val,addr) \ #define put32_unaligned_check(val,addr) \
__put32_unaligned_check("strb", val, addr) __put32_unaligned_check("strb", val, addr)
#define get32t_unaligned_check(val,addr) \
do { \
unsigned int err = 0, v, a = addr; \
get8t_unaligned_check(val,a,err); \
get8t_unaligned_check(v,a,err); \
val |= v << 8; \
get8t_unaligned_check(v,a,err); \
val |= v << 16; \
get8t_unaligned_check(v,a,err); \
val |= v << 24; \
if (err) \
goto fault; \
} while (0)
#define put32t_unaligned_check(val,addr) \ #define put32t_unaligned_check(val,addr) \
__put32_unaligned_check("strbt", val, addr) __put32_unaligned_check("strbt", val, addr)
...@@ -319,6 +311,9 @@ do_alignment_ldrhstrh(unsigned long addr, unsigned long instr, struct pt_regs *r ...@@ -319,6 +311,9 @@ do_alignment_ldrhstrh(unsigned long addr, unsigned long instr, struct pt_regs *r
ai_half += 1; ai_half += 1;
if (user_mode(regs))
goto user;
if (LDST_L_BIT(instr)) { if (LDST_L_BIT(instr)) {
unsigned long val; unsigned long val;
get16_unaligned_check(val, addr); get16_unaligned_check(val, addr);
...@@ -333,12 +328,27 @@ do_alignment_ldrhstrh(unsigned long addr, unsigned long instr, struct pt_regs *r ...@@ -333,12 +328,27 @@ do_alignment_ldrhstrh(unsigned long addr, unsigned long instr, struct pt_regs *r
return TYPE_LDST; return TYPE_LDST;
swp: user:
if (LDST_L_BIT(instr)) {
unsigned long val;
get16t_unaligned_check(val, addr);
/* signed half-word? */
if (instr & 0x40)
val = (signed long)((signed short) val);
regs->uregs[rd] = val;
} else
put16t_unaligned_check(regs->uregs[rd], addr);
return TYPE_LDST;
swp:
printk(KERN_ERR "Alignment trap: not handling swp instruction\n"); printk(KERN_ERR "Alignment trap: not handling swp instruction\n");
bad: bad:
return TYPE_ERROR; return TYPE_ERROR;
fault: fault:
return TYPE_FAULT; return TYPE_FAULT;
} }
...@@ -349,23 +359,27 @@ do_alignment_ldrstr(unsigned long addr, unsigned long instr, struct pt_regs *reg ...@@ -349,23 +359,27 @@ do_alignment_ldrstr(unsigned long addr, unsigned long instr, struct pt_regs *reg
ai_word += 1; ai_word += 1;
if (!LDST_P_BIT(instr) && LDST_W_BIT(instr)) if ((!LDST_P_BIT(instr) && LDST_W_BIT(instr)) || user_mode(regs))
goto trans; goto trans;
if (LDST_L_BIT(instr)) if (LDST_L_BIT(instr)) {
get32_unaligned_check(regs->uregs[rd], addr); unsigned int val;
else get32_unaligned_check(val, addr);
regs->uregs[rd] = val;
} else
put32_unaligned_check(regs->uregs[rd], addr); put32_unaligned_check(regs->uregs[rd], addr);
return TYPE_LDST; return TYPE_LDST;
trans: trans:
if (LDST_L_BIT(instr)) if (LDST_L_BIT(instr)) {
get32t_unaligned_check(regs->uregs[rd], addr); unsigned int val;
else get32t_unaligned_check(val, addr);
regs->uregs[rd] = val;
} else
put32t_unaligned_check(regs->uregs[rd], addr); put32t_unaligned_check(regs->uregs[rd], addr);
return TYPE_LDST; return TYPE_LDST;
fault: fault:
return TYPE_FAULT; return TYPE_FAULT;
} }
...@@ -431,14 +445,31 @@ do_alignment_ldmstm(unsigned long addr, unsigned long instr, struct pt_regs *reg ...@@ -431,14 +445,31 @@ do_alignment_ldmstm(unsigned long addr, unsigned long instr, struct pt_regs *reg
} }
#endif #endif
for (regbits = REGMASK_BITS(instr), rd = 0; regbits; regbits >>= 1, rd += 1) if (user_mode(regs)) {
if (regbits & 1) { for (regbits = REGMASK_BITS(instr), rd = 0; regbits;
if (LDST_L_BIT(instr)) regbits >>= 1, rd += 1)
get32_unaligned_check(regs->uregs[rd], eaddr); if (regbits & 1) {
else if (LDST_L_BIT(instr)) {
put32_unaligned_check(regs->uregs[rd], eaddr); unsigned int val;
eaddr += 4; get32t_unaligned_check(val, eaddr);
} regs->uregs[rd] = val;
} else
put32t_unaligned_check(regs->uregs[rd], eaddr);
eaddr += 4;
}
} else {
for (regbits = REGMASK_BITS(instr), rd = 0; regbits;
regbits >>= 1, rd += 1)
if (regbits & 1) {
if (LDST_L_BIT(instr)) {
unsigned int val;
get32_unaligned_check(val, eaddr);
regs->uregs[rd] = val;
} else
put32_unaligned_check(regs->uregs[rd], eaddr);
eaddr += 4;
}
}
if (LDST_W_BIT(instr)) if (LDST_W_BIT(instr))
regs->uregs[rn] = newaddr; regs->uregs[rn] = newaddr;
...@@ -539,7 +570,7 @@ int do_alignment(unsigned long addr, int error_code, struct pt_regs *regs) ...@@ -539,7 +570,7 @@ int do_alignment(unsigned long addr, int error_code, struct pt_regs *regs)
return 0; return 0;
bad_or_fault: bad_or_fault:
if (type == TYPE_ERROR) if (type == TYPE_ERROR)
goto bad; goto bad;
regs->ARM_pc -= 4; regs->ARM_pc -= 4;
...@@ -549,7 +580,7 @@ int do_alignment(unsigned long addr, int error_code, struct pt_regs *regs) ...@@ -549,7 +580,7 @@ int do_alignment(unsigned long addr, int error_code, struct pt_regs *regs)
do_bad_area(current, current->mm, addr, error_code, regs); do_bad_area(current, current->mm, addr, error_code, regs);
return 0; return 0;
bad: bad:
/* /*
* Oops, we didn't handle the instruction. * Oops, we didn't handle the instruction.
*/ */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment