From: Luis Machado Date: Fri, 28 Feb 2025 09:36:42 +0000 (+0000) Subject: Fix gdbserver crashes on SVE/SME-enabled systems X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=1137625d46f3840dd7cb7f8519ec9dcba0acd53e;p=thirdparty%2Fbinutils-gdb.git Fix gdbserver crashes on SVE/SME-enabled systems Commit 51e6b8cfd649013ae16a3d00f1451b2531ba6bc9 fixed a regression for SVE/SME registers on gdb's side by using a <= comparison for regcache's raw_compare assertion check. We seem to have failed to do the same for gdbserver's raw_compare counterpart. With the code as it is, I'm seeing a lot of crashes for gdbserver on a machine with SVE enabled. For instance, with the following invocation: make check-gdb RUNTESTFLAGS="--target_board=native-gdbserver" TESTS=gdb.base/break.exp Running /work/builds/binutils-gdb/gdb/testsuite/../../../../repos/binutils-gdb/gdb/testsuite/gdb.base/break.exp ... FAIL: gdb.base/break.exp: test_break: run until function breakpoint FAIL: gdb.base/break.exp: test_break: run until breakpoint set at a line number (the program is no longer running) FAIL: gdb.base/break.exp: test_break: run until file:function(6) breakpoint (the program is no longer running) FAIL: gdb.base/break.exp: test_break: run until file:function(5) breakpoint (the program is no longer running) FAIL: gdb.base/break.exp: test_break: run until file:function(4) breakpoint (the program is no longer running) FAIL: gdb.base/break.exp: test_break: run until file:function(3) breakpoint (the program is no longer running) FAIL: gdb.base/break.exp: test_break: run until file:function(2) breakpoint (the program is no longer running) FAIL: gdb.base/break.exp: test_break: run until file:function(1) breakpoint (the program is no longer running) FAIL: gdb.base/break.exp: test_break: run until quoted breakpoint (the program is no longer running) FAIL: gdb.base/break.exp: test_break: run until file:linenum breakpoint (the program is no longer running) FAIL: gdb.base/break.exp: test_break: breakpoint offset +1 FAIL: gdb.base/break.exp: test_break: step onto breakpoint (the program is no longer running) FAIL: gdb.base/break.exp: test_break: setting breakpoint at } FAIL: gdb.base/break.exp: test_break: continue to breakpoint at } (the program is no longer running) FAIL: gdb.base/break.exp: test_no_break_on_catchpoint: runto: run to main FAIL: gdb.base/break.exp: test_break_nonexistent_line: runto: run to main FAIL: gdb.base/break.exp: test_break_default: runto: run to main FAIL: gdb.base/break.exp: test_break_silent_and_more: runto: run to main FAIL: gdb.base/break.exp: test_break_line_convenience_var: runto: run to main FAIL: gdb.base/break.exp: test_break_user_call: runto: run to main FAIL: gdb.base/break.exp: test_finish_arguments: runto: run to main FAIL: gdb.base/break.exp: test_next_with_recursion: kill program FAIL: gdb.base/break.exp: test_next_with_recursion: run to factorial(6) FAIL: gdb.base/break.exp: test_next_with_recursion: continue to factorial(5) (the program is no longer running) FAIL: gdb.base/break.exp: test_next_with_recursion: backtrace from factorial(5) FAIL: gdb.base/break.exp: test_next_with_recursion: next to recursive call (the program is no longer running) FAIL: gdb.base/break.exp: test_next_with_recursion: next over recursive call (the program is no longer running) FAIL: gdb.base/break.exp: test_next_with_recursion: backtrace from factorial(5.1) FAIL: gdb.base/break.exp: test_next_with_recursion: continue until exit at recursive next test (the program is no longer running) FAIL: gdb.base/break.exp: test_break_optimized_prologue: run until function breakpoint, optimized file FAIL: gdb.base/break.exp: test_break_optimized_prologue: run until breakpoint set at small function, optimized file (the program is no longer running) FAIL: gdb.base/break.exp: test_rbreak_shlib: rbreak junk Adjusting the regcache raw_compare assertion check to use <= fixes the problem on aarch64-linux on a SVE-capable system. This patch also adds a simple selftest to gdbserver that validates this particular case by simulating a raw_compare operation. Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=32775 Approved-By: Simon Marchi --- diff --git a/gdbserver/regcache.cc b/gdbserver/regcache.cc index ee0c1b363b0..c08c9ae06c1 100644 --- a/gdbserver/regcache.cc +++ b/gdbserver/regcache.cc @@ -503,7 +503,7 @@ regcache::raw_compare (int regnum, const void *buf, int offset) const gdb_assert (buf != NULL); gdb::array_view regbuf = register_data (this, regnum); - gdb_assert (offset < regbuf.size ()); + gdb_assert (offset <= regbuf.size ()); regbuf = regbuf.slice (offset); return memcmp (buf, regbuf.data (), regbuf.size ()) == 0; diff --git a/gdbserver/server.cc b/gdbserver/server.cc index def01c1ee80..3172cd171aa 100644 --- a/gdbserver/server.cc +++ b/gdbserver/server.cc @@ -4071,6 +4071,33 @@ test_memory_tagging_functions (void) && tags.size () == 5); } +/* Exercise the behavior of doing a 0-length comparison for a register in a + register buffer, which should return true. */ + +static void test_registers_raw_compare_zero_length () +{ + /* Start off with a dummy target description. */ + target_desc dummy_tdesc; + + /* Make it 8 bytes long. */ + dummy_tdesc.registers_size = 8; + + /* Add a couple dummy 32-bit registers. */ + dummy_tdesc.reg_defs.emplace_back ("r0", 0, 32); + dummy_tdesc.reg_defs.emplace_back ("r1", 32, 32); + + /* Create our dummy register cache so we can invoke the raw_compare method + we want to validate. */ + regcache dummy_regcache (&dummy_tdesc); + + /* Create a dummy byte buffer we can pass to the raw_compare method. */ + gdb_byte dummy_buffer[8]; + + /* Validate the 0-length comparison (due to the comparison offset being + equal to the length of the register) returns true. */ + SELF_CHECK (dummy_regcache.raw_compare (0, dummy_buffer, 4)); +} + } /* namespace selftests */ #endif /* GDB_SELF_TEST */ @@ -4094,6 +4121,8 @@ captured_main (int argc, char *argv[]) selftests::register_test ("remote_memory_tagging", selftests::test_memory_tagging_functions); + selftests::register_test ("test_registers_raw_compare_zero_length", + selftests::test_registers_raw_compare_zero_length); #endif current_directory = getcwd (NULL, 0);