Bug 161925

Summary: CVE-2005-2708 user code panics kernel in exec.c
Product: Red Hat Enterprise Linux 3 Reporter: blossom <blossom>
Component: kernelAssignee: Dave Anderson <anderson>
Status: CLOSED ERRATA QA Contact: Brian Brock <bbrock>
Severity: high Docs Contact:
Priority: medium    
Version: 3.0CC: dannf, petrides, security-response-team
Target Milestone: ---Keywords: Security
Target Release: ---   
Hardware: x86_64   
OS: Linux   
Whiteboard: source=bz,reported=20050628,public=20050628,impact=important
Fixed In Version: RHSA-2006-0140 Doc Type: Bug Fix
Doc Text:
Story Points: ---
Clone Of: Environment:
Last Closed: 2006-01-19 15:59:49 UTC Type: ---
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:
Bug Depends On:    
Bug Blocks: 168424    
Attachments:
Description Flags
fixes bug in exec.c which now checks for return code during error condition
none
Final patch none

Description blossom 2005-06-28 14:56:07 UTC
Description of problem:
user code panics kernel in exec.c

Version-Release number of selected component (if applicable):
RHEL 3.0 Update 4

How reproducible:
Everytime

Steps to Reproduce:
1.
Symptom:
the following code
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>

#define SIZE 1000000

int main (int argc, char **argv)
{
int i;
double sum = 0.0;
double t[SIZE];

for (i = 0; i < SIZE; i++)
{
  t[i] = 1.0 + 1.0*SIZE*rand()/(RAND_MAX+1.0) - RAND_MAX/2.0; }

for (i = 0; i > SIZE; i++)
{
  sum += t[i];
}

printf ("Moyenne = %lf\n", sum);

return 0;
}

2.compiled this way
gcc -o essai essai.c

3. on a node :
bash -c 'ulimit -v 100; ./essai'

then node panic:

 
Actual results:

tantal237 login: double fault: 0000
CPU 2
Pid: 3552, comm: modprobe Tainted: P
RIP: 0010:[<0000000000000000>]
RSP: 0000:0000000000000000  EFLAGS: 00010202
RAX: 0000000000000000 RBX: 0000000000000040 RCX: 0000000000000000
RDX: ffffffff80437560 RSI: 00000100bdaf7f08 RDI: ffffffff80437460
RBP: 0000010080008f40 R08: 0000000000000004 R09: 00000100bde77080
R10: 0000000000000000 R11: 0000000000000010 R12: 00000100e660a6c0
R13: 0000010081c2b280 R14: 0000010081c2b280 R15: 0000010080008ec0
FS:  0000002a95aca0a0(0000) GS:ffffffff805e79c0(0000) knlGS:00000000080c9760
CS:  0010 DS: 0000 ES: 0000 CR0: 000000008005003b
CR2: 0000000000000000 CR3: 00000000047a1000 CR4: 00000000000006e0

Call Trace:
Process modprobe (pid: 3552, stackpage=100bdaf5000)

Kernel panic: Fatal exception

Expected results:


Additional info:

Comment 1 blossom 2005-06-28 14:56:07 UTC
Created attachment 116064 [details]
fixes bug in exec.c which now checks for return code during error condition

Comment 2 Ernie Petrides 2005-06-28 17:53:05 UTC
Hello.  Firstly, the test program is irrelevant.  On RHEL3 U4, the following
command takes the code path in question (altered by the patch above):

  bash -c 'ulimit -v 100 ; /bin/date'

However, my x86 box simply prints an error on the console about failing to
load the interpreter for binfmt-464c.  Thus, the steps listed to reproduce
the problem are inadequate.

Please try to reproduce this problem on the latest released kernel (which
is version 2.4.21-32.0.1.EL, a post-U5 security erratum) that is untainted.
Also, please correct the hardware architecture of this bug report if you're
not using an x86-based system.

Thanks in advance.  -ernie


Comment 3 blossom 2005-06-28 18:49:48 UTC
Well, the problem is reproducible under RHEL 3 post update 5,  2.4.21-
32.0.1.EL kernel. We are using x86_64 system. Check whether the fix(attached) 
is fine

Regards
Girish

Comment 4 blossom 2005-06-28 18:53:37 UTC
Well, the problem is reproducible under RHEL 3 post update 5,  2.4.21-
32.0.1.EL kernel. We are using x86_64 system. Check whether the fix(attached) 
is fine

Regards
Girish

Comment 5 Ernie Petrides 2005-06-28 20:19:42 UTC
Fixing hardware field of BZ.  The patch in comment #1 is inappropriate
because since "retval" is always -ENOEXEC in that code path, it
effectively disables the call to request_module() every time.  DaveA
will try to reproduce this locally on an x86_64 system.

