Skip to content

Commit e5c46fd

Browse files
ardbiesheuvelRussell King (Oracle)
authored andcommitted
ARM: 9214/1: alignment: advance IT state after emulating Thumb instruction
After emulating a misaligned load or store issued in Thumb mode, we have to advance the IT state by hand, or it will get out of sync with the actual instruction stream, which means we'll end up applying the wrong condition code to subsequent instructions. This might corrupt the program state rather catastrophically. So borrow the it_advance() helper from the probing code, and use it on CPSR if the emulated instruction is Thumb. Cc: <stable@vger.kernel.org> Reviewed-by: Linus Walleij <linus.walleij@linaro.org> Signed-off-by: Ard Biesheuvel <ardb@kernel.org> Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
1 parent e4ced82 commit e5c46fd

File tree

3 files changed

+30
-25
lines changed

3 files changed

+30
-25
lines changed

arch/arm/include/asm/ptrace.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,5 +163,31 @@ static inline unsigned long user_stack_pointer(struct pt_regs *regs)
163163
((current_stack_pointer | (THREAD_SIZE - 1)) - 7) - 1; \
164164
})
165165

166+
167+
/*
168+
* Update ITSTATE after normal execution of an IT block instruction.
169+
*
170+
* The 8 IT state bits are split into two parts in CPSR:
171+
* ITSTATE<1:0> are in CPSR<26:25>
172+
* ITSTATE<7:2> are in CPSR<15:10>
173+
*/
174+
static inline unsigned long it_advance(unsigned long cpsr)
175+
{
176+
if ((cpsr & 0x06000400) == 0) {
177+
/* ITSTATE<2:0> == 0 means end of IT block, so clear IT state */
178+
cpsr &= ~PSR_IT_MASK;
179+
} else {
180+
/* We need to shift left ITSTATE<4:0> */
181+
const unsigned long mask = 0x06001c00; /* Mask ITSTATE<4:0> */
182+
unsigned long it = cpsr & mask;
183+
it <<= 1;
184+
it |= it >> (27 - 10); /* Carry ITSTATE<2> to correct place */
185+
it &= mask;
186+
cpsr &= ~mask;
187+
cpsr |= it;
188+
}
189+
return cpsr;
190+
}
191+
166192
#endif /* __ASSEMBLY__ */
167193
#endif

arch/arm/mm/alignment.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -935,6 +935,9 @@ do_alignment(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
935935
if (type == TYPE_LDST)
936936
do_alignment_finish_ldst(addr, instr, regs, offset);
937937

938+
if (thumb_mode(regs))
939+
regs->ARM_cpsr = it_advance(regs->ARM_cpsr);
940+
938941
return 0;
939942

940943
bad_or_fault:

arch/arm/probes/decode.h

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <linux/types.h>
1515
#include <linux/stddef.h>
1616
#include <asm/probes.h>
17+
#include <asm/ptrace.h>
1718
#include <asm/kprobes.h>
1819

1920
void __init arm_probes_decode_init(void);
@@ -35,31 +36,6 @@ void __init find_str_pc_offset(void);
3536
#endif
3637

3738

38-
/*
39-
* Update ITSTATE after normal execution of an IT block instruction.
40-
*
41-
* The 8 IT state bits are split into two parts in CPSR:
42-
* ITSTATE<1:0> are in CPSR<26:25>
43-
* ITSTATE<7:2> are in CPSR<15:10>
44-
*/
45-
static inline unsigned long it_advance(unsigned long cpsr)
46-
{
47-
if ((cpsr & 0x06000400) == 0) {
48-
/* ITSTATE<2:0> == 0 means end of IT block, so clear IT state */
49-
cpsr &= ~PSR_IT_MASK;
50-
} else {
51-
/* We need to shift left ITSTATE<4:0> */
52-
const unsigned long mask = 0x06001c00; /* Mask ITSTATE<4:0> */
53-
unsigned long it = cpsr & mask;
54-
it <<= 1;
55-
it |= it >> (27 - 10); /* Carry ITSTATE<2> to correct place */
56-
it &= mask;
57-
cpsr &= ~mask;
58-
cpsr |= it;
59-
}
60-
return cpsr;
61-
}
62-
6339
static inline void __kprobes bx_write_pc(long pcv, struct pt_regs *regs)
6440
{
6541
long cpsr = regs->ARM_cpsr;

0 commit comments

Comments
 (0)