Fix buffer overflow in ada-lang.c:move_bits

Message ID 20181024162037.21024-1-tom@tromey.com
State New, archived
Headers

Commit Message

Tom Tromey Oct. 24, 2018, 4:20 p.m. UTC
  -fsanitize=address showed that ada-lang.c:move_bits can run off the
end of the source buffer.  I believe this patch fixes the problem, by
arranging not to read from the source buffer once there are sufficient
bits in the accumulator.

gdb/ChangeLog
2018-10-23  Tom Tromey  <tom@tromey.com>

	* ada-lang.c (move_bits): Don't run off the end of the source
	buffer.
---
 gdb/ChangeLog  |  5 +++++
 gdb/ada-lang.c | 18 ++++++++++++------
 2 files changed, 17 insertions(+), 6 deletions(-)
  

Comments

Joel Brobecker Nov. 1, 2018, 3:35 p.m. UTC | #1
Hi Tom,

> -fsanitize=address showed that ada-lang.c:move_bits can run off the
> end of the source buffer.  I believe this patch fixes the problem, by
> arranging not to read from the source buffer once there are sufficient
> bits in the accumulator.
> 
> gdb/ChangeLog
> 2018-10-23  Tom Tromey  <tom@tromey.com>
> 
> 	* ada-lang.c (move_bits): Don't run off the end of the source
> 	buffer.

Thanks for the patch!

This is a part of the code that always forces me to think twice
(or ten times), each time I try to touch it. I should really start
adding comments to this code that detail what we are trying to do
as we do it.

I tested your change through our testsuite on the various baremetal
targets we have, and noticed that it causes regressions on ppc and arm
targets. It's hopefully something small, but just being back from
a holiday, I'm a bit tied up at work; I'll put that issue on my TODO
list to look at further.

