]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
gdb: split up construct_inferior_arguments
authorAndrew Burgess <aburgess@redhat.com>
Sat, 2 Nov 2024 17:35:14 +0000 (17:35 +0000)
committerAndrew Burgess <aburgess@redhat.com>
Tue, 18 Mar 2025 13:03:07 +0000 (13:03 +0000)
The function construct_inferior_arguments (gdbsupport/common-inferior.cc)
currently escapes all special shell characters.  After this commit
there will be two "levels" of quoting:

  1. The current "full" quoting, where all posix shell special
  characters are quoted, and

  2. a new "reduced" quoting, where only the characters that GDB sees
  as special (quotes and whitespace) are quoted.

After this, almost all construct_inferior_arguments calls will use the
"full" quoting, which is the current quoting.  The "reduced" quoting
will be used in this commit to restore the behaviour that was lost in
the previous commit (more details below).

In the future, the reduced quoting will be useful for some additional
inferior argument that I have planned.  I already posted my full
inferior argument work here:

  https://inbox.sourceware.org/gdb-patches/cover.1730731085.git.aburgess@redhat.com

But that series is pretty long, and wasn't getting reviewed, so I'm
posted the series in parts now.

Before the previous commit, GDB behaved like this:

  $ gdb -eiex 'set startup-with-shell off' --args /tmp/exec '$FOO'
  (gdb) show args
  Argument list to give program being debugged when it is started is "$FOO".

Notice that with 'startup-with-shell' off, the argument was left as
just '$FOO'.  But after the previous commit, this changed to:

  $ gdb -eiex 'set startup-with-shell off' --args /tmp/exec '$FOO'
  (gdb) show args
  Argument list to give program being debugged when it is started is "\$FOO".

Now the '$' is escaped with a backslash.  This commit restores the
original behaviour, as this is (currently) the only way to unquoted
shell special characters into arguments from the GDB command line.
The series that I listed above includes a new command line option for
GDB which provides a better approach for controlling the quoting of
special shell characters, but that work requires these patches to be
merged first.

I've split out the core of construct_inferior_arguments into the new
function escape_characters, which takes a set of characters to escape.
Then the two functions escape_shell_characters and
escape_gdb_characters call escape_characters with the appropriate
character sets.

Finally, construct_inferior_arguments, now takes a boolean which
indicates if we should perform full shell escaping, or just perform
the reduced escaping.

I've updated all uses of construct_inferior_arguments to pass a
suitable value to indicate what escaping to perform (mostly just
'true', but one case in main.c is different), also I've updated
inferior::set_args to take the same boolean flag, and pass it through
to construct_inferior_arguments.

Tested-By: Guinevere Larsen <guinevere@redhat.com>
gdb/corelow.c
gdb/inferior.c
gdb/inferior.h
gdb/main.c
gdb/python/py-inferior.c
gdbserver/server.cc
gdbsupport/common-inferior.cc
gdbsupport/common-inferior.h

index 4662b5c6fc749e2688faeda72f9eb1032b600577..567ecd5c5712214c30bd474513e2604d8db1d094 100644 (file)
@@ -1174,7 +1174,7 @@ core_target_open (const char *arg, int from_tty)
       for (const gdb::unique_xmalloc_ptr<char> &a : ctx.args ())
        argv.push_back (a.get ());
       gdb::array_view<char * const> view (argv.data (), argv.size ());
-      current_inferior ()->set_args (view);
+      current_inferior ()->set_args (view, true);
 
       /* And now copy the environment.  */
       current_inferior ()->environment = ctx.environment ();
index 67d70c5c2fb1cad10a8c99f085bee93dc7a0a25a..6472d49616cb42934c2e01c280c93caadd21576d 100644 (file)
@@ -167,9 +167,10 @@ inferior::tty ()
 /* See inferior.h.  */
 
 void
-inferior::set_args (gdb::array_view<char * const> args)
+inferior::set_args (gdb::array_view<char * const> args,
+                   bool escape_shell_char)
 {
-  set_args (construct_inferior_arguments (args));
+  set_args (construct_inferior_arguments (args, escape_shell_char));
 }
 
 void
index 3d9f86c0d4a4c5fc9a4b7402eeafbe73ebb94d72..327a474380f81d9f469a6978702f691a18efe352 100644 (file)
@@ -522,8 +522,11 @@ public:
     m_args = std::move (args);
   };
 
