Bug 24132 - A suspicious unsigned integer overflow which may bypass a check
Summary: A suspicious unsigned integer overflow which may bypass a check
Status: RESOLVED FIXED
Alias: None
Product: binutils
Classification: Unclassified
Component: binutils (show other bugs)
Version: 2.31
: P2 normal
Target Milestone: ---
Assignee: Alan Modra
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2019-01-24 19:04 UTC by poppeter1982
Modified: 2019-02-20 10:52 UTC (History)
1 user (show)

See Also:
Host:
Target:
Build:
Last reconfirmed:


Attachments
The PoC to demonstrate the unsigned integer overflow (206.19 KB, application/x-executable)
2019-01-24 19:04 UTC, poppeter1982
Details
attachment-66164-0.html (1.33 KB, text/html)
2019-01-25 19:02 UTC, poppeter1982
Details
Proposed patch (506 bytes, patch)
2019-01-28 13:04 UTC, Nick Clifton
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description poppeter1982 2019-01-24 19:04:50 UTC
Created attachment 11569 [details]
The PoC to demonstrate the unsigned integer overflow

Hi There
 
Peng Li and Shengjian Guo at Baidu XLab found a suspicious unsigned integer overflow which may bypass a check unintentionally. The bug is found in function process_program_headers of readelf.c of version 2.31.51.20190117.
 
static bfd_boolean
process_program_headers (Filedata * filedata)
{
            …
          /* PR binutils/17512: Avoid corrupt dynamic section info in the
             segment.  Check this after matching against the section headers
             so we don't warn on debuginfo file (which have NOBITS .dynamic
             sections).  */
          if (dynamic_addr + dynamic_size >= filedata->file_size)
            {
              error (_("the dynamic segment offset + size exceeds the size of the file\n"));
              dynamic_addr = dynamic_size = 0;
            }
          break;
            …
}
 
If you compile readelf with -fsanitize=unsigned-integer-overflow and run ./readelf -a input, it is found that dynamic_addr + dynamic_size overflows and may bypass the check. Can you please help verify if it is a true positive and do you think adding check for each variable against file_size is necessary?
 
If you have any questions about this issue and input in the attachment, please let me know.
 
Thanks
Peng
Comment 1 Nick Clifton 2019-01-25 12:14:56 UTC
(In reply to poppeter1982 from comment #0)

Hi Peng,

>           if (dynamic_addr + dynamic_size >= filedata->file_size)

> If you compile readelf with -fsanitize=unsigned-integer-overflow

The version of gcc that I am using (8.2.1 on Fedora 29) does not support
a -fsanitize=unsigned-integer-overflow option.  Is this a new feature ?


> and run
> ./readelf -a input, it is found that dynamic_addr + dynamic_size overflows
> and may bypass the check. Can you please help verify if it is a true
> positive

I added a printf statement before the check to examine the values of
these variables:

  fprintf (stderr, "addr %lx size %llx file %llx plus %llx\n",
     dynamic_addr, dynamic_size, filedata->file_size, 
     dynamic_addr + dynamic_size);

But it appears that the arithmetic works:

  addr 22000016 size 60000e002200002f file c190d plus 60000e0044000045
  readelf: Error: the dynamic segment offset + size exceeds the size of the file

(This is with a 32-bit toolchain, which I presume you are using.  You
did not actually specify how you configured your binutils build).

The point is that the dynamic_size and file_size variables are both
unsigned long long types, and so the arithmetic does not overflow.

So I think that the check should be OK.

Cheers
  Nick
Comment 2 poppeter1982 2019-01-25 19:02:40 UTC
Created attachment 11572 [details]
attachment-66164-0.html

Hi Nick

nickc at redhat dot com <sourceware-bugzilla@sourceware.org> 于2019年1月25日周五
上午4:14写道:

> https://sourceware.org/bugzilla/show_bug.cgi?id=24132
>
> Nick Clifton <nickc at redhat dot com> changed:
>
>            What    |Removed                     |Added
>
> ----------------------------------------------------------------------------
>                  CC|                            |nickc at redhat dot com
>
> --- Comment #1 from Nick Clifton <nickc at redhat dot com> ---
> (In reply to poppeter1982 from comment #0)
>
> Hi Peng,
>
> >           if (dynamic_addr + dynamic_size >= filedata->file_size)
>
> > If you compile readelf with -fsanitize=unsigned-integer-overflow
>
> The version of gcc that I am using (8.2.1 on Fedora 29) does not support
> a -fsanitize=unsigned-integer-overflow option.  Is this a new feature ?
>

I used clang as the compilation frontend


>
>
> > and run
> > ./readelf -a input, it is found that dynamic_addr + dynamic_size
> overflows
> > and may bypass the check. Can you please help verify if it is a true
> > positive
>
> I added a printf statement before the check to examine the values of
> these variables:
>
>   fprintf (stderr, "addr %lx size %llx file %llx plus %llx\n",
>      dynamic_addr, dynamic_size, filedata->file_size,
>      dynamic_addr + dynamic_size);
>
> But it appears that the arithmetic works:
>
>   addr 22000016 size 60000e002200002f file c190d plus 60000e0044000045
>   readelf: Error: the dynamic segment offset + size exceeds the size of the
> file
>
> (This is with a 32-bit toolchain, which I presume you are using.  You
> did not actually specify how you configured your binutils build).
>
> The point is that the dynamic_size and file_size variables are both
> unsigned long long types, and so the arithmetic does not overflow.
>
> So I think that the check should be OK.
>

I compiled binutils in 64-bits platform, and carefully checked the output.
I agree with you that based on this input, the check will not be bypassed.
However, do you think there exist the possibility that this check will fail
due
to overflow?

I reported a similar issue
https://sourceware.org/bugzilla/show_bug.cgi?id=24138,
the check fails due to overflow in this case.

Best,
Peng
Comment 3 Nick Clifton 2019-01-28 13:04:00 UTC
Created attachment 11575 [details]
Proposed patch

Hi Peng,

  OK, well I cannot reproduce the problem, but that does not mean that it 
  does not exist.  Please could you try out this potential patch and let
  me know if it solves the problem for you ?

Cheers
  Nick

PS.  The patch should also work for PR 24138.
Comment 4 Sourceware Commits 2019-02-20 08:33:17 UTC
The master branch has been updated by Alan Modra <amodra@sourceware.org>:

https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=c22b42ce308eb538050b4b5789e406b63102b35a

commit c22b42ce308eb538050b4b5789e406b63102b35a
Author: Alan Modra <amodra@gmail.com>
Date:   Wed Feb 20 18:22:50 2019 +1030

    Unsigned integer overflows in readelf checks
    
    	PR 24132
    	PR 24138
    	* readelf.c (get_data): Avoid possibility of overflow when
    	checking for a read that may extend past end of file.
    	(process_program_headers): Likewise.
Comment 5 Alan Modra 2019-02-20 10:52:46 UTC
Fixed.