mirror of
https://github.com/espressif/binutils-gdb.git
synced 2025-06-17 16:05:56 +08:00

A double-free happens when using a JIT debug info reader that creates more than one block. In the loop that frees blocks in finalize_symtab, at the very end, the gdb_block_iter_tmp variable is set initially, but not changed as the loop advances. If we have two blocks, the first iteration frees the first block, the second iteration frees the second block, but the third iteration tries to free the second block again, as gdb_block_iter_tmp keeps pointing on the second block. Fix it by assigning the gdb_block_iter_tmp variable in the loop. I have improved the jit-reader.exp test to cover this case, by adding a second "JIT-ed" function and creating a block for it. I have renamed the existing function to something I find a bit more descriptive. There are no significant changes to jit-reader.exp itself, only updates following the renaming. The important changes are in jithost.c (generate a new function) and in jitreader.c (create a gdb_block for that function). This was found because of an ASan report: $ ./gdb testsuite/outputs/gdb.base/jit-reader/jit-reader -ex "jit-reader-load /home/simark/build/binutils-gdb/gdb/testsuite/outputs/gdb.base/jit-reader/jitreader.so" -ex r Reading symbols from testsuite/outputs/gdb.base/jit-reader/jit-reader... Starting program: /home/simark/build/binutils-gdb/gdb/testsuite/outputs/gdb.base/jit-reader/jit-reader ================================================================= ==1751048==ERROR: AddressSanitizer: heap-use-after-free on address 0x604000042eb8 at pc 0x5650ef8eec88 bp 0x7ffe52767290 sp 0x7ffe52767280 READ of size 8 at 0x604000042eb8 thread T0 #0 0x5650ef8eec87 in finalize_symtab /home/simark/src/binutils-gdb/gdb/jit.c:768 #1 0x5650ef8eef88 in jit_object_close_impl /home/simark/src/binutils-gdb/gdb/jit.c:797 #2 0x7fbbda986278 in read_debug_info /home/simark/src/binutils-gdb/gdb/testsuite/gdb.base/jitreader.c:71 #3 0x5650ef8ef56b in jit_reader_try_read_symtab /home/simark/src/binutils-gdb/gdb/jit.c:850 #4 0x5650ef8effe3 in jit_register_code /home/simark/src/binutils-gdb/gdb/jit.c:948 #5 0x5650ef8f2c92 in jit_event_handler(gdbarch*) /home/simark/src/binutils-gdb/gdb/jit.c:1396 #6 0x5650ef0d137e in handle_jit_event /home/simark/src/binutils-gdb/gdb/breakpoint.c:5470 [snip] 0x604000042eb8 is located 40 bytes inside of 48-byte region [0x604000042e90,0x604000042ec0) freed by thread T0 here: #0 0x7fbbe57376b0 in __interceptor_free /build/gcc/src/gcc/libsanitizer/asan/asan_malloc_linux.cc:122 #1 0x5650ef8f350b in xfree<gdb_block> /home/simark/src/binutils-gdb/gdb/gdbsupport/common-utils.h:62 #2 0x5650ef8eeca9 in finalize_symtab /home/simark/src/binutils-gdb/gdb/jit.c:769 #3 0x5650ef8eef88 in jit_object_close_impl /home/simark/src/binutils-gdb/gdb/jit.c:797 #4 0x7fbbda986278 in read_debug_info /home/simark/src/binutils-gdb/gdb/testsuite/gdb.base/jitreader.c:71 #5 0x5650ef8ef56b in jit_reader_try_read_symtab /home/simark/src/binutils-gdb/gdb/jit.c:850 #6 0x5650ef8effe3 in jit_register_code /home/simark/src/binutils-gdb/gdb/jit.c:948 #7 0x5650ef8f2c92 in jit_event_handler(gdbarch*) /home/simark/src/binutils-gdb/gdb/jit.c:1396 #8 0x5650ef0d137e in handle_jit_event /home/simark/src/binutils-gdb/gdb/breakpoint.c:5470 [snip] previously allocated by thread T0 here: #0 0x7fbbe5737cd8 in __interceptor_calloc /build/gcc/src/gcc/libsanitizer/asan/asan_malloc_linux.cc:153 #1 0x5650eef662f3 in xcalloc /home/simark/src/binutils-gdb/gdb/alloc.c:100 #2 0x5650ef8f34ea in xcnew<gdb_block> /home/simark/src/binutils-gdb/gdb/gdbsupport/poison.h:122 #3 0x5650ef8ed467 in jit_block_open_impl /home/simark/src/binutils-gdb/gdb/jit.c:557 #4 0x7fbbda98620a in read_debug_info /home/simark/src/binutils-gdb/gdb/testsuite/gdb.base/jitreader.c:60 #5 0x5650ef8ef56b in jit_reader_try_read_symtab /home/simark/src/binutils-gdb/gdb/jit.c:850 #6 0x5650ef8effe3 in jit_register_code /home/simark/src/binutils-gdb/gdb/jit.c:948 #7 0x5650ef8f2c92 in jit_event_handler(gdbarch*) /home/simark/src/binutils-gdb/gdb/jit.c:1396 #8 0x5650ef0d137e in handle_jit_event /home/simark/src/binutils-gdb/gdb/breakpoint.c:5470 [snip] gdb/ChangeLog: * jit.c (finalize_symtab): Set gdb_block_iter_tmp in loop. gdb/testsuite/ChangeLog: * gdb.base/jit-reader.exp (jit_reader_test): Rename jit_function_00 to jit_function_stack_mangle. * gdb.base/jithost.c (jit_function_t): Rename to... (jit_function_stack_mangle_t): ... this. (jit_function_add_t): New typedef. (jit_function_00_code): Rename to... (jit_function_stack_mangle_code): ... this, make static. (jit_function_add_code): New. (main): Generate "add" function and call it. Adjust to changes in jithost_abi. * gdb.base/jithost.h (struct jithost_abi_bounds): New. (struct jithost_abi) <begin, end>: Remove fields. <object, function_stack_mangle, function_add>: New fields. * gdb.base/jitreader.c (struct reader_state) <code_begin, code_end>: Remove fields. <func_stack_mangle>: New field. (read_debug_info): Adjust to renaming, create block for "add" function. (read_sp, unwind_frame, get_frame_id): Adjust to other changes.
212 lines
5.9 KiB
C
212 lines
5.9 KiB
C
/* Copyright (C) 2009-2019 Free Software Foundation, Inc.
|
|
|
|
This file is part of GDB.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
|
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include JIT_READER_H /* Please see jit-reader.exp for an explanation. */
|
|
#include "jithost.h"
|
|
|
|
GDB_DECLARE_GPL_COMPATIBLE_READER;
|
|
|
|
enum register_mapping
|
|
{
|
|
AMD64_RA = 16,
|
|
AMD64_RBP = 6,
|
|
AMD64_RSP = 7,
|
|
};
|
|
|
|
struct reader_state
|
|
{
|
|
struct {
|
|
uintptr_t begin;
|
|
uintptr_t end;
|
|
} func_stack_mangle;
|
|
};
|
|
|
|
static enum gdb_status
|
|
read_debug_info (struct gdb_reader_funcs *self,
|
|
struct gdb_symbol_callbacks *cbs,
|
|
void *memory, long memory_sz)
|
|
{
|
|
struct jithost_abi *symfile = memory;
|
|
struct gdb_object *object = cbs->object_open (cbs);
|
|
struct gdb_symtab *symtab = cbs->symtab_open (cbs, object, "");
|
|
|
|
struct reader_state *state = (struct reader_state *) self->priv_data;
|
|
|
|
/* Record the stack mangle function's range, for the unwinder. */
|
|
state->func_stack_mangle.begin
|
|
= (uintptr_t) symfile->function_stack_mangle.begin;
|
|
state->func_stack_mangle.end
|
|
= (uintptr_t) symfile->function_stack_mangle.end;
|
|
|
|
cbs->block_open (cbs, symtab, NULL,
|
|
(GDB_CORE_ADDR) symfile->function_stack_mangle.begin,
|
|
(GDB_CORE_ADDR) symfile->function_stack_mangle.end,
|
|
"jit_function_stack_mangle");
|
|
|
|
cbs->block_open (cbs, symtab, NULL,
|
|
(GDB_CORE_ADDR) symfile->function_add.begin,
|
|
(GDB_CORE_ADDR) symfile->function_add.end,
|
|
"jit_function_add");
|
|
|
|
cbs->symtab_close (cbs, symtab);
|
|
cbs->object_close (cbs, object);
|
|
return GDB_SUCCESS;
|
|
}
|
|
|
|
static void
|
|
free_reg_value (struct gdb_reg_value *value)
|
|
{
|
|
free (value);
|
|
}
|
|
|
|
static void
|
|
write_register (struct gdb_unwind_callbacks *callbacks, int dw_reg,
|
|
uintptr_t value)
|
|
{
|
|
const int size = sizeof (uintptr_t);
|
|
struct gdb_reg_value *reg_val =
|
|
malloc (sizeof (struct gdb_reg_value) + size - 1);
|
|
reg_val->defined = 1;
|
|
reg_val->free = free_reg_value;
|
|
|
|
memcpy (reg_val->value, &value, size);
|
|
callbacks->reg_set (callbacks, dw_reg, reg_val);
|
|
}
|
|
|
|
static int
|
|
read_register (struct gdb_unwind_callbacks *callbacks, int dw_reg,
|
|
uintptr_t *value)
|
|
{
|
|
const int size = sizeof (uintptr_t);
|
|
struct gdb_reg_value *reg_val = callbacks->reg_get (callbacks, dw_reg);
|
|
if (reg_val->size != size || !reg_val->defined)
|
|
{
|
|
reg_val->free (reg_val);
|
|
return 0;
|
|
}
|
|
memcpy (value, reg_val->value, size);
|
|
reg_val->free (reg_val);
|
|
return 1;
|
|
}
|
|
|
|
/* Read the stack pointer into *VALUE. IP is the address the inferior
|
|
is currently stopped at. Takes care of demangling the stack
|
|
pointer if necessary. */
|
|
|
|
static int
|
|
read_sp (struct gdb_reader_funcs *self, struct gdb_unwind_callbacks *cbs,
|
|
uintptr_t ip, uintptr_t *value)
|
|
{
|
|
struct reader_state *state = (struct reader_state *) self->priv_data;
|
|
uintptr_t sp;
|
|
|
|
if (!read_register (cbs, AMD64_RSP, &sp))
|
|
return GDB_FAIL;
|
|
|
|
/* If stopped at the instruction after the "xor $-1, %rsp", demangle
|
|
the stack pointer back. */
|
|
if (ip == state->func_stack_mangle.begin + 5)
|
|
sp ^= (uintptr_t) -1;
|
|
|
|
*value = sp;
|
|
return GDB_SUCCESS;
|
|
}
|
|
|
|
static enum gdb_status
|
|
unwind_frame (struct gdb_reader_funcs *self, struct gdb_unwind_callbacks *cbs)
|
|
{
|
|
const int word_size = sizeof (uintptr_t);
|
|
uintptr_t prev_sp, this_sp;
|
|
uintptr_t prev_ip, this_ip;
|
|
uintptr_t prev_bp, this_bp;
|
|
struct reader_state *state = (struct reader_state *) self->priv_data;
|
|
|
|
if (!read_register (cbs, AMD64_RA, &this_ip))
|
|
return GDB_FAIL;
|
|
|
|
if (this_ip >= state->func_stack_mangle.end
|
|
|| this_ip < state->func_stack_mangle.begin)
|
|
return GDB_FAIL;
|
|
|
|
/* Unwind RBP in order to make the unwinder that tries to unwind
|
|
from the just-unwound frame happy. */
|
|
if (!read_register (cbs, AMD64_RBP, &this_bp))
|
|
return GDB_FAIL;
|
|
/* RBP is unmodified. */
|
|
prev_bp = this_bp;
|
|
|
|
/* Fetch the demangled stack pointer. */
|
|
if (!read_sp (self, cbs, this_ip, &this_sp))
|
|
return GDB_FAIL;
|
|
|
|
/* The return address is saved on the stack. */
|
|
if (cbs->target_read (this_sp, &prev_ip, word_size) == GDB_FAIL)
|
|
return GDB_FAIL;
|
|
prev_sp = this_sp + word_size;
|
|
|
|
write_register (cbs, AMD64_RA, prev_ip);
|
|
write_register (cbs, AMD64_RSP, prev_sp);
|
|
write_register (cbs, AMD64_RBP, prev_bp);
|
|
return GDB_SUCCESS;
|
|
}
|
|
|
|
static struct gdb_frame_id
|
|
get_frame_id (struct gdb_reader_funcs *self, struct gdb_unwind_callbacks *cbs)
|
|
{
|
|
struct reader_state *state = (struct reader_state *) self->priv_data;
|
|
struct gdb_frame_id frame_id;
|
|
uintptr_t ip;
|
|
uintptr_t sp;
|
|
|
|
read_register (cbs, AMD64_RA, &ip);
|
|
read_sp (self, cbs, ip, &sp);
|
|
|
|
frame_id.code_address = (GDB_CORE_ADDR) state->func_stack_mangle.begin;
|
|
frame_id.stack_address = (GDB_CORE_ADDR) sp;
|
|
|
|
return frame_id;
|
|
}
|
|
|
|
static void
|
|
destroy_reader (struct gdb_reader_funcs *self)
|
|
{
|
|
free (self->priv_data);
|
|
free (self);
|
|
}
|
|
|
|
struct gdb_reader_funcs *
|
|
gdb_init_reader (void)
|
|
{
|
|
struct reader_state *state = calloc (1, sizeof (struct reader_state));
|
|
struct gdb_reader_funcs *reader_funcs =
|
|
malloc (sizeof (struct gdb_reader_funcs));
|
|
|
|
reader_funcs->reader_version = GDB_READER_INTERFACE_VERSION;
|
|
reader_funcs->priv_data = state;
|
|
reader_funcs->read = read_debug_info;
|
|
reader_funcs->unwind = unwind_frame;
|
|
reader_funcs->get_frame_id = get_frame_id;
|
|
reader_funcs->destroy = destroy_reader;
|
|
|
|
return reader_funcs;
|
|
}
|