-  /* Set the argument string from some strings.  */
-  void set_args (gdb::array_view<char * const> args);
+  /* Set the argument string from some strings in ARGS.  When
+     ESCAPE_SHELL_CHAR is true all special shell characters in ARGS are
+     escaped, When false only the characters that GDB sees as special will
+     be escaped.  See construct_inferior_arguments for more details.  */
+  void set_args (gdb::array_view<char * const> args, bool escape_shell_char);
 
   /* Get the argument string to use when running this inferior.
 
index 27043b748855e11fe359c982c3da1d3916aa4a74..d126e98e16f231afbdc0944940fad8c2d3787179 100644 (file)
@@ -1078,7 +1078,8 @@ captured_main_1 (struct captured_main_args *context)
       execarg = argv[optind];
       ++optind;
       current_inferior ()->set_args
-       (gdb::array_view<char * const> (&argv[optind], argc - optind));
+       (gdb::array_view<char * const> (&argv[optind], argc - optind),
+        startup_with_shell);
     }
   else
     {
index d11ca9e6506949441d1706a43136256328588d15..356961cfbcb39d6074fdc8adfd051f2c0735664c 100644 (file)
@@ -929,7 +929,7 @@ infpy_set_args (PyObject *self, PyObject *value, void *closure)
       for (const auto &arg : args)
        argvec.push_back (arg.get ());
       gdb::array_view<char * const> view (argvec.data (), argvec.size ());
-      inf->inferior->set_args (view);
+      inf->inferior->set_args (view, true);
     }
   else
     {
index 3d452f91686d5c1089de3dc7f8bb31cb72cc1856..def01c1ee803088b609b8b05a9c3842c0cf86fa1 100644 (file)
@@ -3465,7 +3465,7 @@ handle_v_run (char *own_buf)
   else
     program_path.set (new_program_name.get ());
 
-  program_args = construct_inferior_arguments (new_argv.get ());
+  program_args = construct_inferior_arguments (new_argv.get (), true);
 
   try
     {
@@ -4355,7 +4355,7 @@ captured_main (int argc, char *argv[])
 
       int n = argc - (next_arg - argv);
       program_args
-       = construct_inferior_arguments ({&next_arg[1], &next_arg[n]});
+       = construct_inferior_arguments ({&next_arg[1], &next_arg[n]}, true);
 
       /* Wait till we are at first instruction in program.  */
       target_create_inferior (program_path.get (), program_args);
index 8e35f416e7081db9b24c1a973ee5134c3249a651..4b8682940626a9f6a4d43d33c72478f0eb821ac1 100644 (file)
 
 bool startup_with_shell = true;
 
-/* See common-inferior.h.  */
+/* Escape characters in ARG and return an updated string.  The string
+   SPECIAL contains the set of characters that must be escaped.  SPECIAL
+   must not be nullptr, and it is assumed that SPECIAL contains the newline
+   '\n' character.  It is assumed that ARG is not nullptr, but ARG can
+   be the empty string.  */
 
