[RFA] PR gdb/17210 - fix possible memory leak in read_memory_robust
Commit Message
PR gdb/17210 concerns a possible memory leak in read_memory_robust.
The bug can happen because read_memory_robust allocates memory, does
not install any cleanups, and invokes QUIT. Similarly, it target_read
calls QUIT, so it too can potentially throw.
The fix is to install cleanups to guard the allocated memory.
Built and regtested on x86-64 Fedora 23. I couldn't think of a way to
test this, so no new test; and of course this means it should have
more careful review.
2016-06-09 Tom Tromey <tom@tromey.com>
PR gdb/17210:
* target.c (free_memory_read_result_vector): Take a pointer to the
VEC as an argument.
(read_memory_robust): Install a cleanup for "result".
* mi/mi-main.c (mi_cmd_data_read_memory_bytes): Update.
---
gdb/ChangeLog | 8 ++++++++
gdb/mi/mi-main.c | 2 +-
gdb/target.c | 15 +++++++++++----
3 files changed, 20 insertions(+), 5 deletions(-)
Comments
On Thu, Jun 9, 2016 at 5:33 PM, Tom Tromey <tom@tromey.com> wrote:
>
> VEC(memory_read_result_s) *
> @@ -1810,6 +1810,8 @@ read_memory_robust (struct target_ops *ops,
> {
> VEC(memory_read_result_s) *result = 0;
> int unit_size = gdbarch_addressable_memory_unit_size (target_gdbarch ());
> + struct cleanup *cleanup = make_cleanup (free_memory_read_result_vector,
> + &result);
>
result is a local variable on stack, so its address is meaningless when the
exception is throw, because the stack has already been destroyed.
Probably, we can register cleanup for result once it becomes to non-NULL,
and changes in free_memory_read_result_vector are not needed.
>>>>> "Yao" == Yao Qi <qiyaoltc@gmail.com> writes:
Yao> On Thu, Jun 9, 2016 at 5:33 PM, Tom Tromey <tom@tromey.com> wrote:
>>
>> VEC(memory_read_result_s) *
>> @@ -1810,6 +1810,8 @@ read_memory_robust (struct target_ops *ops,
>> {
>> VEC(memory_read_result_s) *result = 0;
>> int unit_size = gdbarch_addressable_memory_unit_size (target_gdbarch ());
>> + struct cleanup *cleanup = make_cleanup (free_memory_read_result_vector,
>> + &result);
>>
Yao> result is a local variable on stack, so its address is meaningless when the
Yao> exception is throw, because the stack has already been destroyed.
Yao> Probably, we can register cleanup for result once it becomes to non-NULL,
Yao> and changes in free_memory_read_result_vector are not needed.
I don't think that will work, because resizing the vector may cause the
value to change. Though one option would be to discard the cleanup and
recreate it after each push.
Tom
On 06/28/2016 11:42 AM, Yao Qi wrote:
> On Thu, Jun 9, 2016 at 5:33 PM, Tom Tromey <tom@tromey.com> wrote:
>>
>> VEC(memory_read_result_s) *
>> @@ -1810,6 +1810,8 @@ read_memory_robust (struct target_ops *ops,
>> {
>> VEC(memory_read_result_s) *result = 0;
>> int unit_size = gdbarch_addressable_memory_unit_size (target_gdbarch ());
>> + struct cleanup *cleanup = make_cleanup (free_memory_read_result_vector,
>> + &result);
>>
>
> result is a local variable on stack, so its address is meaningless when the
> exception is throw, because the stack has already been destroyed.
Can you clarify?
Cleanups do run before the stack is destroyed. See most
free_current_contents users.
Thanks,
Pedro Alves
On Tue, Jun 28, 2016 at 6:47 PM, Pedro Alves <palves@redhat.com> wrote:
> On 06/28/2016 11:42 AM, Yao Qi wrote:
>> On Thu, Jun 9, 2016 at 5:33 PM, Tom Tromey <tom@tromey.com> wrote:
>>>
>>> VEC(memory_read_result_s) *
>>> @@ -1810,6 +1810,8 @@ read_memory_robust (struct target_ops *ops,
>>> {
>>> VEC(memory_read_result_s) *result = 0;
>>> int unit_size = gdbarch_addressable_memory_unit_size (target_gdbarch ());
>>> + struct cleanup *cleanup = make_cleanup (free_memory_read_result_vector,
>>> + &result);
>>>
>>
>> result is a local variable on stack, so its address is meaningless when the
>> exception is throw, because the stack has already been destroyed.
>
> Can you clarify?
> Cleanups do run before the stack is destroyed. See most
> free_current_contents users.
Urr.. right... do_cleanups is called when the exception is thrown
where the stack
is not destroyed yet. I thought do_cleanups is called when gdb goes back to
the top level in this case.
Tom, the patch is good to me then.
@@ -1,3 +1,11 @@
+2016-06-09 Tom Tromey <tom@tromey.com>
+
+ PR gdb/17210:
+ * target.c (free_memory_read_result_vector): Take a pointer to the
+ VEC as an argument.
+ (read_memory_robust): Install a cleanup for "result".
+ * mi/mi-main.c (mi_cmd_data_read_memory_bytes): Update.
+
2016-06-07 Simon Marchi <simon.marchi@ericsson.com>
* mi/mi-interp.c (mi_record_changed): Add missing braces.
@@ -1635,7 +1635,7 @@ mi_cmd_data_read_memory_bytes (char *command, char **argv, int argc)
result = read_memory_robust (current_target.beneath, addr, length);
- cleanups = make_cleanup (free_memory_read_result_vector, result);
+ cleanups = make_cleanup (free_memory_read_result_vector, &result);
if (VEC_length (memory_read_result_s, result) == 0)
error (_("Unable to read memory."));
@@ -1793,15 +1793,15 @@ read_whatever_is_readable (struct target_ops *ops,
void
free_memory_read_result_vector (void *x)
{
- VEC(memory_read_result_s) *v = (VEC(memory_read_result_s) *) x;
+ VEC(memory_read_result_s) **v = (VEC(memory_read_result_s) **) x;
memory_read_result_s *current;
int ix;
- for (ix = 0; VEC_iterate (memory_read_result_s, v, ix, current); ++ix)
+ for (ix = 0; VEC_iterate (memory_read_result_s, *v, ix, current); ++ix)
{
xfree (current->data);
}
- VEC_free (memory_read_result_s, v);
+ VEC_free (memory_read_result_s, *v);
}
VEC(memory_read_result_s) *
@@ -1810,6 +1810,8 @@ read_memory_robust (struct target_ops *ops,
{
VEC(memory_read_result_s) *result = 0;
int unit_size = gdbarch_addressable_memory_unit_size (target_gdbarch ());
+ struct cleanup *cleanup = make_cleanup (free_memory_read_result_vector,
+ &result);
LONGEST xfered_total = 0;
while (xfered_total < len)
@@ -1836,6 +1838,7 @@ read_memory_robust (struct target_ops *ops,
{
LONGEST to_read = min (len - xfered_total, region_len);
gdb_byte *buffer = (gdb_byte *) xmalloc (to_read * unit_size);
+ struct cleanup *inner_cleanup = make_cleanup (xfree, buffer);
LONGEST xfered_partial =
target_read (ops, TARGET_OBJECT_MEMORY, NULL,
@@ -1846,7 +1849,7 @@ read_memory_robust (struct target_ops *ops,
{
/* Got an error reading full chunk. See if maybe we can read
some subrange. */
- xfree (buffer);
+ do_cleanups (inner_cleanup);
read_whatever_is_readable (ops, offset + xfered_total,
offset + xfered_total + to_read,
unit_size, &result);
@@ -1855,6 +1858,8 @@ read_memory_robust (struct target_ops *ops,
else
{
struct memory_read_result r;
+
+ discard_cleanups (inner_cleanup);
r.data = buffer;
r.begin = offset + xfered_total;
r.end = r.begin + xfered_partial;
@@ -1864,6 +1869,8 @@ read_memory_robust (struct target_ops *ops,
QUIT;
}
}
+
+ discard_cleanups (cleanup);
return result;
}