mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-12-21 04:42:53 +08:00
068ef30e9e
Commit ab42892fb7
("Fix vertical scrolling of TUI source window")
introduced a use-after-free in source_cache::get_source_lines.
At the beginning of the method, we get the fullname of the symtab:
const char *fullname = symtab_to_fullname (s);
fullname points to the string owned by the symtab (s.fullname). When we
later do
scoped_fd desc = open_source_file (s);
s.fullname gets reallocated (even though the string contents may not
change). The fullname local variable now points to freed memory.
To avoid it, refresh the value of fullname after calling
open_source_file.
Here is the ASan report:
$ ./gdb -nx --data-directory=data-directory ./a.out
(gdb) start
Temporary breakpoint 1 at 0x1130: file test.cpp, line 12.
Starting program: /home/simark/build/binutils-gdb/gdb/a.out
Temporary breakpoint 1, main () at test.cpp:12
=================================================================
==26068==ERROR: AddressSanitizer: heap-use-after-free on address 0x6210003d4100 at pc 0x7fed89a34681 bp 0x7ffd8d185d80 sp 0x7ffd8d185528
READ of size 2 at 0x6210003d4100 thread T0
#0 0x7fed89a34680 in __interceptor_strlen /build/gcc/src/gcc/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:301
#1 0x55b6edf6c2f7 in std::char_traits<char>::length(char const*) /usr/include/c++/8.2.1/bits/char_traits.h:320
#2 0x55b6edf6c9b2 in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&) /usr/include/c++/8.2.1/bits/basic_string.h:516
#3 0x55b6ef09121b in source_cache::get_source_lines(symtab*, int, int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*) /home/simark/src/binutils-gdb/gdb/source-cache.c:214
#4 0x55b6ef0a15cb in print_source_lines_base /home/simark/src/binutils-gdb/gdb/source.c:1340
#5 0x55b6ef0a2045 in print_source_lines(symtab*, int, int, enum_flags<print_source_lines_flag>) /home/simark/src/binutils-gdb/gdb/source.c:1415
#6 0x55b6ef112c87 in print_frame_info(frame_info*, int, print_what, int, int) /home/simark/src/binutils-gdb/gdb/stack.c:914
#7 0x55b6ef10e90d in print_stack_frame(frame_info*, int, print_what, int) /home/simark/src/binutils-gdb/gdb/stack.c:180
#8 0x55b6ee9592f8 in print_stop_location /home/simark/src/binutils-gdb/gdb/infrun.c:7853
#9 0x55b6ee95948f in print_stop_event(ui_out*) /home/simark/src/binutils-gdb/gdb/infrun.c:7870
#10 0x55b6ef34b962 in tui_on_normal_stop /home/simark/src/binutils-gdb/gdb/tui/tui-interp.c:98
#11 0x55b6ee01a14d in std::_Function_handler<void (bpstats*, int), void (*)(bpstats*, int)>::_M_invoke(std::_Any_data const&, bpstats*&&, int&&) /usr/include/c++/8.2.1/bits/std_function.h:297
#12 0x55b6ee965415 in std::function<void (bpstats*, int)>::operator()(bpstats*, int) const /usr/include/c++/8.2.1/bits/std_function.h:687
#13 0x55b6ee962f1b in gdb::observers::observable<bpstats*, int>::notify(bpstats*, int) const /home/simark/src/binutils-gdb/gdb/common/observable.h:106
#14 0x55b6ee95a6e7 in normal_stop() /home/simark/src/binutils-gdb/gdb/infrun.c:8142
#15 0x55b6ee93f236 in fetch_inferior_event(void*) /home/simark/src/binutils-gdb/gdb/infrun.c:3782
#16 0x55b6ee8f2641 in inferior_event_handler(inferior_event_type, void*) /home/simark/src/binutils-gdb/gdb/inf-loop.c:43
#17 0x55b6eea2a1f0 in handle_target_event /home/simark/src/binutils-gdb/gdb/linux-nat.c:4358
#18 0x55b6ee7045f1 in handle_file_event /home/simark/src/binutils-gdb/gdb/event-loop.c:733
#19 0x55b6ee704e89 in gdb_wait_for_event /home/simark/src/binutils-gdb/gdb/event-loop.c:859
#20 0x55b6ee7027b5 in gdb_do_one_event() /home/simark/src/binutils-gdb/gdb/event-loop.c:322
#21 0x55b6ee702907 in start_event_loop() /home/simark/src/binutils-gdb/gdb/event-loop.c:371
#22 0x55b6eeadfc16 in captured_command_loop /home/simark/src/binutils-gdb/gdb/main.c:331
#23 0x55b6eeae2ef9 in captured_main /home/simark/src/binutils-gdb/gdb/main.c:1174
#24 0x55b6eeae30c2 in gdb_main(captured_main_args*) /home/simark/src/binutils-gdb/gdb/main.c:1190
#25 0x55b6edf4fa89 in main /home/simark/src/binutils-gdb/gdb/gdb.c:32
#26 0x7fed88ad8222 in __libc_start_main (/usr/lib/libc.so.6+0x24222)
#27 0x55b6edf4f86d in _start (/home/simark/build/binutils-gdb/gdb/gdb+0x197186d)
0x6210003d4100 is located 0 bytes inside of 4096-byte region [0x6210003d4100,0x6210003d5100)
freed by thread T0 here:
#0 0x7fed89a8ac19 in __interceptor_free /build/gcc/src/gcc/libsanitizer/asan/asan_malloc_linux.cc:66
#1 0x55b6edfe12df in xfree<char> /home/simark/src/binutils-gdb/gdb/common/common-utils.h:60
#2 0x55b6edfea675 in gdb::xfree_deleter<char>::operator()(char*) const /home/simark/src/binutils-gdb/gdb/common/gdb_unique_ptr.h:34
#3 0x55b6edfe532c in std::unique_ptr<char, gdb::xfree_deleter<char> >::reset(char*) /usr/include/c++/8.2.1/bits/unique_ptr.h:382
#4 0x55b6edfe7329 in std::unique_ptr<char, gdb::xfree_deleter<char> >::operator=(std::unique_ptr<char, gdb::xfree_deleter<char> >&&) /usr/include/c++/8.2.1/bits/unique_ptr.h:289
#5 0x55b6ef09ec2b in find_and_open_source(char const*, char const*, std::unique_ptr<char, gdb::xfree_deleter<char> >*) /home/simark/src/binutils-gdb/gdb/source.c:990
#6 0x55b6ef09f56a in open_source_file(symtab*) /home/simark/src/binutils-gdb/gdb/source.c:1069
#7 0x55b6ef090f78 in source_cache::get_source_lines(symtab*, int, int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*) /home/simark/src/binutils-gdb/gdb/source-cache.c:205
#8 0x55b6ef0a15cb in print_source_lines_base /home/simark/src/binutils-gdb/gdb/source.c:1340
#9 0x55b6ef0a2045 in print_source_lines(symtab*, int, int, enum_flags<print_source_lines_flag>) /home/simark/src/binutils-gdb/gdb/source.c:1415
#10 0x55b6ef112c87 in print_frame_info(frame_info*, int, print_what, int, int) /home/simark/src/binutils-gdb/gdb/stack.c:914
#11 0x55b6ef10e90d in print_stack_frame(frame_info*, int, print_what, int) /home/simark/src/binutils-gdb/gdb/stack.c:180
#12 0x55b6ee9592f8 in print_stop_location /home/simark/src/binutils-gdb/gdb/infrun.c:7853
#13 0x55b6ee95948f in print_stop_event(ui_out*) /home/simark/src/binutils-gdb/gdb/infrun.c:7870
#14 0x55b6ef34b962 in tui_on_normal_stop /home/simark/src/binutils-gdb/gdb/tui/tui-interp.c:98
#15 0x55b6ee01a14d in std::_Function_handler<void (bpstats*, int), void (*)(bpstats*, int)>::_M_invoke(std::_Any_data const&, bpstats*&&, int&&) /usr/include/c++/8.2.1/bits/std_function.h:297
#16 0x55b6ee965415 in std::function<void (bpstats*, int)>::operator()(bpstats*, int) const /usr/include/c++/8.2.1/bits/std_function.h:687
#17 0x55b6ee962f1b in gdb::observers::observable<bpstats*, int>::notify(bpstats*, int) const /home/simark/src/binutils-gdb/gdb/common/observable.h:106
#18 0x55b6ee95a6e7 in normal_stop() /home/simark/src/binutils-gdb/gdb/infrun.c:8142
#19 0x55b6ee93f236 in fetch_inferior_event(void*) /home/simark/src/binutils-gdb/gdb/infrun.c:3782
#20 0x55b6ee8f2641 in inferior_event_handler(inferior_event_type, void*) /home/simark/src/binutils-gdb/gdb/inf-loop.c:43
#21 0x55b6eea2a1f0 in handle_target_event /home/simark/src/binutils-gdb/gdb/linux-nat.c:4358
#22 0x55b6ee7045f1 in handle_file_event /home/simark/src/binutils-gdb/gdb/event-loop.c:733
#23 0x55b6ee704e89 in gdb_wait_for_event /home/simark/src/binutils-gdb/gdb/event-loop.c:859
#24 0x55b6ee7027b5 in gdb_do_one_event() /home/simark/src/binutils-gdb/gdb/event-loop.c:322
#25 0x55b6ee702907 in start_event_loop() /home/simark/src/binutils-gdb/gdb/event-loop.c:371
#26 0x55b6eeadfc16 in captured_command_loop /home/simark/src/binutils-gdb/gdb/main.c:331
#27 0x55b6eeae2ef9 in captured_main /home/simark/src/binutils-gdb/gdb/main.c:1174
#28 0x55b6eeae30c2 in gdb_main(captured_main_args*) /home/simark/src/binutils-gdb/gdb/main.c:1190
#29 0x55b6edf4fa89 in main /home/simark/src/binutils-gdb/gdb/gdb.c:32
previously allocated by thread T0 here:
#0 0x7fed89a8b019 in __interceptor_malloc /build/gcc/src/gcc/libsanitizer/asan/asan_malloc_linux.cc:86
#1 0x7fed88af983f in realpath@@GLIBC_2.3 (/usr/lib/libc.so.6+0x4583f)
#2 0x7fed899dbbbc in __interceptor_canonicalize_file_name /build/gcc/src/gcc/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:3297
#3 0x55b6ee376a03 in gdb_realpath(char const*) /home/simark/src/binutils-gdb/gdb/common/pathstuff.c:72
#4 0x55b6ef09ec12 in find_and_open_source(char const*, char const*, std::unique_ptr<char, gdb::xfree_deleter<char> >*) /home/simark/src/binutils-gdb/gdb/source.c:990
#5 0x55b6ef09f56a in open_source_file(symtab*) /home/simark/src/binutils-gdb/gdb/source.c:1069
#6 0x55b6ef0a0f12 in print_source_lines_base /home/simark/src/binutils-gdb/gdb/source.c:1270
#7 0x55b6ef0a2045 in print_source_lines(symtab*, int, int, enum_flags<print_source_lines_flag>) /home/simark/src/binutils-gdb/gdb/source.c:1415
#8 0x55b6ef112c87 in print_frame_info(frame_info*, int, print_what, int, int) /home/simark/src/binutils-gdb/gdb/stack.c:914
#9 0x55b6ef10e90d in print_stack_frame(frame_info*, int, print_what, int) /home/simark/src/binutils-gdb/gdb/stack.c:180
#10 0x55b6ee9592f8 in print_stop_location /home/simark/src/binutils-gdb/gdb/infrun.c:7853
#11 0x55b6ee95948f in print_stop_event(ui_out*) /home/simark/src/binutils-gdb/gdb/infrun.c:7870
#12 0x55b6ef34b962 in tui_on_normal_stop /home/simark/src/binutils-gdb/gdb/tui/tui-interp.c:98
#13 0x55b6ee01a14d in std::_Function_handler<void (bpstats*, int), void (*)(bpstats*, int)>::_M_invoke(std::_Any_data const&, bpstats*&&, int&&) /usr/include/c++/8.2.1/bits/std_function.h:297
#14 0x55b6ee965415 in std::function<void (bpstats*, int)>::operator()(bpstats*, int) const /usr/include/c++/8.2.1/bits/std_function.h:687
#15 0x55b6ee962f1b in gdb::observers::observable<bpstats*, int>::notify(bpstats*, int) const /home/simark/src/binutils-gdb/gdb/common/observable.h:106
#16 0x55b6ee95a6e7 in normal_stop() /home/simark/src/binutils-gdb/gdb/infrun.c:8142
#17 0x55b6ee93f236 in fetch_inferior_event(void*) /home/simark/src/binutils-gdb/gdb/infrun.c:3782
#18 0x55b6ee8f2641 in inferior_event_handler(inferior_event_type, void*) /home/simark/src/binutils-gdb/gdb/inf-loop.c:43
#19 0x55b6eea2a1f0 in handle_target_event /home/simark/src/binutils-gdb/gdb/linux-nat.c:4358
#20 0x55b6ee7045f1 in handle_file_event /home/simark/src/binutils-gdb/gdb/event-loop.c:733
#21 0x55b6ee704e89 in gdb_wait_for_event /home/simark/src/binutils-gdb/gdb/event-loop.c:859
#22 0x55b6ee7027b5 in gdb_do_one_event() /home/simark/src/binutils-gdb/gdb/event-loop.c:322
#23 0x55b6ee702907 in start_event_loop() /home/simark/src/binutils-gdb/gdb/event-loop.c:371
#24 0x55b6eeadfc16 in captured_command_loop /home/simark/src/binutils-gdb/gdb/main.c:331
#25 0x55b6eeae2ef9 in captured_main /home/simark/src/binutils-gdb/gdb/main.c:1174
#26 0x55b6eeae30c2 in gdb_main(captured_main_args*) /home/simark/src/binutils-gdb/gdb/main.c:1190
#27 0x55b6edf4fa89 in main /home/simark/src/binutils-gdb/gdb/gdb.c:32
#28 0x7fed88ad8222 in __libc_start_main (/usr/lib/libc.so.6+0x24222)
gdb/ChangeLog:
* source-cache.c (source_cache::get_source_lines): Re-read
fullname after calling open_source_file.
238 lines
5.9 KiB
C
238 lines
5.9 KiB
C
/* Cache of styled source file text
|
|
Copyright (C) 2018-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 "defs.h"
|
|
#include "source-cache.h"
|
|
#include "common/scoped_fd.h"
|
|
#include "source.h"
|
|
#include "cli/cli-style.h"
|
|
|
|
#ifdef HAVE_SOURCE_HIGHLIGHT
|
|
/* If Gnulib redirects 'open' and 'close' to its replacements
|
|
'rpl_open' and 'rpl_close' via cpp macros, including <fstream>
|
|
below with those macros in effect will cause unresolved externals
|
|
when GDB is linked. Happens, e.g., in the MinGW build. */
|
|
#undef open
|
|
#undef close
|
|
#include <fstream>
|
|
#include <sstream>
|
|
#include <srchilite/sourcehighlight.h>
|
|
#include <srchilite/langmap.h>
|
|
#endif
|
|
|
|
/* The number of source files we'll cache. */
|
|
|
|
#define MAX_ENTRIES 5
|
|
|
|
/* See source-cache.h. */
|
|
|
|
source_cache g_source_cache;
|
|
|
|
/* See source-cache.h. */
|
|
|
|
bool
|
|
source_cache::get_plain_source_lines (struct symtab *s, int first_line,
|
|
int last_line, std::string *lines)
|
|
{
|
|
scoped_fd desc (open_source_file (s));
|
|
if (desc.get () < 0)
|
|
return false;
|
|
|
|
if (s->line_charpos == 0)
|
|
find_source_lines (s, desc.get ());
|
|
|
|
if (first_line < 1 || first_line > s->nlines || last_line < 1)
|
|
return false;
|
|
|
|
if (lseek (desc.get (), s->line_charpos[first_line - 1], SEEK_SET) < 0)
|
|
perror_with_name (symtab_to_filename_for_display (s));
|
|
|
|
int last_charpos;
|
|
if (last_line >= s->nlines)
|
|
{
|
|
struct stat st;
|
|
|
|
if (fstat (desc.get (), &st) < 0)
|
|
perror_with_name (symtab_to_filename_for_display (s));
|
|
/* We could cache this in line_charpos... */
|
|
last_charpos = st.st_size;
|
|
}
|
|
else
|
|
last_charpos = s->line_charpos[last_line];
|
|
|
|
lines->resize (last_charpos - s->line_charpos[first_line - 1]);
|
|
if (myread (desc.get (), &(*lines)[0], lines->size ()) < 0)
|
|
perror_with_name (symtab_to_filename_for_display (s));
|
|
|
|
return true;
|
|
}
|
|
|
|
/* See source-cache.h. */
|
|
|
|
std::string
|
|
source_cache::extract_lines (const struct source_text &text, int first_line,
|
|
int last_line)
|
|
{
|
|
int lineno = 1;
|
|
std::string::size_type pos = 0;
|
|
std::string::size_type first_pos = std::string::npos;
|
|
|
|
while (pos != std::string::npos && lineno <= last_line)
|
|
{
|
|
std::string::size_type new_pos = text.contents.find ('\n', pos);
|
|
|
|
if (lineno == first_line)
|
|
first_pos = pos;
|
|
|
|
pos = new_pos;
|
|
if (lineno == last_line || pos == std::string::npos)
|
|
{
|
|
if (first_pos == std::string::npos)
|
|
return {};
|
|
if (pos == std::string::npos)
|
|
pos = text.contents.size ();
|
|
return text.contents.substr (first_pos, pos - first_pos);
|
|
}
|
|
++lineno;
|
|
++pos;
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
#ifdef HAVE_SOURCE_HIGHLIGHT
|
|
|
|
/* Return the Source Highlight language name, given a gdb language
|
|
LANG. Returns NULL if the language is not known. */
|
|
|
|
static const char *
|
|
get_language_name (enum language lang)
|
|
{
|
|
switch (lang)
|
|
{
|
|
case language_c:
|
|
case language_objc:
|
|
return "c.lang";
|
|
|
|
case language_cplus:
|
|
return "cpp.lang";
|
|
|
|
case language_d:
|
|
return "d.lang";
|
|
|
|
case language_go:
|
|
return "go.lang";
|
|
|
|
case language_fortran:
|
|
return "fortran.lang";
|
|
|
|
case language_m2:
|
|
/* Not handled by Source Highlight. */
|
|
break;
|
|
|
|
case language_asm:
|
|
return "asm.lang";
|
|
|
|
case language_pascal:
|
|
return "pascal.lang";
|
|
|
|
case language_opencl:
|
|
/* Not handled by Source Highlight. */
|
|
break;
|
|
|
|
case language_rust:
|
|
/* Not handled by Source Highlight. */
|
|
break;
|
|
|
|
case language_ada:
|
|
return "ada.lang";
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
#endif /* HAVE_SOURCE_HIGHLIGHT */
|
|
|
|
/* See source-cache.h. */
|
|
|
|
bool
|
|
source_cache::get_source_lines (struct symtab *s, int first_line,
|
|
int last_line, std::string *lines)
|
|
{
|
|
if (first_line < 1 || last_line < 1 || first_line > last_line)
|
|
return false;
|
|
|
|
#ifdef HAVE_SOURCE_HIGHLIGHT
|
|
if (source_styling && can_emit_style_escape (gdb_stdout))
|
|
{
|
|
const char *fullname = symtab_to_fullname (s);
|
|
|
|
for (const auto &item : m_source_map)
|
|
{
|
|
if (item.fullname == fullname)
|
|
{
|
|
*lines = extract_lines (item, first_line, last_line);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
const char *lang_name = get_language_name (SYMTAB_LANGUAGE (s));
|
|
if (lang_name != nullptr)
|
|
{
|
|
std::ifstream input (fullname);
|
|
if (input.is_open ())
|
|
{
|
|
if (s->line_charpos == 0)
|
|
{
|
|
scoped_fd desc = open_source_file (s);
|
|
if (desc.get () < 0)
|
|
return false;
|
|
find_source_lines (s, desc.get ());
|
|
|
|
/* FULLNAME points to a value owned by the symtab
|
|
(symtab::fullname). Calling open_source_file reallocates
|
|
that value, so we must refresh FULLNAME to avoid a
|
|
use-after-free. */
|
|
fullname = symtab_to_fullname (s);
|
|
}
|
|
srchilite::SourceHighlight highlighter ("esc.outlang");
|
|
highlighter.setStyleFile("esc.style");
|
|
|
|
std::ostringstream output;
|
|
highlighter.highlight (input, output, lang_name, fullname);
|
|
|
|
source_text result = { fullname, output.str () };
|
|
m_source_map.push_back (std::move (result));
|
|
|
|
if (m_source_map.size () > MAX_ENTRIES)
|
|
m_source_map.erase (m_source_map.begin ());
|
|
|
|
*lines = extract_lines (m_source_map.back (), first_line,
|
|
last_line);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
#endif /* HAVE_SOURCE_HIGHLIGHT */
|
|
|
|
return get_plain_source_lines (s, first_line, last_line, lines);
|
|
}
|