aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm
diff options
context:
space:
mode:
authorNicolas Pitre <nico@fluxnic.net>2010-09-19 23:10:43 -0400
committerRussell King <rmk+kernel@arm.linux.org.uk>2010-09-23 10:17:04 -0400
commit2f27bf834e1d0a06e83d7458b535891c552271aa (patch)
tree6d0ed3804614f146a8001245d52579c4ee0cb311 /arch/arm
parent1d5b4c0fa9ff79a4f01e5efc1caefd16b190a3dc (diff)
ARM: 6401/1: plug a race in the alignment trap handler
When the policy for user space is to ignore misaligned accesses from user space, the processor then performs a documented rotation on the accessed data. This is the result of the access being trapped, and the kernel disabling the alignment trap before returning to user space again. In kernel space we always want misaligned accesses to be fixed up. This is enforced by always re-enabling the alignment trap on every entry into kernel space from user space. No such re-enabling is performed when an exception occurs while already in kernel space as the alignment trap is always supposed to be enabled in that case. There is however a small race window when a misaligned access in user space is trapped and the alignment trap disabled, but the CPU didn't return to user space just yet. Any exception would be entered from kernel space at that point and the kernel would then execute with the alignment trap disabled. Thanks to Maxime Bizon <mbizon@freebox.fr> for providing a test module that made this issue reproducible. Signed-off-by: Nicolas Pitre <nicolas.pitre@linaro.org> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch/arm')
-rw-r--r--arch/arm/mm/alignment.c19
1 files changed, 17 insertions, 2 deletions
diff --git a/arch/arm/mm/alignment.c b/arch/arm/mm/alignment.c
index d073b64ae87e..724ba3bce72c 100644
--- a/arch/arm/mm/alignment.c
+++ b/arch/arm/mm/alignment.c
@@ -885,8 +885,23 @@ do_alignment(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
885 885
886 if (ai_usermode & UM_SIGNAL) 886 if (ai_usermode & UM_SIGNAL)
887 force_sig(SIGBUS, current); 887 force_sig(SIGBUS, current);
888 else 888 else {
889 set_cr(cr_no_alignment); 889 /*
890 * We're about to disable the alignment trap and return to
891 * user space. But if an interrupt occurs before actually
892 * reaching user space, then the IRQ vector entry code will
893 * notice that we were still in kernel space and therefore
894 * the alignment trap won't be re-enabled in that case as it
895 * is presumed to be always on from kernel space.
896 * Let's prevent that race by disabling interrupts here (they
897 * are disabled on the way back to user space anyway in
898 * entry-common.S) and disable the alignment trap only if
899 * there is no work pending for this thread.
900 */
901 raw_local_irq_disable();
902 if (!(current_thread_info()->flags & _TIF_WORK_MASK))
903 set_cr(cr_no_alignment);
904 }
890 905
891 return 0; 906 return 0;
892} 907}