From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTPS id D7B9F3858D35 for ; Mon, 7 Mar 2022 13:09:49 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org D7B9F3858D35 Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-237-VcO3LkdDNR6ximGQtH7v3w-1; Mon, 07 Mar 2022 08:09:47 -0500 X-MC-Unique: VcO3LkdDNR6ximGQtH7v3w-1 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.phx2.redhat.com [10.5.11.16]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id ECC05180FD7D for ; Mon, 7 Mar 2022 13:09:46 +0000 (UTC) Received: from blarsen.com (ovpn-116-31.gru2.redhat.com [10.97.116.31]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 394E97D3DB; Mon, 7 Mar 2022 13:09:41 +0000 (UTC) From: Bruno Larsen To: gdb-patches@sourceware.org Subject: [PATCH v2] [gdb/testsuite] test a function call by hand fron pretty printer Date: Mon, 7 Mar 2022 10:09:24 -0300 Message-Id: <20220307130924.23940-1-blarsen@redhat.com> In-Reply-To: <20220223185729.25609-1-blarsen@redhat.com> References: <20220223185729.25609-1-blarsen@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.16 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: 8bit Content-Type: text/plain; charset="US-ASCII" X-Spam-Status: No, score=-13.2 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H5, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, TXREP, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on server2.sourceware.org X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 07 Mar 2022 13:09:51 -0000 The test case added here is testing the bug gdb/28856, where calling a function by hand from a pretty printer makes GDB crash. There are 6 mechanisms to trigger this crash in the current test, using the commands backtrace, up, down, finish, step and continue. Since the failure happens because of use-after-free (more details below) the tests will always have a chance of passing through sheer luck, but anecdotally they seem to fail all of the time. The reason GDB is crashing is a use-after-free problem. The above mentioned functions save a pointer to the current frame's information, then calls the pretty printer, and uses the saved pointer for different reasons, depending on the function. The issue happens because call_function_by_hand needs to reset the obstack to get the current frame, invalidating the saved pointer. --- Changes from v1: * make .c follow GDB's coding guidelines more closely * Documented .exp file better * renamed and refactored TCL proc to make it less confusing * General style changes Thanks Joel for your comments! --- .../gdb.python/pretty-print-call-by-hand.c | 48 ++++++++ .../gdb.python/pretty-print-call-by-hand.exp | 115 ++++++++++++++++++ .../gdb.python/pretty-print-call-by-hand.py | 25 ++++ 3 files changed, 188 insertions(+) create mode 100644 gdb/testsuite/gdb.python/pretty-print-call-by-hand.c create mode 100644 gdb/testsuite/gdb.python/pretty-print-call-by-hand.exp create mode 100644 gdb/testsuite/gdb.python/pretty-print-call-by-hand.py diff --git a/gdb/testsuite/gdb.python/pretty-print-call-by-hand.c b/gdb/testsuite/gdb.python/pretty-print-call-by-hand.c new file mode 100644 index 00000000000..c215c78541b --- /dev/null +++ b/gdb/testsuite/gdb.python/pretty-print-call-by-hand.c @@ -0,0 +1,48 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2022 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 . */ + +struct mytype +{ + char *x; +}; + +void rec (int i) +{ + if (i <= 0) + return; + rec (i-1); +} + +int f () +{ + rec (100); + return 2; +} + +void g (struct mytype mt, int depth) +{ + if (depth <= 0) + return; /* TAG: final frame */ + g (mt, depth - 1); /* TAG: first frame */ +} + +int main (){ + struct mytype mt; + mt.x = "hello world"; + g (mt, 10); /* TAG: outside the frame */ + return 0; +} diff --git a/gdb/testsuite/gdb.python/pretty-print-call-by-hand.exp b/gdb/testsuite/gdb.python/pretty-print-call-by-hand.exp new file mode 100644 index 00000000000..7508be20a03 --- /dev/null +++ b/gdb/testsuite/gdb.python/pretty-print-call-by-hand.exp @@ -0,0 +1,115 @@ +# Copyright (C) 2022 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 a pretty printer that +# calls an inferior function by hand, triggering a Use-after-Free bug +# (PR gdb/28856). + +load_lib gdb-python.exp + +standard_testfile + +# gdb needs to be started here for skip_python_tests to work. +# prepare_for_testing could be used instead, but it could compile the program +# unnecessarily, so starting GDB like this is preferable. +gdb_start + +# Skip all tests if Python scripting is not enabled. +if { [skip_python_tests] } { continue } + +if { [prepare_for_testing "failed to prepare" $testfile $srcfile debug] } { + untested "failed to compile" + return -1 +} + +# This proc restarts GDB, makes the inferior reach the desired spot - marked +# by a comment in the .c file - and turns on the pretty printer for testing. +# Starting with a new GDB is important because the test may crash GDB. The +# return values are here to avoid us trying to test the pretty printer if +# there was a problem getting to main. +proc start_test { breakpoint_comment } { + global srcdir subdir testfile binfile + + # Start with a fresh gdb. + # This is important because the test can crash GDB. + + clean_restart ${binfile} + + if { ![runto_main] } then { + untested "couldn't run to breakpoint" + return -1 + } + + # Let GDB get to the return line. + gdb_breakpoint [gdb_get_line_number ${breakpoint_comment} ${testfile}.c ] + gdb_continue_to_breakpoint ${breakpoint_comment} ".*" + + gdb_test_no_output "set print pretty on" "starting to pretty print" + + set remote_python_file [gdb_remote_download host ${srcdir}/${subdir}/${testfile}.py] + gdb_test_no_output "source ${remote_python_file}" "load python file" + + return 0 +} + +# Testing the backtrace command. +with_test_prefix "frame print" { + if { [start_test "TAG: final frame"] == 0 } { + setup_kfail gdb/28856 "*-*-*" + gdb_test "backtrace -frame-arguments all" ".*" + } +} + +# Testing the down command. +with_test_prefix "frame movement down" { + if { [start_test "TAG: first frame"] == 0 } { + gdb_test "up" ".*" + setup_kfail gdb/28856 "*-*-*" + gdb_test "down" ".*" + } +} + +# Testing the up command. +with_test_prefix "frame movement up" { + if { [start_test "TAG: final frame"] == 0 } { + setup_kfail gdb/28856 "*-*-*" + gdb_test "up" ".*" + } +} + +# Testing the finish command. +with_test_prefix "frame exit through finish" { + if { [start_test "TAG: first frame"] == 0 } { + setup_kfail gdb/28856 "*-*-*" + gdb_test "finish" ".*" + } +} + +# Testing the step command. +with_test_prefix "frame enter through step" { + if { [start_test "TAG: outside the frame"] == 0 } { + setup_kfail gdb/28856 "*-*-*" + gdb_test "step" ".*" + } +} + +# Testing the continue command. +with_test_prefix "frame enter through continue" { + if { [start_test "TAG: outside the frame"] == 0 } { + setup_kfail gdb/28856 "*-*-*" + gdb_breakpoint [gdb_get_line_number "TAG: first frame" ${testfile}.c ] + gdb_continue_to_breakpoint "TAG: first frame" ".*TAG: first frame.*" + } +} diff --git a/gdb/testsuite/gdb.python/pretty-print-call-by-hand.py b/gdb/testsuite/gdb.python/pretty-print-call-by-hand.py new file mode 100644 index 00000000000..627011a9ac9 --- /dev/null +++ b/gdb/testsuite/gdb.python/pretty-print-call-by-hand.py @@ -0,0 +1,25 @@ +class MytypePrinter: + """pretty print my type""" + + def __init__(self, val): + self.val = val + + def to_string(self): + calls = gdb.parse_and_eval('f()') + return "mytype is %s" % self.val['x'] + +def ec_lookup_function(val): + typ = val.type + if typ.code == gdb.TYPE_CODE_REF: + typ = typ.target() + if str(typ) == 'struct mytype': + return MytypePrinter(val) + return None + +def disable_lookup_function(): + ec_lookup_function.enabled = False + +def enable_lookup_function(): + ec_lookup_function.enabled = True + +gdb.pretty_printers.append(ec_lookup_function) -- 2.31.1