linux-mips
[Top] [All Lists]

The 64-bit version of __access_ok is broken.

To: Ralf Baechle <ralf@linux-mips.org>, Dominic Sweetman <dom@algor.co.uk>, chris@mips.com, kevink@mips.com, linux-mips@linux-mips.org
Subject: The 64-bit version of __access_ok is broken.
From: Carsten Langgaard <carstenl@mips.com>
Date: Thu, 05 Dec 2002 16:28:07 +0100
Original-recipient: rfc822;linux-mips@linux-mips.org
Sender: linux-mips-bounce@linux-mips.org
I have addressed this issue before, and I do it again, because we have a
potential kernel crash situation, if this isn't fixed.

The __access_ok macro in include/asm-mips64/uaccess.h and the check_axs
macro in arch/mips64/kernel/unaligned.c need to be changed in order to
work correctly, it's a copy from the 32-bit kernel. It's not good enough
to simply check for the "sign bit" of the address.
The area between USEG (XUSEG) and KSEG0 will in 64-bit addressing mode
generate an address error, if accessed.
The size of the area depend on the number of virtual addressing bits
implemented in the CPU.

Please take a look at the patch below.
I think Ralf had some objection the last time I send it, about the fix,
not being efficient enough (performance vice), but I think we need to
consider stability and functionality over performance. So until someone
comes up with a better solution, I think we need this fix.

/Carsten




--
_    _ ____  ___   Carsten Langgaard   Mailto:carstenl@mips.com
|\  /|||___)(___   MIPS Denmark        Direct: +45 4486 5527
| \/ |||    ____)  Lautrupvang 4B      Switch: +45 4486 5555
  TECHNOLOGIES     2750 Ballerup       Fax...: +45 4486 5556
                   Denmark             http://www.mips.com


Index: arch/mips64/kernel/unaligned.c
===================================================================
RCS file: /home/cvs/linux/arch/mips64/kernel/unaligned.c,v
retrieving revision 1.6.2.7
diff -u -r1.6.2.7 unaligned.c
--- arch/mips64/kernel/unaligned.c      5 Dec 2002 03:09:58 -0000       1.6.2.7
+++ arch/mips64/kernel/unaligned.c      5 Dec 2002 15:06:59 -0000
@@ -89,11 +89,14 @@
 #define __STR(x)  #x
 
 /*
- * User code may only access USEG; kernel code may access the
- * entire address space.
+ * User code may only access USEG; 
+ * Kernel code may access the entire address space, except the area between
+ * USEG (XUSEG) and KSEG0.
  */
-#define check_axs(pc,a,s)                              \
-       if ((long)(~(pc) & ((a) | ((a)+(s)))) < 0)      \
+#define check_axs(pc,a,s)                                              \
+        if (((pc < KUSIZE) && (((a) | ((a)+(s))) >= KUSIZE)) ||                
\
+           ((((a) | ((a)+(s))) < K0BASE) &&                            \
+            (((a) | ((a)+(s))) >= KUSIZE)))                            \
                goto sigbus;
 
 static inline int emulate_load_store_insn(struct pt_regs *regs,
Index: include/asm-mips64/uaccess.h
===================================================================
RCS file: /home/cvs/linux/include/asm-mips64/uaccess.h,v
retrieving revision 1.13.2.1
diff -u -r1.13.2.1 uaccess.h
--- include/asm-mips64/uaccess.h        1 Jul 2002 15:27:31 -0000       1.13.2.1
+++ include/asm-mips64/uaccess.h        5 Dec 2002 15:07:11 -0000
@@ -40,16 +40,23 @@
  * than tests.
  *
  * Address valid if:
- *  - "addr" doesn't have any high-bits set
- *  - AND "size" doesn't have any high-bits set
- *  - AND "addr+size" doesn't have any high-bits set
- *  - OR we are in kernel mode.
+ *  - In user mode and "addr" and "addr+size" in USEG (or XUSEG).
+ *  - OR we are in kernel mode and "addr" and "addr+size" isn't in the 
+ *    area between USEG (XUSEG) and KSEG0.
  */
 #define __ua_size(size)                                                        
\
        (__builtin_constant_p(size) && (signed long) (size) > 0 ? 0 : (size))
 
-#define __access_ok(addr,size,mask)                                    \
-       (((signed long)((mask)&(addr | (addr + size) | __ua_size(size)))) >= 0)
+static inline int 
+__access_ok(unsigned long addr, unsigned long size, long mask)
+{
+       if (((mask) && ((addr | (addr+size)) >= KUSIZE)) ||     
+           (((addr | (addr+size)) < K0BASE) &&
+            ((addr | (addr+size)) >= KUSIZE)))
+               return 0;
+       else
+               return 1;
+}
 
 #define __access_mask ((long)(get_fs().seg))
<Prev in Thread] Current Thread [Next in Thread>