[Ada] Buffer overflow in ada_unpack_from_contents
authorJoel Brobecker <brobecker@adacore.com>
Fri, 9 Oct 2015 21:15:16 +0000 (14:15 -0700)
committerJoel Brobecker <brobecker@adacore.com>
Fri, 9 Oct 2015 21:33:28 +0000 (14:33 -0700)
This patch fixes a buffer overflow in ada_unpack_from_contents
caused by one of the previous commits. This happens when trying
to print the value of an array of variant records.

The overflow happens while trying to print one element of the array.
Because the size of each element in the array is variable, the array
has a DWARF byte_stride attribute, which makes us treat the array
as if it was packed. And during the extraction of each array element,
we try to unpack an object using the array's byte stride as the size,
into an element whose size is actually less than the stride.

This patch fixes the issue by overriding the byte-stride with
the actual element's length.

gdb/ChangeLog:

        * ada-lang.c (ada_value_primitive_packed_val): Move
        src_len variable to local block where used.  Override
        BIT_SIZE if bigger than size of resolved type.

gdb/ChangeLog
gdb/ada-lang.c

index 9787afeaa4b41b0cf259fe66ce1532c3c2c3da18..45e04aeb46626c176dd088305f5ece76f2e5445e 100644 (file)
@@ -1,3 +1,9 @@
+2015-10-09  Joel Brobecker  <brobecker@adacore.com>
+
+       * ada-lang.c (ada_value_primitive_packed_val): Move
+       src_len variable to local block where used.  Override
+       BIT_SIZE if bigger than size of resolved type.
+
 2015-10-09  Joel Brobecker  <brobecker@adacore.com>
 
        * gdbtypes.h (is_scalar_type): Add extern declaration.
index d9bbed9a9ebcb0b200b6587f887d088fd77b8871..b7440e28820f977683086b1987e33843c8dc52b2 100644 (file)
@@ -2517,7 +2517,6 @@ ada_value_primitive_packed_val (struct value *obj, const gdb_byte *valaddr,
 {
   struct value *v;
   gdb_byte *src;                /* First byte containing data to unpack */
-  int src_len = (bit_size + bit_offset + HOST_CHAR_BIT - 1) / 8;
   gdb_byte *unpacked;
   const int is_scalar = is_scalar_type (type);
   const int is_big_endian = gdbarch_bits_big_endian (get_type_arch (type));
@@ -2550,6 +2549,17 @@ ada_value_primitive_packed_val (struct value *obj, const gdb_byte *valaddr,
                                is_big_endian, has_negatives (type),
                                is_scalar);
       type = resolve_dynamic_type (type, staging, 0);
+      if (TYPE_LENGTH (type) < (bit_size + HOST_CHAR_BIT - 1) / HOST_CHAR_BIT)
+       {
+         /* This happens when the length of the object is dynamic,
+            and is actually smaller than the space reserved for it.
+            For instance, in an array of variant records, the bit_size
+            we're given is the array stride, which is constant and
+            normally equal to the maximum size of its element.
+            But, in reality, each element only actually spans a portion
+            of that stride.  */
+         bit_size = TYPE_LENGTH (type) * HOST_CHAR_BIT;
+       }
     }
 
   if (obj == NULL)
@@ -2559,6 +2569,8 @@ ada_value_primitive_packed_val (struct value *obj, const gdb_byte *valaddr,
     }
   else if (VALUE_LVAL (obj) == lval_memory && value_lazy (obj))
     {
+      int src_len = (bit_size + bit_offset + HOST_CHAR_BIT - 1) / 8;
+
       v = value_at (type, value_address (obj) + offset);
       src = alloca (src_len);
       read_memory (value_address (v), src, src_len);