Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[gdb] Fix heap-use-after-free in typename_concat
When running gdb using AddressSanitizer, and loading a cc1plus binary built with profiledbootstrap and -flto, we run into a heap-use-after-free error: ... $ LD_PRELOAD=/usr/lib64/libasan.so.3 ./gdb -batch cc1plus ==26855==ERROR: AddressSanitizer: heap-use-after-free on address \ 0x62100ad8a8b0 at pc 0x7f13803cc9e3 bp 0x7ffe55b0d090 sp 0x7ffe55b0c840 READ of size 47 at 0x62100ad8a8b0 thread T0 #0 0x7f13803cc9e2 (/usr/lib64/libasan.so.3+0x3e9e2) #1 0x5e7a0d in typename_concat gdb/dwarf2read.c:22661 #2 0x5c6437 in partial_die_full_name gdb/dwarf2read.c:8876 #3 0x5c6555 in add_partial_symbol gdb/dwarf2read.c:8893 #4 0x5c6ecf in add_partial_subprogram gdb/dwarf2read.c:9156 #5 0x5c5e90 in scan_partial_symbols gdb/dwarf2read.c:8668 #6 0x5c6c0a in add_partial_namespace gdb/dwarf2read.c:9081 #7 0x5c5f99 in scan_partial_symbols gdb/dwarf2read.c:8702 #8 0x5c48b6 in process_psymtab_comp_unit_reader gdb/dwarf2read.c:8056 #9 0x5c3c1f in init_cutu_and_read_dies gdb/dwarf2read.c:7689 #10 0x5c4c03 in process_psymtab_comp_unit gdb/dwarf2read.c:8140 #11 0x5c58a2 in dwarf2_build_psymtabs_hard gdb/dwarf2read.c:8500 #12 0x5c0d03 in dwarf2_build_psymtabs(objfile*) gdb/dwarf2read.c:6337 #13 0x612359 in read_psyms gdb/elfread.c:1311 #14 0x798a64 in require_partial_symbols(objfile*, int) gdb/psymtab.c:115 #15 0x867d7b in read_symbols gdb/symfile.c:821 #16 0x8683d9 in syms_from_objfile_1 gdb/symfile.c:1000 #17 0x8684a1 in syms_from_objfile gdb/symfile.c:1017 #18 0x868873 in symbol_file_add_with_addrs gdb/symfile.c:1124 #19 0x868b0a in symbol_file_add_from_bfd(bfd*, char const*, \ enum_flags<symfile_add_flag>, std::vector<other_sections, \ std::allocator<other_sections> >*, \ enum_flags<objfile_flag>, objfile*) gdb/symfile.c:1204 #20 0x868b64 in symbol_file_add(char const*, \ enum_flags<symfile_add_flag>, \ std::vector<other_sections, \ std::allocator<other_sections> >*, \ enum_flags<objfile_flag>) gdb/symfile.c:1217 #21 0x868c39 in symbol_file_add_main_1 gdb/symfile.c:1240 #22 0x868bd0 in symbol_file_add_main(char const*, \ enum_flags<symfile_add_flag>) gdb/symfile.c:1231 #23 0x71f1b2 in symbol_file_add_main_adapter gdb/main.c:395 #24 0x71f10e in catch_command_errors gdb/main.c:372 #25 0x71ff5f in captured_main_1 gdb/main.c:1043 #26 0x72045d in captured_main gdb/main.c:1163 #27 0x7204c8 in gdb_main(captured_main_args*) gdb/main.c:1188 #28 0x40fd7d in main gdb/gdb.c:32 #29 0x7f137e300f49 in __libc_start_main (/lib64/libc.so.6+0x20f49) #30 0x40fc89 in _start (/data/gdb_versions/devel/build/gdb/gdb+0x40fc89) 0x62100ad8a8b0 is located 944 bytes inside of 4064-byte region \ [0x62100ad8a500,0x62100ad8b4e0) freed by thread T0 here: #0 0x7f13804523a0 in __interceptor_free (/usr/lib64/libasan.so.3+0xc43a0) #1 0x435e44 in xfree<void> gdb/common/common-utils.h:60 #2 0xa82c25 in call_freefun libiberty/obstack.c:103 #3 0xa83098 in _obstack_free libiberty/obstack.c:280 #4 0x4367da in auto_obstack::~auto_obstack() gdb/gdb_obstack.h:101 #5 0x5ed72c in dwarf2_cu::~dwarf2_cu() gdb/dwarf2read.c:25341 #6 0x5fb5bb in std::default_delete<dwarf2_cu>::operator()(dwarf2_cu*) const \ /usr/include/c++/7/bits/unique_ptr.h:78 #7 0x5f7334 in std::unique_ptr<dwarf2_cu, \ std::default_delete<dwarf2_cu> >::~unique_ptr() \ /usr/include/c++/7/bits/unique_ptr.h:268 #8 0x5c3ce5 in init_cutu_and_read_dies gdb/dwarf2read.c:7624 #9 0x5c4c03 in process_psymtab_comp_unit gdb/dwarf2read.c:8140 #10 0x5c58a2 in dwarf2_build_psymtabs_hard gdb/dwarf2read.c:8500 #11 0x5c0d03 in dwarf2_build_psymtabs(objfile*) gdb/dwarf2read.c:6337 #12 0x612359 in read_psyms gdb/elfread.c:1311 #13 0x798a64 in require_partial_symbols(objfile*, int) gdb/psymtab.c:115 #14 0x867d7b in read_symbols gdb/symfile.c:821 #15 0x8683d9 in syms_from_objfile_1 gdb/symfile.c:1000 #16 0x8684a1 in syms_from_objfile gdb/symfile.c:1017 #17 0x868873 in symbol_file_add_with_addrs gdb/symfile.c:1124 #18 0x868b0a in symbol_file_add_from_bfd(bfd*, char const*, \ enum_flags<symfile_add_flag>, std::vector<other_sections, \ std::allocator<other_sections> >*, \ enum_flags<objfile_flag>, objfile*) gdb/symfile.c:1204 #19 0x868b64 in symbol_file_add(char const*, \ enum_flags<symfile_add_flag>, std::vector<other_sections, \ std::allocator<other_sections> >*, \ enum_flags<objfile_flag>) gdb/symfile.c:1217 #20 0x868c39 in symbol_file_add_main_1 gdb/symfile.c:1240 #21 0x868bd0 in symbol_file_add_main(char const*, \ enum_flags<symfile_add_flag>) gdb/symfile.c:1231 #22 0x71f1b2 in symbol_file_add_main_adapter gdb/main.c:395 #23 0x71f10e in catch_command_errors gdb/main.c:372 #24 0x71ff5f in captured_main_1 gdb/main.c:1043 #25 0x72045d in captured_main gdb/main.c:1163 #26 0x7204c8 in gdb_main(captured_main_args*) gdb/main.c:1188 #27 0x40fd7d in main gdb/gdb.c:32 #28 0x7f137e300f49 in __libc_start_main (/lib64/libc.so.6+0x20f49) previously allocated by thread T0 here: #0 0x7f13804526b8 in __interceptor_malloc (/usr/lib64/libasan.so.3+0xc46b8) #1 0x5114b5 in xmalloc gdb/common/common-utils.c:44 #2 0xa82bd5 in call_chunkfun libiberty/obstack.c:94 #3 0xa82eda in _obstack_newchunk libiberty/obstack.c:206 #4 0x477310 in allocate_on_obstack::operator new(unsigned long, obstack*) \ gdb/gdb_obstack.h:117 #5 0x5dea8c in load_partial_dies gdb/dwarf2read.c:18571 #6 0x5c487f in process_psymtab_comp_unit_reader gdb/dwarf2read.c:8054 #7 0x5c3c1f in init_cutu_and_read_dies gdb/dwarf2read.c:7689 #8 0x5c4c03 in process_psymtab_comp_unit gdb/dwarf2read.c:8140 #9 0x5c58a2 in dwarf2_build_psymtabs_hard gdb/dwarf2read.c:8500 #10 0x5c0d03 in dwarf2_build_psymtabs(objfile*) gdb/dwarf2read.c:6337 #11 0x612359 in read_psyms gdb/elfread.c:1311 #12 0x798a64 in require_partial_symbols(objfile*, int) gdb/psymtab.c:115 #13 0x867d7b in read_symbols gdb/symfile.c:821 #14 0x8683d9 in syms_from_objfile_1 gdb/symfile.c:1000 #15 0x8684a1 in syms_from_objfile gdb/symfile.c:1017 #16 0x868873 in symbol_file_add_with_addrs gdb/symfile.c:1124 #17 0x868b0a in symbol_file_add_from_bfd(bfd*, char const*, \ enum_flags<symfile_add_flag>, \ std::vector<other_sections, \ std::allocator<other_sections> >*, \ enum_flags<objfile_flag>, objfile*) gdb/symfile.c:1204 #18 0x868b64 in symbol_file_add(char const*, enum_flags<symfile_add_flag>, \ std::vector<other_sections, \ std::allocator<other_sections> >*, \ enum_flags<objfile_flag>) gdb/symfile.c:1217 #19 0x868c39 in symbol_file_add_main_1 gdb/symfile.c:1240 #20 0x868bd0 in symbol_file_add_main(char const*, \ enum_flags<symfile_add_flag>) gdb/symfile.c:1231 #21 0x71f1b2 in symbol_file_add_main_adapter gdb/main.c:395 #22 0x71f10e in catch_command_errors gdb/main.c:372 #23 0x71ff5f in captured_main_1 gdb/main.c:1043 #24 0x72045d in captured_main gdb/main.c:1163 #25 0x7204c8 in gdb_main(captured_main_args*) gdb/main.c:1188 #26 0x40fd7d in main gdb/gdb.c:32 #27 0x7f137e300f49 in __libc_start_main (/lib64/libc.so.6+0x20f49) ... This error happens as follows. The function find_partial_die has a cu argument, but returns a pdi which may or may not be from that cu: ... /* Find a partial DIE at OFFSET, which may or may not be in CU, except in the case of .debug_types DIEs which do not reference outside their CU (they do however referencing other types via DW_FORM_ref_sig8). */ static struct partial_die_info * find_partial_die (sect_offset sect_off, int offset_in_dwz, struct dwarf2_cu *cu) ... So the pdi returned by find_partial_die here in partial_die_parent_scope may be from another cu: ... partial_die_parent_scope (struct partial_die_info *pdi, struct dwarf2_cu *cu) { const char *grandparent_scope; struct partial_die_info *parent, *real_pdi; /* We need to look at our parent DIE; if we have a DW_AT_specification, then this means the parent of the specification DIE. */ real_pdi = pdi; while (real_pdi->has_specification) real_pdi = find_partial_die (real_pdi->spec_offset, real_pdi->spec_is_dwz, cu); parent = real_pdi->die_parent; ... in which case both real_pdi and parent will be not from cu, but from another one, say cu2. Subsequently, cu's comp_unit_obstack is used to set parent->scope: ... parent->scope = typename_concat (&cu->comp_unit_obstack, grandparent_scope, parent->name, 0, cu); ... So, we use cu->comp_unit_obstack to assign a value to the scope field of a pdi belonging to cu2, and when cu is deleted, the scope field points to a freed value. Fix this by making find_partial_die return the cu corresponding to the returned pdi, and handling this at the call sites. Tested on x86_64-linux. gdb/ChangeLog: 2019-05-17 Tom de Vries <[email protected]> PR gdb/24094 * dwarf2read.c (struct cu_partial_die_info): New struct. (find_partial_die): Return cu_partial_die_info. (partial_die_parent_scope, guess_partial_die_structure_name) (partial_die_info::fixup): Handle new return type of find_partial_die.
- Loading branch information