Bug 447518

Summary: Call to capget() overflows buffers
Product: [Fedora] Fedora Reporter: Bojan Smojver <bojan>
Component: kernelAssignee: Kernel Maintainer List <kernel-maint>
Status: CLOSED CURRENTRELEASE QA Contact: Fedora Extras Quality Assurance <extras-qa>
Severity: high Docs Contact:
Priority: high    
Version: 9CC: jakub, mnagy, morgan, nhorman
Target Milestone: ---   
Target Release: ---   
Hardware: i686   
OS: Linux   
Whiteboard:
Fixed In Version: 2.6.25.6-55.fc9 Doc Type: Bug Fix
Doc Text:
Story Points: ---
Clone Of: Environment:
Last Closed: 2008-06-13 02:27:09 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:
Attachments:
Description Flags
Example program
none
A program to verify how much of a buffer was used
none
Program that creates the crash
none
Proposed kernel (include/linux/capability.h) 'fix' for this issue
none
new kernel patch from lkml none

Description Bojan Smojver 2008-05-20 09:47:28 UTC
Description of problem:
Try to compile the attached program and see it crash with:
------------------------------------------------------------------
*** glibc detected *** ./a: free(): invalid next size (fast): 0x0817b018 ***
======= Backtrace: =========
/lib/libc.so.6[0x3fa7e4]
/lib/libc.so.6(cfree+0x96)[0x3fc846]
./a[0x804848f]
/lib/libc.so.6(__libc_start_main+0xe6)[0x3a35d6]
./a[0x80483a1]
======= Memory map: ========
00110000-00111000 r-xp 00110000 00:00 0          [vdso]
00369000-00385000 r-xp 00000000 08:02 2820377    /lib/ld-2.8.so
00385000-00386000 r--p 0001c000 08:02 2820377    /lib/ld-2.8.so
00386000-00387000 rw-p 0001d000 08:02 2820377    /lib/ld-2.8.so
0038d000-004f0000 r-xp 00000000 08:02 2820378    /lib/libc-2.8.so
004f0000-004f2000 r--p 00163000 08:02 2820378    /lib/libc-2.8.so
004f2000-004f3000 rw-p 00165000 08:02 2820378    /lib/libc-2.8.so
004f3000-004f6000 rw-p 004f3000 00:00 0 
00bb6000-00bc3000 r-xp 00000000 08:02 2818255    /lib/libgcc_s-4.3.0-20080428.so.1
00bc3000-00bc4000 rw-p 0000c000 08:02 2818255    /lib/libgcc_s-4.3.0-20080428.so.1
08048000-08049000 r-xp 00000000 08:06 4686295    /home/users/bojan/t/a
08049000-0804a000 rw-p 00000000 08:06 4686295    /home/users/bojan/t/a
0817b000-0819c000 rw-p 0817b000 00:00 0          [heap]
b7f00000-b7f21000 rw-p b7f00000 00:00 0 
b7f21000-b8000000 ---p b7f21000 00:00 0 
b80da000-b80dc000 rw-p b80da000 00:00 0 
bf8de000-bf8f3000 rw-p bffeb000 00:00 0          [stack]
Aborted
------------------------------------------------------------------

BTW, adding 10 more bytes to allocation of data seems to avoid the crash.

Version-Release number of selected component (if applicable):
2.6.25.3-18.fc9

How reproducible:
Always.

Steps to Reproduce:
1. Compile the attached program with gcc -g -O2 -o a a.c
2. Run it.
  
Actual results:
Shouldn't crash?

Expected results:
Dies.

Additional info:
See bug #447045 and how it affects squid-3.0.STABLE5

Comment 1 Bojan Smojver 2008-05-20 09:47:28 UTC
Created attachment 306100 [details]
Example program

Comment 2 Bojan Smojver 2008-05-20 10:38:47 UTC
Actually, make that 12 bytes. I'll attach another program that outputs this:

0000000000000000000000004141414141414141

This is a hex output of the buffer that was filled with 'A's before capget() call.

Comment 3 Bojan Smojver 2008-05-20 10:39:19 UTC
Created attachment 306104 [details]
A program to verify how much of a buffer was used

Comment 4 Jakub Jelinek 2008-05-20 13:39:45 UTC
Why has this been reassigned to glibc?  catget in glibc is just a syscall
wrapper, with no extra code whatsoever, and sys/capability.h includes
linux/capability.h
from kernel-headers to get the types.
So how much data is filled in and what are the structure sizes is solely a
kernel issue.

Comment 5 Matt Bernstein 2008-05-22 07:08:04 UTC
Your program compiles and runs without the crash on x86_64.

Comment 6 Bojan Smojver 2008-05-22 10:15:16 UTC
It's been filed for i686.

Comment 7 Bojan Smojver 2008-05-22 10:56:59 UTC
Just note that in order to see the crash, the first program needs to modified to
remove extra 10 bytes being allocated. I verified on another i686 machine. I'll
attach that program as well.

Comment 8 Bojan Smojver 2008-05-22 10:57:30 UTC
Created attachment 306360 [details]
Program that creates the crash

Comment 9 Andrew G. Morgan 2008-05-22 19:39:26 UTC
It seems to me that this sample code is broken.

The assumption of the provided sample code is that is understands what
the data format is for the prevailing kernel magic:

=========================
#include <stdio.h>
#include <stdlib.h>
#include <sys/capability.h>