-std::string
-construct_inferior_arguments (gdb::array_view<char * const> argv)
+static std::string
+escape_characters (const char *arg, const char *special)
 {
+  gdb_assert (special != nullptr);
+  gdb_assert (arg != nullptr);
+
   std::string result;
 
 #ifdef __MINGW32__
-  /* This holds all the characters considered special to the
-     Windows shells.  */
-  static const char special[] = "\"!&*|[]{}<>?`~^=;, \t\n";
   static const char quote = '"';
 #else
-  /* This holds all the characters considered special to the
-     typical Unix shells.  We include `^' because the SunOS
-     /bin/sh treats it as a synonym for `|'.  */
-  static const char special[] = "\"!#$&*()\\|[]{}<>?'`~^; \t\n";
   static const char quote = '\'';
 #endif
-  for (int i = 0; i < argv.size (); ++i)
+
+  /* Need to handle empty arguments specially.  */
+  if (arg[0] == '\0')
     {
-      if (i > 0)
-       result += ' ';
+      result += quote;
+      result += quote;
+    }
+  /* The special character handling code here assumes that if SPECIAL is
+     not nullptr, then SPECIAL will contain '\n'.  This is true for all our
+     current usages, but if this ever changes in the future the following
+     might need reworking.  */
+  else
+    {
+#ifdef __MINGW32__
+      bool quoted = false;
 
-      /* Need to handle empty arguments specially.  */
-      if (argv[i][0] == '\0')
+      if (strpbrk (argv[i], special))
        {
-         result += quote;
+         quoted = true;
          result += quote;
        }
-      else
+#endif
+      for (const char *cp = arg; *cp; ++cp)
        {
-#ifdef __MINGW32__
-         bool quoted = false;
-
-         if (strpbrk (argv[i], special))
+         if (*cp == '\n')
            {
-             quoted = true;
+             /* A newline cannot be quoted with a backslash (it just
+                disappears), only by putting it inside quotes.  */
+             result += quote;
+             result += '\n';
              result += quote;
            }
-#endif
-         for (char *cp = argv[i]; *cp != '\0'; ++cp)
+         else
            {
-             if (*cp == '\n')
-               {
-                 /* A newline cannot be quoted with a backslash (it
-                    just disappears), only by putting it inside
-                    quotes.  */
-                 result += quote;
-                 result += '\n';
-                 result += quote;
-               }
-             else
-               {
 #ifdef __MINGW32__
-                 if (*cp == quote)
+             if (*cp == quote)
 #else
-                   if (strchr (special, *cp) != NULL)
+               if (strchr (special, *cp) != nullptr)
 #endif
-                     result += '\\';
-                 result += *cp;
-               }
+                 result += '\\';
+             result += *cp;
            }
+       }
 #ifdef __MINGW32__
-         if (quoted)
-           result += quote;
+      if (quoted)
+       result += quote;
 #endif
-       }
+    }
+
+  return result;
+}
+
+/* Return a version of ARG that has special shell characters escaped.  */
+
+static std::string
+escape_shell_characters (const char *arg)
+{
+#ifdef __MINGW32__
+  /* This holds all the characters considered special to the
+     Windows shells.  */
+  static const char special[] = "\"!&*|[]{}<>?`~^=;, \t\n";
+#else
+  /* This holds all the characters considered special to the
+     typical Unix shells.  We include `^' because the SunOS
+     /bin/sh treats it as a synonym for `|'.  */
+  static const char special[] = "\"!#$&*()\\|[]{}<>?'`~^; \t\n";
+#endif
+
+  return escape_characters (arg, special);
+}
+
+/* Return a version of ARG that has quote characters and white space
+   characters escaped.  These are the characters that GDB sees as special
+   when splitting a string into separate arguments.  */
+
+static std::string
+escape_gdb_characters (const char * arg)
+{
+#ifdef __MINGW32__
+  static const char special[] = "\" \t\n";
+#else
+  static const char special[] = "\"' \t\n";
+#endif
+
+  return escape_characters (arg, special);
+}
+
+/* See common-inferior.h.  */
+
+std::string
+construct_inferior_arguments (gdb::array_view<char * const> argv,
+                             bool escape_shell_char)
+{
+  /* Select the desired escape function.  */
+  const auto escape_func = (escape_shell_char
+                           ? escape_shell_characters
+                           : escape_gdb_characters);
+
+  std::string result;
+
+  for (const char *a : argv)
+    {
+      if (!result.empty ())
+       result += " ";
+
+      result += escape_func (a);
     }
 
   return result;
index ef99815894515a64c3f80e768b5157b382f0e412..3e8ec10df9bc65db862113a4e0eaf44099a17e49 100644 (file)
@@ -52,9 +52,13 @@ extern const std::string &get_inferior_cwd ();
    the target is started up with a shell.  */
 extern bool startup_with_shell;
 
-/* Compute command-line string given argument vector. This does the
-   same shell processing as fork_inferior.  */
+/* Combine elements of ARGV into a single string, placing a single
+   whitespace character between each element.  When ESCAPE_SHELL_CHAR is
+   true then any special shell characters in elemets of ARGV will be
+   escaped.  When ESCAPE_SHELL_CHAR is false only the characters that GDB
+   sees as special (quotes and whitespace) are escaped.  */
 extern std::string
-construct_inferior_arguments (gdb::array_view<char * const>);
+construct_inferior_arguments (gdb::array_view<char * const> argv,
+                             bool escape_shell_char);
 
 #endif /* GDBSUPPORT_COMMON_INFERIOR_H */