ARM: 9106/1: traps: use get_kernel_nofault instead of set_fs()
authorArnd Bergmann <arnd@arndb.de>
Wed, 11 Aug 2021 07:30:19 +0000 (08:30 +0100)
committerRussell King (Oracle) <rmk+kernel@armlinux.org.uk>
Fri, 20 Aug 2021 10:39:25 +0000 (11:39 +0100)
ARM uses set_fs() and __get_user() to allow the stack dumping code to
access possibly invalid pointers carefully. These can be changed to the
simpler get_kernel_nofault(), and allow the eventual removal of set_fs().

dump_instr() will print either kernel or user space pointers,
depending on how it was called. For dump_mem(), I assume we are only
interested in kernel pointers, and the only time that this is called
with user_mode(regs)==true is when the regs themselves are unreliable
as a result of the condition that caused the trap.

Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
arch/arm/kernel/traps.c

index 64308e3a5d0c4d5cab1a802127dd2507c8892f03..10dd3ef1f3987394d5de79020636f8b914f71549 100644 (file)
@@ -122,17 +122,8 @@ static void dump_mem(const char *lvl, const char *str, unsigned long bottom,
                     unsigned long top)
 {
        unsigned long first;
                     unsigned long top)
 {
        unsigned long first;
-       mm_segment_t fs;
        int i;
 
        int i;
 
-       /*
-        * We need to switch to kernel mode so that we can use __get_user
-        * to safely read from kernel space.  Note that we now dump the
-        * code first, just in case the backtrace kills us.
-        */
-       fs = get_fs();
-       set_fs(KERNEL_DS);
-
        printk("%s%s(0x%08lx to 0x%08lx)\n", lvl, str, bottom, top);
 
        for (first = bottom & ~31; first < top; first += 32) {
        printk("%s%s(0x%08lx to 0x%08lx)\n", lvl, str, bottom, top);
 
        for (first = bottom & ~31; first < top; first += 32) {
@@ -145,7 +136,7 @@ static void dump_mem(const char *lvl, const char *str, unsigned long bottom,
                for (p = first, i = 0; i < 8 && p < top; i++, p += 4) {
                        if (p >= bottom && p < top) {
                                unsigned long val;
                for (p = first, i = 0; i < 8 && p < top; i++, p += 4) {
                        if (p >= bottom && p < top) {
                                unsigned long val;
-                               if (__get_user(val, (unsigned long *)p) == 0)
+                               if (get_kernel_nofault(val, (unsigned long *)p))
                                        sprintf(str + i * 9, " %08lx", val);
                                else
                                        sprintf(str + i * 9, " ????????");
                                        sprintf(str + i * 9, " %08lx", val);
                                else
                                        sprintf(str + i * 9, " ????????");
@@ -153,11 +144,9 @@ static void dump_mem(const char *lvl, const char *str, unsigned long bottom,
                }
                printk("%s%04lx:%s\n", lvl, first & 0xffff, str);
        }
                }
                printk("%s%04lx:%s\n", lvl, first & 0xffff, str);
        }
-
-       set_fs(fs);
 }
 
 }
 
-static void __dump_instr(const char *lvl, struct pt_regs *regs)
+static void dump_instr(const char *lvl, struct pt_regs *regs)
 {
        unsigned long addr = instruction_pointer(regs);
        const int thumb = thumb_mode(regs);
 {
        unsigned long addr = instruction_pointer(regs);
        const int thumb = thumb_mode(regs);
@@ -173,10 +162,20 @@ static void __dump_instr(const char *lvl, struct pt_regs *regs)
        for (i = -4; i < 1 + !!thumb; i++) {
                unsigned int val, bad;
 
        for (i = -4; i < 1 + !!thumb; i++) {
                unsigned int val, bad;
 
-               if (thumb)
-                       bad = get_user(val, &((u16 *)addr)[i]);
-               else
-                       bad = get_user(val, &((u32 *)addr)[i]);
+               if (!user_mode(regs)) {
+                       if (thumb) {
+                               u16 val16;
+                               bad = get_kernel_nofault(val16, &((u16 *)addr)[i]);
+                               val = val16;
+                       } else {
+                               bad = get_kernel_nofault(val, &((u32 *)addr)[i]);
+                       }
+               } else {
+                       if (thumb)
+                               bad = get_user(val, &((u16 *)addr)[i]);
+                       else
+                               bad = get_user(val, &((u32 *)addr)[i]);
+               }
 
                if (!bad)
                        p += sprintf(p, i == 0 ? "(%0*x) " : "%0*x ",
 
                if (!bad)
                        p += sprintf(p, i == 0 ? "(%0*x) " : "%0*x ",
@@ -189,20 +188,6 @@ static void __dump_instr(const char *lvl, struct pt_regs *regs)
        printk("%sCode: %s\n", lvl, str);
 }
 
        printk("%sCode: %s\n", lvl, str);
 }
 
-static void dump_instr(const char *lvl, struct pt_regs *regs)
-{
-       mm_segment_t fs;
-
-       if (!user_mode(regs)) {
-               fs = get_fs();
-               set_fs(KERNEL_DS);
-               __dump_instr(lvl, regs);
-               set_fs(fs);
-       } else {
-               __dump_instr(lvl, regs);
-       }
-}
-
 #ifdef CONFIG_ARM_UNWIND
 static inline void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk,
                                  const char *loglvl)
 #ifdef CONFIG_ARM_UNWIND
 static inline void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk,
                                  const char *loglvl)