int main(int argc,char **argv){
  cap_user_header_t head=malloc(sizeof(*head));
  cap_user_data_t data=malloc(sizeof(*data));

  head->version=_LINUX_CAPABILITY_VERSION;

  capget(head,data);

  free(data);
  free(head);

  return 0;
}
=========================

Unfortunately, it is not interpreting the value of
_LINUX_CAPABILITY_VERSION - which does, after all, version the
size/meaning of the 'head' and 'data'. If the user wants to supply a
buffer of this size, then they should use

   head->version = _LINUX_CAPABILITY_VERSION_1

on the other hand, the user might want to explore using libcap to
protect him from this level of detail.


Comment 10 Bojan Smojver 2008-05-22 21:08:11 UTC
So, what you saying is that Squid (see bug 447045) actually uses this
incorrectly, right? They should be either passing in _LINUX_CAPABILITY_VERSION_1
or doing POSIX calls cap_get_proc()/cap_set_proc().

Please confirm so that I can close and notify Squid folks.

Comment 11 Bojan Smojver 2008-05-22 21:25:52 UTC
Ah, sorry. Spoke before seeing e-mails from other kernel folks. Please disregard
comment #10.

Comment 12 Bojan Smojver 2008-05-22 23:44:15 UTC
Just of of curiosity, if _LINUX_CAPABILITY_VERSION is chosen (as opposed to
_LINUX_CAPABILITY_VERSION_1 as you suggested), with what size buffers should
capget() be called?

Comment 13 Chuck Ebbert 2008-05-23 01:59:49 UTC
(In reply to comment #12)
> Just of of curiosity, if _LINUX_CAPABILITY_VERSION is chosen (as opposed to
> _LINUX_CAPABILITY_VERSION_1 as you suggested), with what size buffers should
> capget() be called?

The kernel headers won't actually let you use VERSION_2 afaict. Looks like old
programs compiled with old headers should be fine, but maybe the default in the
kernel headers should be version 1 since that's the only one that will work right.

Comment 14 Andrew G. Morgan 2008-05-23 14:28:17 UTC
Created attachment 306503 [details]
Proposed kernel (include/linux/capability.h) 'fix' for this issue

I'm not sure I follow comment #13, version's 1 and 2 work fine for me.

libcap2, for example, does something like the following to figure out which ABI
the running kernel prefers/supports:

   struct __user_cap_header_struct head;
   head.version = _LINUX_CAPABILITY_VERSION;
   (void) capget(&head, NULL);
   switch (head.version) {
   case _LINUX_CAPABILITY_VERSION_1:
      version = _LINUX_CAPABILITY_VERSION_1;
      u32s = _LINUX_CAPABILITY_U32S_1;
      break;
   case _LINUX_CAPABILITY_VERSION_2:
      version = _LINUX_CAPABILITY_VERSION_2;
      u32s = _LINUX_CAPABILITY_U32S_2;
      break;
   default:
       abort("no idea what to do"); /* new version not defined */
   }

   data = calloc(u32s, sizeof(struct __user_cap_data_struct));
   head.pid = pid_of_target;
   capget(&head, data);

If the kernel evolves a third iteration of the ABI, then this switch etc code
will evolve again.

In the light of this bug report, libcap will likely stop using
_LINUX_CAPABILITY_VERSION at all and be more explicit with version numbers,
since I expect the _LINUX_CAPABILITY_VERSION value to revert to
_LINUX_CAPABILITY_VERSION_1 to help suppress issues like the one raised in this
bug report.

So, thanks for highlighting this issue in general!

Comment 15 Chuck Ebbert 2008-05-27 22:15:47 UTC
(In reply to comment #13)
> 
> I'm not sure I follow comment #13, version's 1 and 2 work fine for me.
> 

You can't use version 2 unless you write your own headers; the kernel headers
don't provide any memory structures for using it even though they set the
default to 2.


Comment 16 Chuck Ebbert 2008-05-28 02:45:07 UTC
Created attachment 306879 [details]
new kernel patch from lkml

This patch looks even better...

Comment 17 Andrew G. Morgan 2008-05-28 15:32:46 UTC
(In reply to comment #15)
> (In reply to comment #13)
> > 
> > I'm not sure I follow comment #13, version's 1 and 2 work fine for me.
> > 
> 
> You can't use version 2 unless you write your own headers; the kernel headers
> don't provide any memory structures for using it even though they set the
> default to 2.
> 

  typedef struct __user_cap_data_struct {
        __u32 effective;
        __u32 permitted;
        __u32 inheritable;
  } __user *cap_user_data_t;

this type is unchanged. With v2(and now v3), the pointer data refers to

  struct __user_cap_data_struct data[_LINUX_CAPABILITY_U32S_3];

Hope that helps.

PS. If we ever need v4 capabilities, we reserve the right to redefine this data
structure...

Hope that helps.

Andrew


Comment 18 Chuck Ebbert 2008-06-07 01:20:53 UTC
Patch went in 2.6.25.4-47

Comment 19 Fedora Update System 2008-06-12 01:38:31 UTC
kernel-2.6.25.6-55.fc9 has been submitted as an update for Fedora 9

Comment 20 Fedora Update System 2008-06-13 02:26:42 UTC
kernel-2.6.25.6-55.fc9 has been pushed to the Fedora 9 stable repository.  If problems still persist, please make note of it in this bug report.