The 64-bit version of __access_ok is broken.
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:[email protected] |\ /|||___)(___ 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
mips64/kernel/unaligned.c +7 -4
@@ -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
asm-mips64/uaccess.h +13 -6
@@ -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))
Quoted 12 lines · Carsten Langgaard
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_okmacro ininclude/asm-mips64/uaccess.hand thecheck_axsmacro inarch/mips64/kernel/unaligned.cneed 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.
Quoted 5 lines
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.
Standard Linux approach - a bad solution is worse than no solution.
Just to show the impact of your patch:
text data bss dec hex filename
1817824 1079664 173664 3071152 2edcb0 vmlinux-cvs 1870752 1079664 173664 3124080 2fab70 vmlinux-carsten
So that's 52928 bytes of bloat. __access_ok is one of those kernel functions that are inlined so often that each extra instruction needs a _very_ good justification.
The patch below adds 32 bytes. It's still not the right thing though. It's not fixing all stuff in the assembler code. I have a better patch but it results in odd userspace behaviour. Smells like a compiler problem ...
Ralf
Index: arch/mips64/kernel/process.c
===================================================================
RCS file: /home/cvs/linux/arch/mips64/kernel/process.c,v retrieving revision 1.18.2.9
mips64/kernel/process.c +3 -1
@@ -52,17 +52,19 @@void start_thread (struct pt_regs * regs,unsigned long pc,unsigned long sp) {unsigned long status; +int compat32 = current->thread.mflags &MF_32BIT ;/* New thread looses kernel privileges. */ status = regs->cp0_status & ~(ST0_CU0 |ST0_FR |ST0_KSU ); status |=KSU_USER ; - status |= (current->thread.mflags &MF_32BIT ) ?0 :ST0_FR ; + status |= compat32 ?0 :ST0_FR ; regs->cp0_status = status; current->used_math =0 ;loose_fpu (); regs->cp0_epc = pc; regs->regs[29 ] = sp; current->thread.current_ds =USER_DS ; + current->thread.user_ds.seg = - (compat32 ?0x80000000UL :TASK_SIZE ); }void exit_thread (void )
Index: include/asm-mips64/processor.h
===================================================================
RCS file: /home/cvs/linux/include/asm-mips64/processor.h,v retrieving revision 1.32.2.9
asm-mips64/processor.h +2 -2
@@ -180,7 +180,7 @@#define MF_LOGADE 2/* Log address errors to syslog */ #define MF_32BIT 4/* Process is in 32-bit compat mode */ unsigned long mflags; -mm_segment_t current_ds; +mm_segment_t current_ds, user_ds;unsigned long irix_trampoline;/* Wheee... */ unsigned long irix_oldctx; };
@@ -208,7 +208,7 @@/* \ * For now the default is to fix address errors \ */ \ - MF_FIXADE, { 0 }, 0, 0 \ + MF_FIXADE, KERNEL_DS, { -TASK_SIZE }, 0, 0 \ }#ifdef __KERNEL__
Index: include/asm-mips64/uaccess.h
===================================================================
RCS file: /home/cvs/linux/include/asm-mips64/uaccess.h,v retrieving revision 1.13.2.1
asm-mips64/uaccess.h +2 -2
@@ -23,7 +23,7 @@* For historical reasons, these macros are grossly misnamed. */ #define KERNEL_DS ((mm_segment_t) { (unsigned long) 0L }) -#define USER_DS ((mm_segment_t) { (unsigned long) -1L }) +#define USER_DS (current->thread.user_ds)#define VERIFY_READ 0#define VERIFY_WRITE 1
@@ -49,7 +49,7 @@ (__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) + (((mask) & (addr | (addr + size) | __ua_size(size))) == 0)#define __access_mask ((long)(get_fs().seg))
Quoted 25 lines · Ralf Baechle
Quoted 12 lines · Carsten Langgaard
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_okmacro ininclude/asm-mips64/uaccess.hand thecheck_axsmacro inarch/mips64/kernel/unaligned.cneed 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.Quoted 5 lines
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.Standard Linux approach - a bad solution is worse than no solution.
Just to show the impact of your patch:
text data bss dec hex filename
Quoted 8 lines
1817824 1079664 173664 3071152 2edcb0 vmlinux-cvs 1870752 1079664 173664 3124080 2fab70 vmlinux-carstenSo that's 52928 bytes of bloat.
__access_okis one of those kernel
functions that are inlined so often that each extra instruction needs a _very_ good justification.
I really disagree.
My solution may impact the size of the kernel (the increase is less than 2 percent), but it prevent kernel crashes.
Sure we may be able to find a better solution, but it has been almost 1/2 year ago, the last time I addressed this problem.
I know at least one other on the list was hit by this problem.
Quoted 4 lines
The patch below adds 32 bytes. It's still not the right thing though. It's not fixing all stuff in the assembler code. I have a better patch but it results in odd userspace behaviour. Smells like a compiler problem ...
I tried you patch below, but then nothing seems to work.
Quoted 73 lines
Ralf
Index:
arch/mips64/kernel/process.c
===================================================================
RCS file:/home/cvs/linux/arch/mips64/kernel/process.c,vretrieving revision 1.18.2.9
diff -u -r1.18.2.9process.cmips64/kernel/process.c 7 Dec 2002 02:13:40 -0000 renamed from mips64/kernel/process.c 2 Dec 2002 00:24:52 -0000 1.18.2.9 +3
-1@@ -52,17 +52,19 @@ void start_thread(struct pt_regs * regs, unsigned long pc, unsigned long sp) { unsigned long status; + int compat32 = current->thread.mflags & MF_32BIT; /* New thread looses kernel privileges. */ status = regs->cp0_status & ~(ST0_CU0|ST0_FR|ST0_KSU); status |= KSU_USER; - status |= (current->thread.mflags & MF_32BIT) ? 0 : ST0_FR; + status |= compat32 ? 0 : ST0_FR; regs->cp0_status = status; current->used_math = 0; loose_fpu(); regs->cp0_epc = pc; regs->regs[29] = sp; current->thread.current_ds = USER_DS; + current->thread.user_ds.seg = - (compat32 ? 0x80000000UL : TASK_SIZE); } void exit_thread(void)Index:
include/asm-mips64/processor.h
===================================================================
RCS file:/home/cvs/linux/include/asm-mips64/processor.h,vretrieving revision 1.32.2.9
diff -u -r1.32.2.9processor.hasm-mips64/processor.h 7 Dec 2002 02:14:10 -0000 renamed from asm-mips64/processor.h 4 Nov 2002 19:39:56 -0000 1.32.2.9 +2
-2@@ -180,7 +180,7 @@ #define MF_LOGADE 2 /* Log address errors to syslog */ #define MF_32BIT 4 /* Process is in 32-bit compat mode */ unsigned long mflags; - mm_segment_t current_ds; + mm_segment_t current_ds, user_ds; unsigned long irix_trampoline; /* Wheee... */ unsigned long irix_oldctx; };@@ -208,7 +208,7 @@ /* \ * For now the default is to fix address errors \ */ \ - MF_FIXADE, { 0 }, 0, 0 \ + MF_FIXADE, KERNEL_DS, { -TASK_SIZE }, 0, 0 \ } #ifdef __KERNEL__Index:
include/asm-mips64/uaccess.h
===================================================================
RCS file:/home/cvs/linux/include/asm-mips64/uaccess.h,vretrieving revision 1.13.2.1
diff -u -r1.13.2.1uaccess.hasm-mips64/uaccess.h 7 Dec 2002 02:14:10 -0000 renamed from asm-mips64/uaccess.h 1 Jul 2002 15:27:31 -0000 1.13.2.1 +1
-1@@ -23,7 +23,7 @@ * For historical reasons, these macros are grossly misnamed. */ #define KERNEL_DS ((mm_segment_t) { (unsigned long) 0L }) -#define USER_DS ((mm_segment_t) { (unsigned long) -1L }) +#define USER_DS (current->thread.user_ds) #define VERIFY_READ 0 #define VERIFY_WRITE 1
asm-mips64/uaccess.h 7 Dec 2002 02:14:10 -0000 line 55
Quoted 8 lines
asm-mips64/uaccess.h 7 Dec 2002 02:14:10 -0000 renamed from asm-mips64/uaccess.h 1 Jul 2002 15:27:31 -0000 1.13.2.1 +1
-1@@ -49,7 +49,7 @@ (__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) + (((mask) & (addr | (addr + size) | __ua_size(size))) == 0) #define __access_mask ((long)(get_fs().seg))
--
_ _ ____ ___ Carsten Langgaard Mailto:[email protected] |\ /|||___)(___ MIPS Denmark Direct: +45 4486 5527 | \/ ||| ____) Lautrupvang 4B Switch: +45 4486 5555 TECHNOLOGIES 2750 Ballerup Fax...: +45 4486 5556 Denmark http://www.mips.com
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
The
__access_okmacro ininclude/asm-mips64/uaccess.hand thecheck_axsmacro inarch/mips64/kernel/unaligned.c... is a copy from the 32-bit kernel...The area between USEG (XUSEG) and KSEG0 will in 64-bit addressing mode generate an address error, if accessed.
I'd like to be clear about the consequences of this. Presumably the 'access_ok()' macro is used to check addresses which were (originally) provided by a user program's system call.
Carsten, are you saying that if such an address is set to say 2**41 in a CPU supporting 40-bit user virtual addresses, that the kernel will crash?
If so, that seems to require a fix, even if we don't know a very efficient one. But perhaps any problem is a bit more subtle than that?
Signature · 5 lines
Dominic Sweetman MIPS Technologies The Fruit Farm, Ely Road, Chittering, CAMBS CB5 9PH, ENGLAND phone +44 1223 706205/fax +44 1223 706250/swbrd +44 1223 706200 http://www.algor.co.uk
Quoted 7 lines · Dominic Sweetman
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
The
__access_okmacro ininclude/asm-mips64/uaccess.hand thecheck_axsmacro inarch/mips64/kernel/unaligned.c... is a copy from the 32-bit kernel...The area between USEG (XUSEG) and KSEG0 will in 64-bit addressing mode generate an address error, if accessed.
Quoted 7 lines
I'd like to be clear about the consequences of this. Presumably the '
access_ok()' macro is used to check addresses which were (originally) provided by a user program's system call.Carsten, are you saying that if such an address is set to say 2**41 in a CPU supporting 40-bit user virtual addresses, that the kernel will crash?
Yes, that's the case.
It's been a while since I fixed it locally, but if I ran something like crashme, I could end up, in a situation where the kernel tries (on the behalf of the user) to access an address like 2**41 in a CPU supporting 40-bit user virtual addresses, which generate an address error and because we are in kernel mode we die.
Quoted 11 lines
If so, that seems to require a fix, even if we don't know a very efficient one. But perhaps any problem is a bit more subtle than that?
--
Dominic Sweetman
MIPS Technologies
The Fruit Farm, Ely Road, Chittering, CAMBS CB5 9PH, ENGLAND phone +44 1223 706205/fax +44 1223 706250/swbrd +44 1223 706200
http://www.algor.co.uk
--
_ _ ____ ___ Carsten Langgaard Mailto:[email protected] |\ /|||___)(___ MIPS Denmark Direct: +45 4486 5527 | \/ ||| ____) Lautrupvang 4B Switch: +45 4486 5555 TECHNOLOGIES 2750 Ballerup Fax...: +45 4486 5556 Denmark http://www.mips.com
Quoted 5 lines · Carsten Langgaard
Quoted 3 lines
The patch below adds 32 bytes. It's still not the right thing though. It's not fixing all stuff in the assembler code. I have a better patch but it results in odd userspace behaviour. Smells like a compiler problem ...
I tried you patch below, but then nothing seems to work.
The reason for this problem (and a few others is the broken call to __access_ok() in clear_user(). That should actually be access_ok(). Basically the kernel was only working so far because addresses were just right ...
Below my working version. I still needs to make TASK_SIZE variable but with the clear_user thing fixed that should be easy.
Ralf
Index: arch/mips64/kernel/scall_o32.S
===================================================================
RCS file: /home/cvs/linux/arch/mips64/kernel/scall_o32.S,v retrieving revision 1.48.2.21
mips64/kernel/scall_o32.S +2 -2
@@ -209,7 +209,7 @@ daddiu a0, a1, 4 or a0, a0, a1 and a0, a0, v1 - bltz a0, bad_address + bnez a0, bad_address /* Ok, this is the ll/sc case. World is sane :-) */ 1: ll v0, (a1)
@@ -273,7 +273,7 @@ ld v1, THREAD_CURDS($28) or v0, v0, t1 and v1, v1, v0 - bltz v1, efault + bnez v1, efault move a0, a1 # shift argument registers move a1, a2
Index: arch/mips64/lib/strlen_user.S
===================================================================
RCS file: /home/cvs/linux/arch/mips64/lib/strlen_user.S,v retrieving revision 1.4.2.1
mips64/lib/strlen_user.S +1 -1
@@ -25,7 +25,7 @@ LEAF(__strlen_user_asm) ld v0, THREAD_CURDS($28) # pointer ok? and v0, a0 - bltz v0, fault + bnez v0, fault FEXPORT(__strlen_user_nocheck_asm) move v0, a0
Index: arch/mips64/lib/strncpy_user.S
===================================================================
RCS file: /home/cvs/linux/arch/mips64/lib/strncpy_user.S,v retrieving revision 1.4
mips64/lib/strncpy_user.S +1 -1
@@ -30,7 +30,7 @@ LEAF(__strncpy_from_user_asm) ld v0, THREAD_CURDS($28) # pointer ok? and v0, a1 - bltz v0, fault + bnez v0, fault FEXPORT(__strncpy_from_user_nocheck_asm) move v0, zero
Index: arch/mips64/lib/strnlen_user.S
===================================================================
RCS file: /home/cvs/linux/arch/mips64/lib/strnlen_user.S,v retrieving revision 1.2.2.2
mips64/lib/strnlen_user.S +1 -1
@@ -25,7 +25,7 @@ LEAF(__strnlen_user_asm) ld v0, THREAD_CURDS($28) # pointer ok? and v0, a0 - bltz v0, fault + bnez v0, fault FEXPORT(__strnlen_user_nocheck_asm) move v0, a0
Index: include/asm-mips64/processor.h
===================================================================
RCS file: /home/cvs/linux/include/asm-mips64/processor.h,v retrieving revision 1.32.2.9
asm-mips64/processor.h +1 -1
@@ -208,7 +208,7 @@/* \ * For now the default is to fix address errors \ */ \ - MF_FIXADE, { 0 }, 0, 0 \ + MF_FIXADE, KERNEL_DS, 0, 0 \ }#ifdef __KERNEL__
Index: include/asm-mips64/uaccess.h
===================================================================
RCS file: /home/cvs/linux/include/asm-mips64/uaccess.h,v retrieving revision 1.13.2.1
asm-mips64/uaccess.h +11 -11
@@ -22,8 +22,8 @@* * For historical reasons, these macros are grossly misnamed. */ -#define KERNEL_DS ((mm_segment_t) { (unsigned long) 0L }) -#define USER_DS ((mm_segment_t) { (unsigned long) -1L }) +#define KERNEL_DS ((mm_segment_t) { 0UL }) +#define USER_DS ((mm_segment_t) { -TASK_SIZE })#define VERIFY_READ 0#define VERIFY_WRITE 1
@@ -46,19 +46,19 @@* - OR we are in kernel mode. */ #define __ua_size (size) \ - (__builtin_constant_p(size) && (signed long) (size) > 0 ? 0 : (size)) + ((__builtin_constant_p(size) && (size)) > 0 ? 0 : (size)) -#define __access_ok (addr,size,mask) \ - (((signed long)((mask)&(addr | (addr + size) | __ua_size(size)))) >= 0) +#define __access_ok (addr, size, mask) \ + (((mask) & ((addr) | ((addr) + (size)) | __ua_size(size))) == 0) -#define __access_mask ((long)(get_fs().seg)) +#define __access_mask get_fs().seg -#define access_ok (type,addr,size) \ - __access_ok(((unsigned long)(addr)),(size),__access_mask) +#define access_ok (type, addr, size) \ + __access_ok((unsigned long)(addr), (size), __access_mask)static inline int verify_area (int type,const void * addr,unsigned long size) { -return access_ok (type,addr,size) ?0 : -EFAULT ; +return access_ok (type, addr, size) ?0 : -EFAULT ; }/*
@@ -340,8 +340,8 @@ ({ \void * __cl_addr = (addr); \unsigned long __cl_size = (n); \ -if (__cl_size &&__access_ok (VERIFY_WRITE , \ - ((unsigned long )(__cl_addr)), __cl_size)) \ +if (__cl_size &&access_ok (VERIFY_WRITE , \ + ((unsigned long )(__cl_addr)), __cl_size)) \ __cl_size =__clear_user (__cl_addr, __cl_size); \ __cl_size; \ })
Quoted 28 lines · Ralf Baechle
Quoted 7 lines · Dominic Sweetman
I'd like to be clear about the consequences of this. Presumably the '
access_ok()' macro is used to check addresses which were (originally) provided by a user program's system call.Carsten, are you saying that if such an address is set to say 2**41 in a CPU supporting 40-bit user virtual addresses, that the kernel will crash?
That's correct. The problem which Carsten diagnosed correctly was the assumption which has been inherited from the 32-bit kernel that the sign- bit makes the difference between valid userspace and kernelspace addresses.
Linux doesn't use the supervisor mode so basically that assumption is still true with the except of the area 2^PHYSBITS ... 2^63-1.
Quoted 3 lines
If so, that seems to require a fix, even if we don't know a very efficient one. But perhaps any problem is a bit more subtle than that?
Access_okis a macro which depending on kernel configuration is expanded hundreds, if not thousands of times throughout the kernel. So every single machine instruction inaccess_okwill make a size difference of several kB. Carsten's patch was performing pretty badly in that cathegory. Ifaccess_okwasn't used that often the issue certainly wasn't worth the fuzz.
Quoted 5 lines
Access_okis of course only usable in C code. We also have a few piece of assembler code that access userspace and need to perform the same kind of address validation tests. Carsten's patch was missing these completly. As such it did only reduce the window of this bug from huge to "just" big.
At least I haven't hit those holes, the would have been fixed otherwise, too ;-)
Quoted 8 lines
An efficient solution only requires fairly minor changes as you can see in the patch I just posted. It doesn't even require thinking, it can be obtained by cut'n'paste from the Alpha code. Alternatively the problem could also have been solved by forwarding address errors for the address range in question to the page fault handler which would have served the same purpose, maybe even a tad more efficient but ofuscated ...
I absolutely agree that we should go for an optimized solution, but we discuss this issue 1/2 year ago, none of us, had the time to come up with a better fix than the one I send. I'm going through my to-do list and came across this issue again, and I just wanted to reopen the case again.
This time it annoyed you enough, so you came up with a better solution and I achieved what I came for, so that's great ;-)
Thanks a lot. I will try you patch right away.
Quoted 2 lines
Ralf
--
_ _ ____ ___ Carsten Langgaard Mailto:[email protected] |\ /|||___)(___ MIPS Denmark Direct: +45 4486 5527 | \/ ||| ____) Lautrupvang 4B Switch: +45 4486 5555 TECHNOLOGIES 2750 Ballerup Fax...: +45 4486 5556 Denmark http://www.mips.com
Your patch seems to do the job, thanks a lot.
/Carsten
Quoted 153 lines · Ralf Baechle
Quoted 5 lines · Carsten Langgaard
Quoted 3 lines
The patch below adds 32 bytes. It's still not the right thing though. It's not fixing all stuff in the assembler code. I have a better patch but it results in odd userspace behaviour. Smells like a compiler problem ...
I tried you patch below, but then nothing seems to work.
The reason for this problem (and a few others is the broken call to
__access_ok()inclear_user(). That should actually beaccess_ok(). Basically the kernel was only working so far because addresses were just right ...Below my working version. I still needs to make
TASK_SIZEvariable but with theclear_userthing fixed that should be easy.Ralf
Index:
arch/mips64/kernel/scall_o32.S
===================================================================
RCS file:/home/cvs/linux/arch/mips64/kernel/scall_o32.S,vretrieving revision 1.48.2.21
diff -u -r1.48.2.21scall_o32.Smips64/kernel/scall_o32.S 8 Dec 2002 06:08:55 -0000 renamed from mips64/kernel/scall_o32.S 3 Dec 2002 14:23:05 -0000 1.48.2.21 +2
-2@@ -209,7 +209,7 @@ daddiu a0, a1, 4 or a0, a0, a1 and a0, a0, v1 - bltz a0, bad_address + bnez a0, bad_address /* Ok, this is the ll/sc case. World is sane :-) */ 1: ll v0, (a1)@@ -273,7 +273,7 @@ ld v1, THREAD_CURDS($28) or v0, v0, t1 and v1, v1, v0 - bltz v1, efault + bnez v1, efault move a0, a1 # shift argument registers move a1, a2Index:
arch/mips64/lib/strlen_user.S
===================================================================
RCS file:/home/cvs/linux/arch/mips64/lib/strlen_user.S,vretrieving revision 1.4.2.1
diff -u -r1.4.2.1strlen_user.Smips64/lib/strlen_user.S 8 Dec 2002 06:08:55 -0000 renamed from mips64/lib/strlen_user.S 1 Jul 2002 15:27:29 -0000 1.4.2.1 +1
-1@@ -25,7 +25,7 @@ LEAF(__strlen_user_asm) ld v0, THREAD_CURDS($28) # pointer ok? and v0, a0 - bltz v0, fault + bnez v0, fault FEXPORT(__strlen_user_nocheck_asm) move v0, a0Index:
arch/mips64/lib/strncpy_user.S
===================================================================
RCS file:/home/cvs/linux/arch/mips64/lib/strncpy_user.S,vretrieving revision 1.4
diff -u -r1.4strncpy_user.Smips64/lib/strncpy_user.S 8 Dec 2002 06:08:55 -0000 renamed from mips64/lib/strncpy_user.S 9 Jul 2001 00:25:37 -0000 1.4 +1
-1@@ -30,7 +30,7 @@ LEAF(__strncpy_from_user_asm) ld v0, THREAD_CURDS($28) # pointer ok? and v0, a1 - bltz v0, fault + bnez v0, fault FEXPORT(__strncpy_from_user_nocheck_asm) move v0, zeroIndex:
arch/mips64/lib/strnlen_user.S
===================================================================
RCS file:/home/cvs/linux/arch/mips64/lib/strnlen_user.S,vretrieving revision 1.2.2.2
diff -u -r1.2.2.2strnlen_user.Smips64/lib/strnlen_user.S 8 Dec 2002 06:08:55 -0000 renamed from mips64/lib/strnlen_user.S 1 Jul 2002 15:27:29 -0000 1.2.2.2 +1
-1@@ -25,7 +25,7 @@ LEAF(__strnlen_user_asm) ld v0, THREAD_CURDS($28) # pointer ok? and v0, a0 - bltz v0, fault + bnez v0, fault FEXPORT(__strnlen_user_nocheck_asm) move v0, a0Index:
include/asm-mips64/processor.h
===================================================================
RCS file:/home/cvs/linux/include/asm-mips64/processor.h,vretrieving revision 1.32.2.9
diff -u -r1.32.2.9processor.hasm-mips64/processor.h 8 Dec 2002 06:09:38 -0000 renamed from asm-mips64/processor.h 4 Nov 2002 19:39:56 -0000 1.32.2.9 +1
-1@@ -208,7 +208,7 @@ /* \ * For now the default is to fix address errors \ */ \ - MF_FIXADE, { 0 }, 0, 0 \ + MF_FIXADE, KERNEL_DS, 0, 0 \ } #ifdef __KERNEL__Index:
include/asm-mips64/uaccess.h
===================================================================
RCS file:/home/cvs/linux/include/asm-mips64/uaccess.h,vretrieving revision 1.13.2.1
diff -u -r1.13.2.1uaccess.hasm-mips64/uaccess.h 8 Dec 2002 06:09:39 -0000 renamed from asm-mips64/uaccess.h 1 Jul 2002 15:27:31 -0000 1.13.2.1 +9
-9@@ -22,8 +22,8 @@ * * For historical reasons, these macros are grossly misnamed. */ -#define KERNEL_DS ((mm_segment_t) { (unsigned long) 0L }) -#define USER_DS ((mm_segment_t) { (unsigned long) -1L }) +#define KERNEL_DS ((mm_segment_t) { 0UL }) +#define USER_DS ((mm_segment_t) { -TASK_SIZE }) #define VERIFY_READ 0 #define VERIFY_WRITE 1@@ -46,19 +46,19 @@ * - OR we are in kernel mode. */ #define __ua_size(size) \ - (__builtin_constant_p(size) && (signed long) (size) > 0 ? 0 : (size)) + ((__builtin_constant_p(size) && (size)) > 0 ? 0 : (size)) -#define __access_ok(addr,size,mask) \ - (((signed long)((mask)&(addr | (addr + size) | __ua_size(size)))) >= 0) +#define __access_ok(addr, size, mask) \ + (((mask) & ((addr) | ((addr) + (size)) | __ua_size(size))) == 0) -#define __access_mask ((long)(get_fs().seg)) +#define __access_mask get_fs().seg -#define access_ok(type,addr,size) \ - __access_ok(((unsigned long)(addr)),(size),__access_mask) +#define access_ok(type, addr, size) \ + __access_ok((unsigned long)(addr), (size), __access_mask) static inline int verify_area(int type, const void * addr, unsigned long size) { - return access_ok(type,addr,size) ? 0 : -EFAULT; + return access_ok(type, addr, size) ? 0 : -EFAULT; } /*
asm-mips64/uaccess.h 8 Dec 2002 06:09:39 -0000 line 347
Quoted 10 lines
asm-mips64/uaccess.h 8 Dec 2002 06:09:39 -0000 renamed from asm-mips64/uaccess.h 1 Jul 2002 15:27:31 -0000 1.13.2.1 +2
-2@@ -340,8 +340,8 @@ ({ \ void * __cl_addr = (addr); \ unsigned long __cl_size = (n); \ - if (__cl_size && __access_ok(VERIFY_WRITE, \ - ((unsigned long)(__cl_addr)), __cl_size)) \ + if (__cl_size && access_ok(VERIFY_WRITE, \ + ((unsigned long)(__cl_addr)), __cl_size)) \ __cl_size = __clear_user(__cl_addr, __cl_size); \ __cl_size; \ })
--
_ _ ____ ___ Carsten Langgaard Mailto:[email protected] |\ /|||___)(___ MIPS Denmark Direct: +45 4486 5527 | \/ ||| ____) Lautrupvang 4B Switch: +45 4486 5555 TECHNOLOGIES 2750 Ballerup Fax...: +45 4486 5556 Denmark http://www.mips.com
Quoted 7 lines · Carsten Langgaard
I absolutely agree that we should go for an optimized solution, but we discuss this issue 1/2 year ago, none of us, had the time to come up with a better fix than the one I send. I'm going through my to-do list and came across this issue again, and I just wanted to reopen the case again.
This time it annoyed you enough, so you came up with a better solution and I achieved what I came for, so that's great ;-)
Thanks a lot. I will try you patch right away.
It's already in CVS.
Btw, a missconfigured firewall turned linux-mips.org into a heating for the server room last night. This should be fixed now. The machine is now hosted at MIPS UK, formerly Algorithmics. There should be no user visible changes.
Ralf
Quoted 7 lines · Dominic Sweetman
I'd like to be clear about the consequences of this. Presumably the '
access_ok()' macro is used to check addresses which were (originally) provided by a user program's system call.Carsten, are you saying that if such an address is set to say 2**41 in a CPU supporting 40-bit user virtual addresses, that the kernel will crash?
That's correct. The problem which Carsten diagnosed correctly was the assumption which has been inherited from the 32-bit kernel that the sign- bit makes the difference between valid userspace and kernelspace addresses.
Linux doesn't use the supervisor mode so basically that assumption is still true with the except of the area 2^PHYSBITS ... 2^63-1.
Quoted 3 lines
If so, that seems to require a fix, even if we don't know a very efficient one. But perhaps any problem is a bit more subtle than that?
Access_ok is a macro which depending on kernel configuration is expanded hundreds, if not thousands of times throughout the kernel. So every single machine instruction in access_ok will make a size difference of several kB. Carsten's patch was performing pretty badly in that cathegory. If access_ok wasn't used that often the issue certainly wasn't worth the fuzz.
Access_ok is of course only usable in C code. We also have a few piece of assembler code that access userspace and need to perform the same kind of address validation tests. Carsten's patch was missing these completly. As such it did only reduce the window of this bug from huge to "just" big.
An efficient solution only requires fairly minor changes as you can see in the patch I just posted. It doesn't even require thinking, it can be obtained by cut'n'paste from the Alpha code. Alternatively the problem could also have been solved by forwarding address errors for the address range in question to the page fault handler which would have served the same purpose, maybe even a tad more efficient but ofuscated ...
Ralf