gdb/python: fix invalid use disassemble_info::stream
authorAndrew Burgess <aburgess@redhat.com>
Wed, 20 Jul 2022 12:57:08 +0000 (13:57 +0100)
committerAndrew Burgess <aburgess@redhat.com>
Mon, 25 Jul 2022 18:26:24 +0000 (19:26 +0100)
After this commit:

  commit 81384924cdcc9eb2676dd9084b76845d7d0e0759
  Date:   Tue Apr 5 11:06:16 2022 +0100

      gdb: have gdb_disassemble_info carry 'this' in its stream pointer

The disassemble_info::stream field will no longer be a ui_file*.  That
commit failed to update one location in py-disasm.c though.

While running some tests using the Python disassembler API, I
triggered a call to gdbpy_disassembler::print_address_func, and, as I
had compiled GDB with the undefined behaviour sanitizer, GDB crashed
as the code currently (incorrectly) casts the stream field to be a
ui_file*.

In this commit I fix this error.

In order to test this case I had to tweak the existing test case a
little.  I also spotted some debug printf statements in py-disasm.py,
which I have removed.

gdb/python/py-disasm.c
gdb/testsuite/gdb.python/py-disasm.c
gdb/testsuite/gdb.python/py-disasm.exp
gdb/testsuite/gdb.python/py-disasm.py

index 4c78ca350c27ceb30427edcd05b1e0ea32d9eafe..c37452fcf729e7b852ef5d12dae5e21c154c3e90 100644 (file)
@@ -626,7 +626,7 @@ gdbpy_disassembler::print_address_func (bfd_vma addr,
 {
   gdbpy_disassembler *dis
     = static_cast<gdbpy_disassembler *> (info->application_data);
-  print_address (dis->arch (), addr, (struct ui_file *) info->stream);
+  print_address (dis->arch (), addr, dis->stream ());
 }
 
 /* constructor.  */
index ee0bb157f4dd1abd1bc9fc0058e869e7af641cde..e5c4d2f1d0e710325f2736eb17bd2d3188372be7 100644 (file)
    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 int
-main ()
+test ()
 {
   asm ("nop");
   asm ("nop"); /* Break here.  */
   asm ("nop");
   return 0;
 }
+
+int
+main ()
+{
+  return test ();
+}
index 1b9cd4465ac994149f3b950e0b586942b77080e3..1f94d3e60f3d613c2220a1ea15e57097771bf586 100644 (file)
@@ -110,8 +110,8 @@ foreach plan $test_plans {
            gdb_test_no_output "python add_global_disassembler($global_disassembler_name)"
        }
 
-       # Disassemble main, and check the disassembler output.
-       gdb_test "disassemble main" $expected_pattern
+       # Disassemble test, and check the disassembler output.
+       gdb_test "disassemble test" $expected_pattern
     }
 }
 
@@ -138,21 +138,21 @@ with_test_prefix "DisassemblerResult errors" {
 with_test_prefix "GLOBAL tagging disassembler" {
     py_remove_all_disassemblers
     gdb_test_no_output "python gdb.disassembler.register_disassembler(TaggingDisassembler(\"GLOBAL\"), None)"
-    gdb_test "disassemble main" "${base_pattern}\\s+## tag = GLOBAL\r\n.*"
+    gdb_test "disassemble test" "${base_pattern}\\s+## tag = GLOBAL\r\n.*"
 }
 
 # Now register an architecture specific disassembler, and check it
 # overrides the global disassembler.
 with_test_prefix "LOCAL tagging disassembler" {
     gdb_test_no_output "python gdb.disassembler.register_disassembler(TaggingDisassembler(\"LOCAL\"), \"${curr_arch}\")"
-    gdb_test "disassemble main" "${base_pattern}\\s+## tag = LOCAL\r\n.*"
+    gdb_test "disassemble test" "${base_pattern}\\s+## tag = LOCAL\r\n.*"
 }
 
 # Now remove the architecture specific disassembler, and check that
 # the global disassembler kicks back in.
 with_test_prefix "GLOBAL tagging disassembler again" {
     gdb_test_no_output "python gdb.disassembler.register_disassembler(None, \"${curr_arch}\")"
-    gdb_test "disassemble main" "${base_pattern}\\s+## tag = GLOBAL\r\n.*"
+    gdb_test "disassemble test" "${base_pattern}\\s+## tag = GLOBAL\r\n.*"
 }
 
 # Check that a DisassembleInfo becomes invalid after the call into the
@@ -160,7 +160,7 @@ with_test_prefix "GLOBAL tagging disassembler again" {
 with_test_prefix "DisassembleInfo becomes invalid" {
     py_remove_all_disassemblers
     gdb_test_no_output "python add_global_disassembler(GlobalCachingDisassembler)"
-    gdb_test "disassemble main" "${base_pattern}\\s+## CACHED\r\n.*"
+    gdb_test "disassemble test" "${base_pattern}\\s+## CACHED\r\n.*"
     gdb_test "python GlobalCachingDisassembler.check()" "PASS"
 }
 
@@ -168,10 +168,10 @@ with_test_prefix "DisassembleInfo becomes invalid" {
 with_test_prefix "memory source api" {
     py_remove_all_disassemblers
     gdb_test_no_output "python analyzing_disassembler = add_global_disassembler(AnalyzingDisassembler)"
-    gdb_test "disassemble main" "${base_pattern}\r\n.*"
+    gdb_test "disassemble test" "${base_pattern}\r\n.*"
     gdb_test "python analyzing_disassembler.find_replacement_candidate()" \
        "Replace from $hex to $hex with NOP"
-    gdb_test "disassemble main" "${base_pattern}\r\n.*" \
+    gdb_test "disassemble test" "${base_pattern}\r\n.*" \
        "second disassembler pass"
     gdb_test "python analyzing_disassembler.check()" \
        "PASS"
@@ -196,6 +196,12 @@ with_test_prefix "maint info python-disassemblers" {
             "\[^\r\n\]+BuiltinDisassembler\\s+\\(Matches current architecture\\)" \
             "GLOBAL\\s+BuiltinDisassembler"] \
        "list disassemblers, multiple disassemblers registered"
+
+    # Check that disassembling main (with the BuiltinDisassembler in
+    # place) doesn't cause GDB to crash.  The hope is that
+    # disassembling main will result in a call to print_address, which
+    # is where the problem was.
+    gdb_test "disassemble main" ".*"
 }
 
 # Check the attempt to create a "new" DisassembleInfo object fails.
index ff7ffdb97d9f76067925606b2572659d63b97d51..3b9008b1c54aad9c8c357a1e35a8c0f04deca017 100644 (file)
@@ -584,7 +584,6 @@ class AnalyzingDisassembler(Disassembler):
         if self._nop_index is None and result.string == "nop":
             self._nop_index = len(self._pass_1_length)
             # The offset in the following read_memory call defaults to 0.
-            print("APB: Reading nop bytes")
             self._nop_bytes = info.read_memory(result.length)
 
         # Record information about each instruction that is disassembled.
@@ -661,8 +660,6 @@ class AnalyzingDisassembler(Disassembler):
     def check(self):
         """Call this after the second disassembler pass to validate the output."""
         if self._check != self._pass_2_insn:
-            print("APB, Check : %s" % self._check)
-            print("APB, Result: %s" % self._pass_2_insn)
             raise gdb.GdbError("mismatch")
         print("PASS")