GDB crash trying to subscript array of variant record.

Consider the following declarations:

   subtype Small_Type is Integer range 0 .. 10;
   type Record_Type (I : Small_Type := 0) is record
      S : String (1 .. I);
   end record;
   A2 : Array_Type := (1 => (I => 2, S => "AB"),
                       2 => (I => 1, S => "A"),
                       3 => (I => 0, S => <>));

Compiled with -fgnat-encodings=minimal, and trying to print
one element of our array, valgrind reports an invalid memory
access. On certain GNU/Linux boxes, malloc even reports it as
well, and causes GDB to crash.

    (gdb) print a2(1)
     *** glibc detected *** /[...]/gdb:
         malloc(): memory corruption: 0x0a30ba48 ***
    [crash]

The invalid memory access occurs because of a simple buffer
overflow in ada_value_primitive_packed_val. When this function
is called, it is given a bit_size of 128 (or 16 bytes), which
corresponds to the stride of our array. But the actual size of
each element depends on its value. In particular, A2(1) is a record
whose size is only 6 bytes.

What happens in our example is that we start building a new value
(v) where the element is to be unpacked, with any of its dynamic
properties getting resolved as well. We then unpack the data into
this value's buffer:

  unpacked = (unsigned char *) value_contents (v);
  [...]
  nsrc = len;
  [...]
  while (nsrc > 0)
    {
      [...]
          unpacked[targ] = accum & ~(~0L << HOST_CHAR_BIT);
          [...]
          targ += delta;
      [...]
      nsrc -= 1;
      [...]
    }

In the loop above, targ starts at zero (for LE architectures),
and len is 16. With delta being +1, we end up iterating 16 times,
writing 16 bytes into a 6-bytes buffer.

This patch fixes the issue by adjusting BIT_SIZE and recomputing
LEN after having resolved our type if the resolved type turns out
to be smaller than bit_size.

gdb/ChangeLog:

        * ada-lang.c (ada_value_primitive_packed_val): Recompute
        BIT_SIZE and LEN if the size of the resolved type is smaller
        than BIT_SIZE * HOST_CHAR_BIT.
This commit is contained in:
Joel Brobecker 2015-04-14 11:55:57 -07:00
parent ca34b84ff6
commit fc958966e4
2 changed files with 17 additions and 0 deletions

View File

@ -1,3 +1,9 @@
2015-05-05 Joel Brobecker <brobecker@adacore.com>
* ada-lang.c (ada_value_primitive_packed_val): Recompute
BIT_SIZE and LEN if the size of the resolved type is smaller
than BIT_SIZE * HOST_CHAR_BIT.
2015-05-05 Joel Brobecker <brobecker@adacore.com>
* ada-lang.c (ada_value_primitive_packed_val): Use a more

View File

@ -2419,6 +2419,17 @@ ada_value_primitive_packed_val (struct value *obj, const gdb_byte *valaddr,
{
v = value_at (type, value_address (obj) + offset);
type = value_type (v);
if (TYPE_LENGTH (type) * HOST_CHAR_BIT < bit_size)
{
/* This can happen in the case of an array of dynamic objects,
where the size of each element changes from element to element.
In that case, we're initially given the array stride, but
after resolving the element type, we find that its size is
less than this stride. In that case, adjust bit_size to
match TYPE's length, and recompute LEN accordingly. */
bit_size = TYPE_LENGTH (type) * HOST_CHAR_BIT;
len = TYPE_LENGTH (type) + (bit_offset + HOST_CHAR_BIT - 1) / 8;
}
bytes = (unsigned char *) alloca (len);
read_memory (value_address (v), bytes, len);
}