diff options
author | Joel Brobecker <brobecker@adacore.com> | 2015-04-14 11:55:57 -0700 |
---|---|---|
committer | Joel Brobecker <brobecker@adacore.com> | 2015-05-05 10:47:44 -0700 |
commit | fc958966e47f622d738088509bacd0573a9db2c7 (patch) | |
tree | 6455a4e6ff2285e9f752405d92cfb274c5cd46a2 /gdb | |
parent | ca34b84ff68f5823e215a4d0b44b92f35cbb74a5 (diff) | |
download | gdb-fc958966e47f622d738088509bacd0573a9db2c7.zip gdb-fc958966e47f622d738088509bacd0573a9db2c7.tar.gz gdb-fc958966e47f622d738088509bacd0573a9db2c7.tar.bz2 |
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.
Diffstat (limited to 'gdb')
-rw-r--r-- | gdb/ChangeLog | 6 | ||||
-rw-r--r-- | gdb/ada-lang.c | 11 |
2 files changed, 17 insertions, 0 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 7c61032..990bad7 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,5 +1,11 @@ 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 correct address in call to value_at. Adjust call to value_address accordingly. diff --git a/gdb/ada-lang.c b/gdb/ada-lang.c index 42f84e4..d0aea26 100644 --- a/gdb/ada-lang.c +++ b/gdb/ada-lang.c @@ -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); } |