[trusty/master-next 1/1] UBUNTU: SAUCE: x86, extable: fix uaccess fixup detection

Andy Whitcroft apw at canonical.com
Thu Feb 22 10:24:07 UTC 2018


The existing code intends to identify a subset of fixups which need
special handling, uaccess related faults need to record the failure.
This is done by adjusting the fixup code pointer by a (random) constant
0x7ffffff0.  This is detected in fixup_exception by comparing the two
pointers.  The intent of this code is to detect the the delta between
the original code and its fixup code being greater than the constant.
However, the code as written triggers undefined comparison behaviour.
In this kernel this prevents the condition triggering, leading to panics
when jumping to the corrupted fixup address.

Convert the code to better implement the intent.  Convert both of the
offsets to final addresses and compare the delta between those.  Also add
a massive comment to explain all of this including the implicit assumptions
on order of the segments that this comparison implies.

Fixes: 706276543b69 ("x86, extable: Switch to relative exception table entries")
Signed-off-by: Andy Whitcroft <apw at canonical.com>
---
 arch/x86/mm/extable.c | 16 ++++++++++++++--
 1 file changed, 14 insertions(+), 2 deletions(-)

diff --git a/arch/x86/mm/extable.c b/arch/x86/mm/extable.c
index 903ec1e9c326..a06be2f7f1bb 100644
--- a/arch/x86/mm/extable.c
+++ b/arch/x86/mm/extable.c
@@ -17,6 +17,7 @@ ex_fixup_addr(const struct exception_table_entry *x)
 int fixup_exception(struct pt_regs *regs)
 {
 	const struct exception_table_entry *fixup;
+	unsigned long insn_ip;
 	unsigned long new_ip;
 
 #ifdef CONFIG_PNPBIOS
@@ -35,9 +36,17 @@ int fixup_exception(struct pt_regs *regs)
 
 	fixup = search_exception_tables(regs->ip);
 	if (fixup) {
+		insn_ip = ex_insn_addr(fixup);
 		new_ip = ex_fixup_addr(fixup);
 
-		if (fixup->fixup - fixup->insn >= 0x7ffffff0 - 4) {
+		/*
+		 * If the code and its fixup are "very far apart" then
+		 * they are infact tagged as uaccess'es.  Handle them
+		 * specially and fix the fixup address.  This relies on
+		 * the .fixup section being at higher addresses that the
+		 * original code.
+		 */
+		if (new_ip - insn_ip >= 0x7ffffff0) {
 			/* Special hack for uaccess_err */
 			current_thread_info()->uaccess_err = 1;
 			new_ip -= 0x7ffffff0;
@@ -53,13 +62,16 @@ int fixup_exception(struct pt_regs *regs)
 int __init early_fixup_exception(unsigned long *ip)
 {
 	const struct exception_table_entry *fixup;
+	unsigned long insn_ip;
 	unsigned long new_ip;
 
 	fixup = search_exception_tables(*ip);
 	if (fixup) {
+		insn_ip = ex_insn_addr(fixup);
 		new_ip = ex_fixup_addr(fixup);
 
-		if (fixup->fixup - fixup->insn >= 0x7ffffff0 - 4) {
+		/* See fixup_exception for details ... */
+		if (new_ip - insn_ip >= 0x7ffffff0) {
 			/* uaccess handling not supported during early boot */
 			return 0;
 		}
-- 
2.15.1





More information about the kernel-team mailing list