From: Andrew Burgess Date: Mon, 15 Sep 2025 13:20:52 +0000 (+0100) Subject: gdbserver: prevent assertion caused by passing empty program name X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=e68870e4cdd55352cce29c4fcb51414734ad713e;p=thirdparty%2Fbinutils-gdb.git gdbserver: prevent assertion caused by passing empty program name While testing another patch I'm working on I discovered that passing an empty program name to gdbserver would trigger an assertion, like this: $ gdbserver --multi :54321 "" ../../gdb/gdbserver/../gdb/nat/fork-inferior.c:240: A problem internal to GDBserver has been detected. fork_inferior: Assertion `exec_file != nullptr' failed. User input, no matter how weird, shouldn't be triggering an assertion, so lets fix that. In extended mode, it is valid to start gdbserver without an executable name, like this: $ gdbserver --multi :54321 Here gdbserver doesn't start an inferior, and it is up to GDB to connect, and tell gdbserver what to run, and to then start it running. I did wonder if the empty string case should handled like the no executable name case, but then you get into the situation where the user can specify command line arguments without an inferior, like: $ gdbserver --multi :54321 "" a b c And while there's nothing really wrong with this, and I'm sure someone could come up with a use case for it. I'd like to propose that for now at least, we take the simple approach of not allowing an empty executable name, instead we should give an error, like this: $ gdbserver --multi :54321 "" No program to debug Exiting We can always relax this requirement in the future, and allow the empty executable with or without inferior arguments, if we decide there's a compelling reason for it. It would be simple enough to add this in the future, but once we add support for it, it's much harder to remove the feature in the future, so lets start simple. The non-extended remote case works much the same. It too triggers the assertion currently, and after this patch exits with the same error. Of course, the non-extended remote case never supported not having an inferior, if you did: $ gdbserver :54321 You'd be shown the usage text and gdbserver would exit. Approved-By: Tom Tromey --- diff --git a/gdb/testsuite/gdb.server/server-empty-exec-name.exp b/gdb/testsuite/gdb.server/server-empty-exec-name.exp new file mode 100644 index 00000000000..a9cb9abb76f --- /dev/null +++ b/gdb/testsuite/gdb.server/server-empty-exec-name.exp @@ -0,0 +1,93 @@ +# This testcase is part of GDB, the GNU debugger. +# +# Copyright 2025 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 . + +# Test what happens if we try to start gdbserver with an empty string +# for the executable name. In theory we could choose to accept this +# for the extended-remote case, indeed, in the extended-remote case +# it's OK to start gdbserver with no program name or arguments. +# +# But actually specifying an empty executable name seems different, +# and wrong, so for now at least, we don't allow this. If that +# position changes in the future, then this test will need updating. + +# Skip test if target does not support argument passing. +require {!target_info exists noargs} + +load_lib gdbserver-support.exp + +standard_testfile + +require allow_gdbserver_tests + +set gdbserver [find_gdbserver] +if { $gdbserver == "" } { + unsupported "could not find gdbserver" + return +} + +clean_restart + +# Try starting gdbserver in extended, and non-extended mode. +foreach_with_prefix proto { extended-remote remote } { + + if { $proto eq "extended-remote" } { + set server_flag "--multi" + } else { + set server_flag "" + } + + # Try starting gdbserver with and without arguments. + foreach_with_prefix args { " a b c" "" } { + # Start gdbserver on its own. This makes it easier to check + # the error message that gdbserver emits. The program name is + # the empty string. gdbserver should exit. + set gdbserver_cmd \ + "$gdbserver $server_flag --once - \"\"${args}" + + set spawn_id [remote_spawn target $gdbserver_cmd] + set testname "start gdbserver" + expect { + -i $spawn_id + + -re "No program to debug\r\nExiting" { pass $testname } + + eof { fail $testname } + timeout { fail $testname } + } + + catch { + close -i $spawn_id + wait -nowait -i $spawn_id + } + + # Try connecting to gdbserver from GDB. The connection should + # immediately close as gdbserver exits. The inferior should + # be left with no connection. + gdb_test "disconnect" ".*" \ + "disconnect before remote connection" + + gdb_test "target $proto | ${::gdbserver} $server_flag --once - \"\"${args}" \ + [multi_line \ + "Remote debugging using \\| \[^\r\n\]+" \ + "Remote communication error\\. Target disconnected: .*"] \ + "start gdbserver over stdin" + + gdb_test "info inferiors" \ + "\\*\\s+1\\s+\\s*" \ + "check inferior 1 has no connection" + } +} diff --git a/gdbserver/server.cc b/gdbserver/server.cc index 5b40600f457..cb1a63e2c8f 100644 --- a/gdbserver/server.cc +++ b/gdbserver/server.cc @@ -4545,6 +4545,9 @@ captured_main (int argc, char *argv[]) { program_path.set (next_arg[0]); + if (program_path.get () == nullptr) + error (_("No program to debug")); + int n = argc - (next_arg - argv); program_args = construct_inferior_arguments ({&next_arg[1], &next_arg[n]},