Comment 6 blossom 2005-08-26 12:31:24 UTC
Here is a better fix for the problem:

Regards
Girish

diff -uprN linux-2.4.21/fs/binfmt_elf.c linux-2.4.21n/fs/binfmt_elf.c
--- linux-2.4.21/fs/binfmt_elf.c        2005-08-26 02:34:17.000000000 +0530
+++ linux-2.4.21n/fs/binfmt_elf.c       2005-08-26 05:06:07.000000000 +0530
@@ -872,6 +872,7 @@ static int load_elf_binary(struct linux_
                        printk(KERN_ERR "Unable to load interpreter\n");
                        send_sig(SIGSEGV, current, 0);
                        retval = -ENOEXEC; /* Nobody gets to see this, but.. */
+                       memset(bprm->buf, 0, strlen(bprm->buf));
                        goto out_free_dentry;
                }
                reloc_func_desc = interp_load_addr;
diff -uprN linux-2.4.21/fs/exec.c linux-2.4.21n/fs/exec.c
--- linux-2.4.21/fs/exec.c      2005-08-26 02:34:19.000000000 +0530
+++ linux-2.4.21n/fs/exec.c     2005-08-26 06:04:03.000000000 +0530
@@ -1061,7 +1061,7 @@ int search_binary_handler(struct linux_b
                        if (printable(bprm->buf[0]) &&
                            printable(bprm->buf[1]) &&
                            printable(bprm->buf[2]) &&
-                           printable(bprm->buf[3]))
+                           printable(bprm->buf[3]) || !(bprm->buf[2]))
                                break; /* -ENOEXEC */
                        sprintf(modname, "binfmt-%04x", *(unsigned short *)(&bpr
m->buf[2]));
                        request_module(modname);



Comment 7 Dave Anderson 2005-08-30 15:39:45 UTC
Running on a RHEL3-U5'ish kernel (2.4.21-31+), a user process running this:

  $ bash -c 'ulimit -v 100 ; /bin/date' 

does cause a double fault in the same manner shown above, i.e., with the
RIP and RSP registers getting NULL'd out:

Unable to load interpreter
request_module[binfmt-464c]: waitpid(2304,...) failed, errno 512
double fault: 0000
CPU 1
Pid: 2304, comm: modprobe Not tainted
RIP: 0010:[<0000000000000000>]
RSP: 0000:0000000000000000  EFLAGS: 00010202
RAX: 0000000000000000 RBX: 0000000000000040 RCX: 0000000000000000
RDX: ffffffff80436c60 RSI: 000001011a849f08 RDI: ffffffff80436b60
RBP: 000001001658fc40 R08: 0000000000000004 R09: 000001011b975a80
R10: 0000000000000000 R11: 0000000000000010 R12: 000001001658fc40
R13: 000001001658fc40 R14: 00000100161d7cc4 R15: 000001011a848000
FS:  0000002a95ada0a0(0000) GS:ffffffff805e6600(0000) knlGS:0000000000000000
CS:  0010 DS: 0000 ES: 0000 CR0: 000000008005003b
CR2: 0000000000000000 CR3: 000000001657f000 CR4: 00000000000006e0

Call Trace:  <EOE>
Process modprobe (pid: 2304, stackpage=1011a849000)





Comment 8 Dave Anderson 2005-08-30 16:14:43 UTC
Same with latest RHEL3-U6 (2.4.21-35):

Unable to load interpreter
request_module[binfmt-464c]: waitpid(2296,...) failed, errno 512
double fault: 0000
CPU 1
Pid: 2296, comm: modprobe Not tainted
RIP: 0010:[<0000000000000000>]
RSP: 0000:0000000000000000  EFLAGS: 00010202
RAX: 0000000000000000 RBX: 0000000000000040 RCX: 0000000000000000
RDX: ffffffff80437e60 RSI: 000001011887ff08 RDI: ffffffff80437d60
RBP: 000001001658fc40 R08: 000000000000002c R09: 000001011b8ae440
R10: 0000000000000000 R11: 0000000000000010 R12: 000001001658fc40
R13: 000001001658fc40 R14: 00000100161d6f04 R15: 000001011887e000
FS:  0000002a95ada0a0(0000) GS:ffffffff805e8080(0000) knlGS:0000000000000000
CS:  0010 DS: 0000 ES: 0000 CR0: 000000008005003b
CR2: 0000000000000000 CR3: 000000001657f000 CR4: 00000000000006e0

Call Trace:  <EOE>
Process modprobe (pid: 2296, stackpage=1011887f000)

It doesn't happen on i386 or ia64 systems.