> ---
>  gdb/ChangeLog  |  5 +++++
>  gdb/ada-lang.c | 18 ++++++++++++------
>  2 files changed, 17 insertions(+), 6 deletions(-)
> 
> diff --git a/gdb/ada-lang.c b/gdb/ada-lang.c
> index 1462271a71..7288d65df6 100644
> --- a/gdb/ada-lang.c
> +++ b/gdb/ada-lang.c
> @@ -2682,9 +2682,12 @@ move_bits (gdb_byte *target, int targ_offset, const gdb_byte *source,
>          {
>            int unused_right;
>  
> -          accum = (accum << HOST_CHAR_BIT) + (unsigned char) *source;
> -          accum_bits += HOST_CHAR_BIT;
> -          source += 1;
> +	  if (n > accum_bits)
> +	    {
> +	      accum = (accum << HOST_CHAR_BIT) + (unsigned char) *source;
> +	      accum_bits += HOST_CHAR_BIT;
> +	      source += 1;
> +	    }
>            chunk_size = HOST_CHAR_BIT - targ_offset;
>            if (chunk_size > n)
>              chunk_size = n;
> @@ -2707,9 +2710,12 @@ move_bits (gdb_byte *target, int targ_offset, const gdb_byte *source,
>  
>        while (n > 0)
>          {
> -          accum = accum + ((unsigned char) *source << accum_bits);
> -          accum_bits += HOST_CHAR_BIT;
> -          source += 1;
> +	  if (n > accum_bits)
> +	    {
> +	      accum = accum + ((unsigned char) *source << accum_bits);
> +	      accum_bits += HOST_CHAR_BIT;
> +	      source += 1;
> +	    }
>            chunk_size = HOST_CHAR_BIT - targ_offset;
>            if (chunk_size > n)
>              chunk_size = n;
> -- 
> 2.17.1
  
Tom Tromey Nov. 1, 2018, 10:11 p.m. UTC | #2
>>>>> "Joel" == Joel Brobecker <brobecker@adacore.com> writes:

Joel> I tested your change through our testsuite on the various baremetal
Joel> targets we have, and noticed that it causes regressions on ppc and arm
Joel> targets. It's hopefully something small, but just being back from
Joel> a holiday, I'm a bit tied up at work; I'll put that issue on my TODO
Joel> list to look at further.

Thanks.  To reproduce the problem I saw, just rebuild with
-fsanitize=address and run the gdb.ada tests.  I don't recall exactly
which ones failed, but you should definitely see a read off the end of
the source buffer.

Tom
  
Pedro Alves Nov. 8, 2018, 7:11 p.m. UTC | #3
On 11/01/2018 03:35 PM, Joel Brobecker wrote:
> Hi Tom,
> 
>> -fsanitize=address showed that ada-lang.c:move_bits can run off the
>> end of the source buffer.  I believe this patch fixes the problem, by
>> arranging not to read from the source buffer once there are sufficient
>> bits in the accumulator.
>>
>> gdb/ChangeLog
>> 2018-10-23  Tom Tromey  <tom@tromey.com>
>>
>> 	* ada-lang.c (move_bits): Don't run off the end of the source
>> 	buffer.
> 
> Thanks for the patch!
> 
> This is a part of the code that always forces me to think twice
> (or ten times), each time I try to touch it. I should really start
> adding comments to this code that detail what we are trying to do
> as we do it.
> 
> I tested your change through our testsuite on the various baremetal
> targets we have, and noticed that it causes regressions on ppc and arm
> targets. It's hopefully something small, but just being back from
> a holiday, I'm a bit tied up at work; I'll put that issue on my TODO
> list to look at further.

I was going to suggest that this would benefit from unit tests in
the style of dwarf2read.c:copy_bitwise's, but, actually, isn't this
exactly the same as copy_bitwise?  Can we get rid of ada-lang.c:move_bits?
(And maybe move copy_bitwise elsewhere?)

Thanks,
Pedro Alves
  
Pedro Alves Nov. 8, 2018, 7:12 p.m. UTC | #4
On 11/08/2018 07:11 PM, Pedro Alves wrote:
> On 11/01/2018 03:35 PM, Joel Brobecker wrote:
>> Hi Tom,
>>
>>> -fsanitize=address showed that ada-lang.c:move_bits can run off the
>>> end of the source buffer.  I believe this patch fixes the problem, by
>>> arranging not to read from the source buffer once there are sufficient
>>> bits in the accumulator.
>>>
>>> gdb/ChangeLog
>>> 2018-10-23  Tom Tromey  <tom@tromey.com>
>>>
>>> 	* ada-lang.c (move_bits): Don't run off the end of the source
>>> 	buffer.
>>
>> Thanks for the patch!
>>
>> This is a part of the code that always forces me to think twice
>> (or ten times), each time I try to touch it. I should really start
>> adding comments to this code that detail what we are trying to do
>> as we do it.
>>
>> I tested your change through our testsuite on the various baremetal
>> targets we have, and noticed that it causes regressions on ppc and arm
>> targets. It's hopefully something small, but just being back from
>> a holiday, I'm a bit tied up at work; I'll put that issue on my TODO
>> list to look at further.
> 
> I was going to suggest that this would benefit from unit tests in
> the style of dwarf2read.c:copy_bitwise's, but, actually, isn't this
> exactly the same as copy_bitwise?  Can we get rid of ada-lang.c:move_bits?
> (And maybe move copy_bitwise elsewhere?)

I meant to say dwarf2loc.c instead of dwarf2read.c.

Thanks,
Pedro Alves
  
Joel Brobecker Nov. 9, 2018, 5:16 p.m. UTC | #5
> > I was going to suggest that this would benefit from unit tests in
> > the style of dwarf2read.c:copy_bitwise's, but, actually, isn't this
> > exactly the same as copy_bitwise?  Can we get rid of ada-lang.c:move_bits?
> > (And maybe move copy_bitwise elsewhere?)
> 
> I meant to say dwarf2loc.c instead of dwarf2read.c.

It does look exactly the same, doesn't it? I'll see if we can just
re-use dwarf2loc's copy_bitwise. Thanks for the suggestion!
  

Patch

diff --git a/gdb/ada-lang.c b/gdb/ada-lang.c
index 1462271a71..7288d65df6 100644
--- a/gdb/ada-lang.c
+++ b/gdb/ada-lang.c
@@ -2682,9 +2682,12 @@  move_bits (gdb_byte *target, int targ_offset, const gdb_byte *source,
         {
           int unused_right;
 
-          accum = (accum << HOST_CHAR_BIT) + (unsigned char) *source;
-          accum_bits += HOST_CHAR_BIT;
-          source += 1;
+	  if (n > accum_bits)
+	    {
+	      accum = (accum << HOST_CHAR_BIT) + (unsigned char) *source;
+	      accum_bits += HOST_CHAR_BIT;
+	      source += 1;
+	    }
           chunk_size = HOST_CHAR_BIT - targ_offset;
           if (chunk_size > n)
             chunk_size = n;
@@ -2707,9 +2710,12 @@  move_bits (gdb_byte *target, int targ_offset, const gdb_byte *source,
 
       while (n > 0)
         {
-          accum = accum + ((unsigned char) *source << accum_bits);
-          accum_bits += HOST_CHAR_BIT;
-          source += 1;
+	  if (n > accum_bits)
+	    {
+	      accum = accum + ((unsigned char) *source << accum_bits);
+	      accum_bits += HOST_CHAR_BIT;
+	      source += 1;
+	    }
           chunk_size = HOST_CHAR_BIT - targ_offset;
           if (chunk_size > n)
             chunk_size = n;