]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - arch/x86/kernel/unwind_frame.c
x86/unwind: Fix dereference of untrusted pointer
[linux.git] / arch / x86 / kernel / unwind_frame.c
index d145a0b1f529877b67e16992ca4c3830869f9950..d05637726c10e1f1873686b44e265f3e1aff1d8b 100644 (file)
@@ -184,6 +184,12 @@ static struct pt_regs *decode_frame_pointer(unsigned long *bp)
        return (struct pt_regs *)(regs & ~0x1);
 }
 
+#ifdef CONFIG_X86_32
+#define KERNEL_REGS_SIZE (sizeof(struct pt_regs) - 2*sizeof(long))
+#else
+#define KERNEL_REGS_SIZE (sizeof(struct pt_regs))
+#endif
+
 static bool update_stack_state(struct unwind_state *state,
                               unsigned long *next_bp)
 {
@@ -202,7 +208,7 @@ static bool update_stack_state(struct unwind_state *state,
        regs = decode_frame_pointer(next_bp);
        if (regs) {
                frame = (unsigned long *)regs;
-               len = regs_size(regs);
+               len = KERNEL_REGS_SIZE;
                state->got_irq = true;
        } else {
                frame = next_bp;
@@ -226,6 +232,14 @@ static bool update_stack_state(struct unwind_state *state,
            frame < prev_frame_end)
                return false;
 
+       /*
+        * On 32-bit with user mode regs, make sure the last two regs are safe
+        * to access:
+        */
+       if (IS_ENABLED(CONFIG_X86_32) && regs && user_mode(regs) &&
+           !on_stack(info, frame, len + 2*sizeof(long)))
+               return false;
+
        /* Move state to the next frame: */
        if (regs) {
                state->regs = regs;