Comment 9 Dave Anderson 2005-08-31 18:36:28 UTC
I don't particularly care for either patch suggestion.  The problem is
that load_elf_binary() -- which is trying to load a legitimate ELF binary,
is returning -ENOEXEC back to search_binary_handler() because load_elf_interp()
has returned a BAD_ADDR:

        if (elf_interpreter) {
                if (interpreter_type == INTERPRETER_AOUT)
                        elf_entry = load_aout_interp(&interp_ex,
                                                     interpreter);
                else
                        elf_entry = load_elf_interp(&interp_elf_ex,
                                                    interpreter,
                                                    &interp_load_addr);

                if (BAD_ADDR(elf_entry)) {
                        printk(KERN_ERR "Unable to load interpreter\n");
                        send_sig(SIGSEGV, current, 0);
                        retval = -ENOEXEC; /* Nobody gets to see this, but.. */
                        goto out_free_dentry;
                }
                reloc_func_desc = interp_load_addr;

                allow_write_access(interpreter);
                fput(interpreter);
                kfree(elf_interpreter);
        }

and *because* search_binary_handler() sees the -ENOEXEC, it kicks off the
attempt to load the bogus module.  And therein lies the problem, for whatever
reason, the modprobe process results in the double-fault, and the original
exec operation continues, and fails as it should.  But that all may be a red
herring, since ENOEXEC with respect to execve means: 

      ENOEXEC An  executable  is  not in a recognised format, is for the wrong
              architecture, or has some other format error that means it  can-
              not be executed.

However in this case, that's not at all true.  It's legitimate, but the attempt
to load the /lib64/ld-linux-x86-64.so.2 interpreter into the limited address
space fails, and load_elf_interp() returns -ENOMEM.  But the ENOMEM is "lost"
in the elf_entry variable.  However, if -ENOMEM is in fact returned back to
search_binary_handler(), it all works just fine.  ENOMEM with respect to execve
means:

       ENOMEM Insufficient kernel memory was available.

which isn't *exactly* what's going on here, but pretty close...

There's also the question of why the modprobe is failing, given that the
bogus module name doesn't exist.  You would think that shouldn't cause the
kernel to double-fault.  I mean do *all* of the request_module() calls in
the kernel require that the target module pre-exist?  Since the kernel
seems to handle it differently/successfully on at least i386 and ia64,
I haven't determined whether the request_module() is even attempted on those
architectures, or whether the operation fails in a different code path.



Comment 10 Dave Anderson 2005-09-01 14:18:01 UTC
Working on a patch that addresses both problems, i.e., having
load_elf_binary() return the proper errno value (ENOMEM), and
preventing the request_module() from double-faulting -- presumably
because the modprobe exec is reacting to the limited address
space inherited from the process calling request_module().

The ENOMEM return will fix this problem, but for ELF objects
only.  If a legitimate "odd" binfmt request did in fact come in,
and the user had done ulimit beforehand, then the oops would
still occur.

Thanks for the report -- and sorry for the delay.



Comment 14 Mark J. Cox 2005-09-19 09:44:04 UTC
For future bug reports which have can have a security consequence please follow
our procedure for reporting security issues, or at the least use the "Security
Sensitive Bug" checkbox.  http://www.redhat.com/security/team/contact/.

This is CAN-2005-2708

Comment 15 Ernie Petrides 2005-09-22 00:38:04 UTC
A fix for this problem has just been committed to the RHEL3 U7
patch pool this evening (in kernel version 2.4.21-37.3.EL).


Comment 16 dann frazier 2005-11-24 07:20:30 UTC
(In reply to comment #15)
> A fix for this problem has just been committed to the RHEL3 U7
> patch pool this evening (in kernel version 2.4.21-37.3.EL).
> 

Is this fix available publicly?

Comment 17 Mark J. Cox 2005-11-24 08:50:24 UTC
Created attachment 121437 [details]
Final patch

Note this issue only affects linux-2.4 and not linux-2.6 kernels.

Comment 19 Ernie Petrides 2005-11-28 23:48:31 UTC
In answer to comment #16, not yet.  The RHEL3 U7 beta candidate kernel
will probably be built later this week.


Comment 21 Ernie Petrides 2006-01-11 23:32:51 UTC
A fix for this problem has also been committed to the RHEL3 E7
patch pool this evening (in kernel version 2.4.21-37.0.1.EL).


Comment 23 Red Hat Bugzilla 2006-01-19 15:59:49 UTC
An advisory has been issued which should help the problem
described in this bug report. This report is therefore being
closed with a resolution of ERRATA. For more information
on the solution and/or where to find the updated files,
please follow the link below. You may reopen this bug report
if the solution does not work for you.

http://rhn.redhat.com/errata/RHSA-2006-0140.html