commit c75bd3a23915c3122070a95e1974e323543ffbe4 Author: Jan Kratochvil Date: Sun Sep 7 14:09:59 2014 +0200 Fix crash on Python frame filters with unreadable arg https://bugzilla.redhat.com/show_bug.cgi?id=1126177 ERROR: AddressSanitizer: SEGV on unknown address 0x000000000050 (pc 0x000000992bef sp 0x7ffff9039530 bp 0x7ffff9039540 T0) #0 0x992bee in value_type .../gdb/value.c:925 #1 0x87c951 in py_print_single_arg python/py-framefilter.c:445 #2 0x87cfae in enumerate_args python/py-framefilter.c:596 #3 0x87e0b0 in py_print_args python/py-framefilter.c:968 It crashes because frame_arg::val is documented it may contain NULL (frame_arg::error is then non-NULL) but the code does not handle it. Another bug is that py_print_single_arg() calls goto out of its TRY_CATCH which messes up GDB cleanup chain crashing GDB later. It is probably 7.7 regression (I have not verified it) due to the introduction of Python frame filters. gdb/ChangeLog PR python/17355 * python/py-framefilter.c (py_print_single_arg): Handle NULL FA->VAL. Fix goto out of TRY_CATCH. gdb/testsuite/ChangeLog PR python/17355 * gdb.python/amd64-py-framefilter-invalidarg.S: New file. * gdb.python/py-framefilter-invalidarg-gdb.py.in: New file. * gdb.python/py-framefilter-invalidarg.exp: New file. * gdb.python/py-framefilter-invalidarg.py: New file. ### a/gdb/ChangeLog ### b/gdb/ChangeLog ## -1,3 +1,9 @@ +2014-09-07 Jan Kratochvil + + PR python/17355 + * python/py-framefilter.c (py_print_single_arg): Handle NULL FA->VAL. + Fix goto out of TRY_CATCH. + 2014-09-06 Doug Evans Tom Tromey ### a/gdb/testsuite/ChangeLog ### b/gdb/testsuite/ChangeLog ## -1,3 +1,11 @@ +2014-09-07 Jan Kratochvil + + PR python/17355 + * gdb.python/amd64-py-framefilter-invalidarg.S: New file. + * gdb.python/py-framefilter-invalidarg-gdb.py.in: New file. + * gdb.python/py-framefilter-invalidarg.exp: New file. + * gdb.python/py-framefilter-invalidarg.py: New file. + 2014-09-06 Doug Evans PR 15276 Index: gdb-7.6.1/gdb/python/py-framefilter.c =================================================================== --- gdb-7.6.1.orig/gdb/python/py-framefilter.c 2015-11-27 18:06:21.901228682 +0100 +++ gdb-7.6.1/gdb/python/py-framefilter.c 2015-11-27 18:06:22.445231731 +0100 @@ -365,9 +365,12 @@ { struct value *val; volatile struct gdb_exception except; + enum py_bt_status retval = PY_BT_OK; if (fa != NULL) { + if (fa->val == NULL && fa->error == NULL) + return PY_BT_OK; language = language_def (SYMBOL_LANGUAGE (fa->sym)); val = fa->val; } @@ -433,16 +436,18 @@ /* For MI print the type, but only for simple values. This seems weird, but this is how MI choose to format the various output types. */ - if (args_type == MI_PRINT_SIMPLE_VALUES) + if (args_type == MI_PRINT_SIMPLE_VALUES && val != NULL) { if (py_print_type (out, val) == PY_BT_ERROR) { + retval = PY_BT_ERROR; do_cleanups (cleanups); - goto error; + continue; } } - annotate_arg_value (value_type (val)); + if (val != NULL) + annotate_arg_value (value_type (val)); /* If the output is to the CLI, and the user option "set print frame-arguments" is set to none, just output "...". */ @@ -454,27 +459,25 @@ for the case of MI_PRINT_NO_VALUES. */ if (args_type != NO_VALUES) { - if (py_print_value (out, val, opts, 0, args_type, language) - == PY_BT_ERROR) + if (val == NULL) { - do_cleanups (cleanups); - goto error; + gdb_assert (fa != NULL && fa->error != NULL); + ui_out_field_fmt (out, "value", + _(""), + fa->error); } + else if (py_print_value (out, val, opts, 0, args_type, language) + == PY_BT_ERROR) + retval = PY_BT_ERROR; } } do_cleanups (cleanups); } if (except.reason < 0) - { - gdbpy_convert_exception (except); - goto error; - } - - return PY_BT_OK; + gdbpy_convert_exception (except); - error: - return PY_BT_ERROR; + return retval; } /* Helper function to loop over frame arguments provided by the Index: gdb-7.6.1/gdb/testsuite/gdb.python/amd64-py-framefilter-invalidarg.S =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ gdb-7.6.1/gdb/testsuite/gdb.python/amd64-py-framefilter-invalidarg.S 2015-11-27 18:06:22.446231736 +0100 @@ -0,0 +1,261 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2014 Free Software Foundation, Inc. + + 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 . */ + +/* This file is compiled from a single line + int main (int argc, char **argv) { return 0; } + using -g -dA -S -O2 and patched as #if-ed below. */ + + .file "py-framefilter-invalidarg.c" + .text +.Ltext0: + .globl main + .type main, @function +main: +.LFB0: + .file 1 "py-framefilter-invalidarg.c" + # py-framefilter-invalidarg.c:1 + .loc 1 1 0 + .cfi_startproc +# BLOCK 2 seq:0 +# PRED: ENTRY (FALLTHRU) + pushq %rbp + .cfi_def_cfa_offset 16 + .cfi_offset 6, -16 + movq %rsp, %rbp + .cfi_def_cfa_register 6 + movl %edi, -4(%rbp) + movq %rsi, -16(%rbp) + # py-framefilter-invalidarg.c:2 + .loc 1 2 0 + movl $0, %eax + # py-framefilter-invalidarg.c:3 + .loc 1 3 0 + popq %rbp + .cfi_def_cfa 7, 8 +# SUCC: EXIT [100.0%] + ret + .cfi_endproc +.LFE0: + .size main, .-main +.Letext0: + .section .debug_info,"",@progbits +.Ldebug_info0: + .long .Le - .Ls # Length of Compilation Unit Info +.Ls: + .value 0x4 # DWARF version number + .long .Ldebug_abbrev0 # Offset Into Abbrev. Section + .byte 0x8 # Pointer Size (in bytes) + .uleb128 0x1 # (DIE (0xb) DW_TAG_compile_unit) + .long .LASF3 # DW_AT_producer: "GNU C 4.9.1 20140813 (Red Hat 4.9.1-7) -mtune=generic -march=x86-64 -g" + .byte 0x1 # DW_AT_language + .long .LASF4 # DW_AT_name: "py-framefilter-invalidarg.c" + .long .LASF5 # DW_AT_comp_dir: "" + .quad .Ltext0 # DW_AT_low_pc + .quad .Letext0-.Ltext0 # DW_AT_high_pc + .long .Ldebug_line0 # DW_AT_stmt_list +die2d: + .uleb128 0x2 # (DIE (0x2d) DW_TAG_subprogram) + # DW_AT_external + .long .LASF6 # DW_AT_name: "main" + .byte 0x1 # DW_AT_decl_file (py-framefilter-invalidarg.c) + .byte 0x1 # DW_AT_decl_line + # DW_AT_prototyped + .long die6b-.Ldebug_info0 # DW_AT_type + .quad .LFB0 # DW_AT_low_pc + .quad .LFE0-.LFB0 # DW_AT_high_pc + .uleb128 0x1 # DW_AT_frame_base + .byte 0x9c # DW_OP_call_frame_cfa + # DW_AT_GNU_all_call_sites +die4e: + .uleb128 0x3 # (DIE (0x4e) DW_TAG_formal_parameter) + .long .LASF0 # DW_AT_name: "argc" + .byte 0x1 # DW_AT_decl_file (py-framefilter-invalidarg.c) + .byte 0x1 # DW_AT_decl_line + .long die6b-.Ldebug_info0 # DW_AT_type +#if 0 + .uleb128 0x2 # DW_AT_location + .byte 0x91 # DW_OP_fbreg + .sleb128 -20 +#endif +#if 0 + .uleb128 1f - 2f # DW_AT_location +2: + .byte 0x03 # DW_OP_addr + .quad 0 +1: +#endif +#if 1 + .uleb128 1f - 2f # DW_AT_location +2: + .byte 0x13 # DW_OP_drop + .quad 0 +1: +#endif +die5c: + .uleb128 0x3 # (DIE (0x5c) DW_TAG_formal_parameter) + .long .LASF1 # DW_AT_name: "argv" + .byte 0x1 # DW_AT_decl_file (py-framefilter-invalidarg.c) + .byte 0x1 # DW_AT_decl_line + .long die72-.Ldebug_info0 # DW_AT_type + .uleb128 0x2 # DW_AT_location + .byte 0x91 # DW_OP_fbreg + .sleb128 -32 + .byte 0 # end of children of DIE 0x2d +die6b: + .uleb128 0x4 # (DIE (0x6b) DW_TAG_base_type) + .byte 0x4 # DW_AT_byte_size + .byte 0x5 # DW_AT_encoding + .ascii "int\0" # DW_AT_name +die72: + .uleb128 0x5 # (DIE (0x72) DW_TAG_pointer_type) + .byte 0x8 # DW_AT_byte_size + .long die78-.Ldebug_info0 # DW_AT_type +die78: + .uleb128 0x5 # (DIE (0x78) DW_TAG_pointer_type) + .byte 0x8 # DW_AT_byte_size + .long die7e-.Ldebug_info0 # DW_AT_type +die7e: + .uleb128 0x6 # (DIE (0x7e) DW_TAG_base_type) + .byte 0x1 # DW_AT_byte_size + .byte 0x6 # DW_AT_encoding + .long .LASF2 # DW_AT_name: "char" + .byte 0 # end of children of DIE 0xb +.Le: + .section .debug_abbrev,"",@progbits +.Ldebug_abbrev0: + .uleb128 0x1 # (abbrev code) + .uleb128 0x11 # (TAG: DW_TAG_compile_unit) + .byte 0x1 # DW_children_yes + .uleb128 0x25 # (DW_AT_producer) + .uleb128 0xe # (DW_FORM_strp) + .uleb128 0x13 # (DW_AT_language) + .uleb128 0xb # (DW_FORM_data1) + .uleb128 0x3 # (DW_AT_name) + .uleb128 0xe # (DW_FORM_strp) + .uleb128 0x1b # (DW_AT_comp_dir) + .uleb128 0xe # (DW_FORM_strp) + .uleb128 0x11 # (DW_AT_low_pc) + .uleb128 0x1 # (DW_FORM_addr) + .uleb128 0x12 # (DW_AT_high_pc) + .uleb128 0x7 # (DW_FORM_data8) + .uleb128 0x10 # (DW_AT_stmt_list) + .uleb128 0x17 # (DW_FORM_sec_offset) + .byte 0 + .byte 0 + .uleb128 0x2 # (abbrev code) + .uleb128 0x2e # (TAG: DW_TAG_subprogram) + .byte 0x1 # DW_children_yes + .uleb128 0x3f # (DW_AT_external) + .uleb128 0x19 # (DW_FORM_flag_present) + .uleb128 0x3 # (DW_AT_name) + .uleb128 0xe # (DW_FORM_strp) + .uleb128 0x3a # (DW_AT_decl_file) + .uleb128 0xb # (DW_FORM_data1) + .uleb128 0x3b # (DW_AT_decl_line) + .uleb128 0xb # (DW_FORM_data1) + .uleb128 0x27 # (DW_AT_prototyped) + .uleb128 0x19 # (DW_FORM_flag_present) + .uleb128 0x49 # (DW_AT_type) + .uleb128 0x13 # (DW_FORM_ref4) + .uleb128 0x11 # (DW_AT_low_pc) + .uleb128 0x1 # (DW_FORM_addr) + .uleb128 0x12 # (DW_AT_high_pc) + .uleb128 0x7 # (DW_FORM_data8) + .uleb128 0x40 # (DW_AT_frame_base) + .uleb128 0x18 # (DW_FORM_exprloc) + .uleb128 0x2117 # (DW_AT_GNU_all_call_sites) + .uleb128 0x19 # (DW_FORM_flag_present) + .byte 0 + .byte 0 + .uleb128 0x3 # (abbrev code) + .uleb128 0x5 # (TAG: DW_TAG_formal_parameter) + .byte 0 # DW_children_no + .uleb128 0x3 # (DW_AT_name) + .uleb128 0xe # (DW_FORM_strp) + .uleb128 0x3a # (DW_AT_decl_file) + .uleb128 0xb # (DW_FORM_data1) + .uleb128 0x3b # (DW_AT_decl_line) + .uleb128 0xb # (DW_FORM_data1) + .uleb128 0x49 # (DW_AT_type) + .uleb128 0x13 # (DW_FORM_ref4) + .uleb128 0x2 # (DW_AT_location) + .uleb128 0x18 # (DW_FORM_exprloc) + .byte 0 + .byte 0 + .uleb128 0x4 # (abbrev code) + .uleb128 0x24 # (TAG: DW_TAG_base_type) + .byte 0 # DW_children_no + .uleb128 0xb # (DW_AT_byte_size) + .uleb128 0xb # (DW_FORM_data1) + .uleb128 0x3e # (DW_AT_encoding) + .uleb128 0xb # (DW_FORM_data1) + .uleb128 0x3 # (DW_AT_name) + .uleb128 0x8 # (DW_FORM_string) + .byte 0 + .byte 0 + .uleb128 0x5 # (abbrev code) + .uleb128 0xf # (TAG: DW_TAG_pointer_type) + .byte 0 # DW_children_no + .uleb128 0xb # (DW_AT_byte_size) + .uleb128 0xb # (DW_FORM_data1) + .uleb128 0x49 # (DW_AT_type) + .uleb128 0x13 # (DW_FORM_ref4) + .byte 0 + .byte 0 + .uleb128 0x6 # (abbrev code) + .uleb128 0x24 # (TAG: DW_TAG_base_type) + .byte 0 # DW_children_no + .uleb128 0xb # (DW_AT_byte_size) + .uleb128 0xb # (DW_FORM_data1) + .uleb128 0x3e # (DW_AT_encoding) + .uleb128 0xb # (DW_FORM_data1) + .uleb128 0x3 # (DW_AT_name) + .uleb128 0xe # (DW_FORM_strp) + .byte 0 + .byte 0 + .byte 0 + .section .debug_aranges,"",@progbits + .long 0x2c # Length of Address Ranges Info + .value 0x2 # DWARF Version + .long .Ldebug_info0 # Offset of Compilation Unit Info + .byte 0x8 # Size of Address + .byte 0 # Size of Segment Descriptor + .value 0 # Pad to 16 byte boundary + .value 0 + .quad .Ltext0 # Address + .quad .Letext0-.Ltext0 # Length + .quad 0 + .quad 0 + .section .debug_line,"",@progbits +.Ldebug_line0: + .section .debug_str,"MS",@progbits,1 +.LASF1: + .string "argv" +.LASF4: + .string "py-framefilter-invalidarg.c" +.LASF5: + .string "" +.LASF0: + .string "argc" +.LASF3: + .string "GNU C 4.9.1 20140813 (Red Hat 4.9.1-7) -mtune=generic -march=x86-64 -g" +.LASF6: + .string "main" +.LASF2: + .string "char" + .ident "GCC: (GNU) 4.9.1 20140813 (Red Hat 4.9.1-7)" + .section .note.GNU-stack,"",@progbits Index: gdb-7.6.1/gdb/testsuite/gdb.python/py-framefilter-invalidarg-gdb.py.in =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ gdb-7.6.1/gdb/testsuite/gdb.python/py-framefilter-invalidarg-gdb.py.in 2015-11-27 18:06:22.446231736 +0100 @@ -0,0 +1,48 @@ +# Copyright (C) 2014 Free Software Foundation, Inc. + +# 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 . + +# This file is part of the GDB testsuite. It tests Python-based +# frame-filters. +import gdb +import itertools +from gdb.FrameDecorator import FrameDecorator + + +class FrameObjFile (): + + def __init__ (self): + self.name = "Filter1" + self.priority = 1 + self.enabled = False + gdb.current_progspace().frame_filters ["Progspace" + self.name] = self + gdb.current_objfile().frame_filters ["ObjectFile" + self.name] = self + + def filter (self, frame_iter): + return frame_iter + +class FrameObjFile2 (): + + def __init__ (self): + self.name = "Filter2" + self.priority = 100 + self.enabled = True + gdb.current_progspace().frame_filters ["Progspace" + self.name] = self + gdb.current_objfile().frame_filters ["ObjectFile" + self.name] = self + + def filter (self, frame_iter): + return frame_iter + +FrameObjFile() +FrameObjFile2() Index: gdb-7.6.1/gdb/testsuite/gdb.python/py-framefilter-invalidarg.exp =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ gdb-7.6.1/gdb/testsuite/gdb.python/py-framefilter-invalidarg.exp 2015-11-27 18:19:26.346625521 +0100 @@ -0,0 +1,86 @@ +# Copyright (C) 2014 Free Software Foundation, Inc. + +# 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 . + +load_lib gdb-python.exp + +standard_testfile amd64-py-framefilter-invalidarg.S + +if { ![istarget x86_64-*-* ] || ![is_lp64_target] } { + verbose "Skipping py-framefilter-invalidarg." + return +} + +# We cannot use prepare_for_testing as we have to set the safe-patch +# to check objfile and progspace printers. +if {[build_executable $testfile.exp $testfile $srcfile {}] == -1} { + return -1 +} + +# Start with a fresh gdb. +gdb_exit +gdb_start + +# Skip all tests if Python scripting is not enabled. +if { [skip_python_tests] } { continue } + +### IMPORT: +# Like remote_download but provides a gdb-specific behavior. If DEST +# is "host", and the host is not remote, and TOFILE is not specified, +# then the [file tail] of FROMFILE is passed through +# standard_output_file to compute the destination. + +proc gdb_remote_download {dest fromfile {tofile {}}} { + if {$dest == "host" && ![is_remote host] && $tofile == ""} { + set tofile [standard_output_file [file tail $fromfile]] + } + + if { $tofile == "" } { + return [remote_download $dest $fromfile] + } else { + return [remote_download $dest $fromfile $tofile] + } +} +### IMPORT^ + +# Make the -gdb.py script available to gdb, it is automagically loaded by gdb. +# Care is taken to put it in the same directory as the binary so that +# gdb will find it. +set remote_obj_python_file \ + [remote_download \ + host ${srcdir}/${subdir}/${testfile}-gdb.py.in \ + [standard_output_file ${testfile}-gdb.py]] + +gdb_reinitialize_dir $srcdir/$subdir +gdb_test_no_output "set auto-load safe-path ${remote_obj_python_file}" \ + "set auto-load safe-path" +gdb_load ${binfile} +# Verify gdb loaded the script. +gdb_test "info auto-load python-scripts" "Yes.*/${testfile}-gdb.py.*" \ + "Test auto-load had loaded python scripts" + +if ![runto_main] then { + perror "couldn't run to breakpoint" + return +} +gdb_test_no_output "set python print-stack full" \ + "Set python print-stack to full" + +# Load global frame-filters +set remote_python_file [gdb_remote_download host \ + ${srcdir}/${subdir}/${testfile}.py] +gdb_test_no_output "python exec (open ('${remote_python_file}').read ())" \ + "Load python file" + +gdb_test "bt" " in niam \\(argc=, argv=0x\[0-9a-f\]+\\) at py-framefilter-invalidarg.c:\[0-9\]+" "bt full with filters" Index: gdb-7.6.1/gdb/testsuite/gdb.python/py-framefilter-invalidarg.py =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ gdb-7.6.1/gdb/testsuite/gdb.python/py-framefilter-invalidarg.py 2015-11-27 18:06:22.447231742 +0100 @@ -0,0 +1,59 @@ +# Copyright (C) 2014 Free Software Foundation, Inc. + +# 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 . + +# This file is part of the GDB testsuite. It tests Python-based +# frame-filters. +import gdb +import itertools +from gdb.FrameDecorator import FrameDecorator +import copy + +class Reverse_Function (FrameDecorator): + + def __init__(self, fobj): + super(Reverse_Function, self).__init__(fobj) + self.fobj = fobj + + def function (self): + fname = str (self.fobj.function()) + if (fname == None or fname == ""): + return None + if fname == 'end_func': + extra = self.fobj.inferior_frame().read_var('str').string() + else: + extra = '' + fname = fname[::-1] + extra + return fname + +class FrameFilter (): + + def __init__ (self): + self.name = "Reverse" + self.priority = 100 + self.enabled = True + gdb.frame_filters [self.name] = self + + def filter (self, frame_iter): + # Python 3.x moved the itertools.imap functionality to map(), + # so check if it is available. + if hasattr(itertools, "imap"): + frame_iter = itertools.imap (Reverse_Function, + frame_iter) + else: + frame_iter = map(Reverse_Function, frame_iter) + + return frame_iter + +FrameFilter()