]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
gdb/python/guile: check for invalid prefixes in Command/Parameter creation
authorAndrew Burgess <aburgess@redhat.com>
Fri, 11 Apr 2025 15:01:02 +0000 (16:01 +0100)
committerAndrew Burgess <aburgess@redhat.com>
Tue, 13 May 2025 13:22:22 +0000 (14:22 +0100)
The manual for gdb.Parameter says:

  If NAME consists of multiple words, and no prefix parameter group
  can be found, an exception is raised.

This makes sense; we cannot create a parameter within a prefix group,
if the prefix doesn't exist.  And this almost works, so:

  (gdb) python gdb.Parameter("xxx foo", gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)
  Python Exception <class 'RuntimeError'>: Could not find command prefix xxx.
  Error occurred in Python: Could not find command prefix xxx.

The prefix 'xxx' doesn't exist, and we get an error.  But, if we try
multiple levels of prefix:

  (gdb) python gdb.Parameter("print xxx foo", gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)

This completes without error, however, we didn't get what we were
maybe expecting:

  (gdb) show print xxx foo
  Undefined show print command: "xxx foo".  Try "help show print".

But we did get:

  (gdb) show print foo
  The current value of 'print foo' is "off".

GDB stopped scanning the prefix string at the unknown 'xxx', and just
created the parameter there.  I don't think this makes sense, nor is
it inline with the manual.

An identical problem exists with gdb.Command creation; GDB stops
parsing the prefix at the first unknown prefix, and just creates the
command there.  The manual for gdb.Command says:

  NAME is the name of the command.  If NAME consists of multiple
  words, then the initial words are looked for as prefix commands.
  In this case, if one of the prefix commands does not exist, an
  exception is raised.

So again, the correct action is, I believe, to raise an exception.

The problem is in gdbpy_parse_command_name (python/py-cmd.c), GDB
calls lookup_cmd_1 to look through the prefix string and return the
last prefix group.  If the very first prefix word is invalid then
lookup_cmd_1 returns NULL, and this case is handled.  However, if
there is a valid prefix, followed by an invalid prefix, then
lookup_cmd_1 will return a pointer to the last valid prefix list, and
will update the input argument to point to the start of the invalid
prefix word.  This final case, where the input is left pointing to an
unknown prefix, was previously not handled.

I've fixed gdbpy_parse_command_name, and added tests for command and
parameter creation to cover this case.

The exact same error is present in the guile API too.  The guile
documentation for make-parameter and make-command says the same things
about unknown prefixes resulting in an exception, but the same error
is present in gdbscm_parse_command_name (guile/scm-cmd.c), so I've
fixed that too, and added some tests.

gdb/guile/scm-cmd.c
gdb/python/py-cmd.c
gdb/testsuite/gdb.guile/scm-cmd.exp
gdb/testsuite/gdb.guile/scm-parameter.exp
gdb/testsuite/gdb.python/py-cmd.exp
gdb/testsuite/gdb.python/py-parameter.exp

index 47578729425dc37e99b7bb45898157420b527492..453394ccb19fe00cfb2ebf4f719b980a087e9b5f 100644 (file)
@@ -509,7 +509,7 @@ gdbscm_parse_command_name (const char *name,
 
   const char *prefix_text2 = prefix_text.get ();
   elt = lookup_cmd_1 (&prefix_text2, *start_list, NULL, NULL, 1);
-  if (elt == NULL || elt == CMD_LIST_AMBIGUOUS)
+  if (elt == nullptr || elt == CMD_LIST_AMBIGUOUS || *prefix_text2 != '\0')
     {
       msg = xstrprintf (_("could not find command prefix '%s'"),
                        prefix_text.get ()).release ();
index 5d98d03e3636c7f6e8250485bb7faae984deef36..c53138a525f8c4194615c882c7fd9731cce71548 100644 (file)
@@ -385,7 +385,7 @@ gdbpy_parse_command_name (const char *name,
 
   prefix_text2 = prefix_text.c_str ();
   elt = lookup_cmd_1 (&prefix_text2, *start_list, NULL, NULL, 1);
-  if (elt == NULL || elt == CMD_LIST_AMBIGUOUS)
+  if (elt == nullptr || elt == CMD_LIST_AMBIGUOUS || *prefix_text2 != '\0')
     {
       PyErr_Format (PyExc_RuntimeError, _("Could not find command prefix %s."),
                    prefix_text.c_str ());
index 9caca247cb39bf8b82e2f93ec885dc8fd13b0350..3709cb19042c78fa92437246b18c14654b478b88 100644 (file)
@@ -71,6 +71,65 @@ gdb_test_multiline "input subcommand" \
 
 gdb_test "prefix-cmd subcmd ugh" "subcmd output, arg = ugh" "call subcmd"
 
+# Create a sub-command using a partial, but still unique, prefix.
+
+gdb_test_multiline "sub-command using partial prefix" \
+    "guile" "" \
+    "(register-command! (make-command \"prefix subcmd2\"" "" \
+    "  #:command-class COMMAND_OBSCURE" "" \
+    "  #:invoke (lambda (self arg from-tty)" "" \
+    "    (display (format #f \"subcmd2 output, arg = ~a\\n\" arg)))))" "" \
+    "end" ""
+
+gdb_test "prefix-cmd subcmd2 ugh" "subcmd2 output, arg = ugh" "call subcmd2"
+
+# Now create a second prefix, similar to the first.
+
+gdb_test_multiline "create prefix-xxx prefix command" \
+    "guile" "" \
+    "(register-command! (make-command \"prefix-xxx\"" "" \
+    "  #:command-class COMMAND_OBSCURE" "" \
+    "  #:completer-class COMPLETE_NONE" "" \
+    "  #:prefix? #t))" "" \
+    "end" ""
+
+# Now create a sub-command using an ambiguous prefix.
+
+gdb_test_multiline "sub-command using ambiguous partial prefix" \
+    "guile" "" \
+    "(register-command! (make-command \"prefix subcmd3\"" "" \
+    "  #:command-class COMMAND_OBSCURE" "" \
+    "  #:invoke (lambda (self arg from-tty)" "" \
+    "    (display (format #f \"subcmd3 output, arg = ~a\\n\" arg)))))" "" \
+    "end" \
+    [multi_line \
+        "Out of range: could not find command prefix 'prefix' in position 1: \"prefix subcmd3\"" \
+        "Error while executing Scheme code\\."]
+
+# Check for errors when creating a command with an unknown prefix.
+
+gdb_test_multiline "try to create 'unknown-prefix subcmd'" \
+    "guile" "" \
+    "(register-command! (make-command \"unknown-prefix subcmd\"" "" \
+    "  #:command-class COMMAND_OBSCURE" "" \
+    "  #:invoke (lambda (self arg from-tty)" "" \
+    "    (display \"called unknown-prefix subcmd\"))))" "" \
+    "end" \
+    [multi_line \
+        "Out of range: could not find command prefix 'unknown-prefix' in position 1: \"unknown-prefix subcmd\"" \
+        "Error while executing Scheme code\\."]
+
+gdb_test_multiline "try to create 'prefix-cmd unknown-prefix subcmd'" \
+    "guile" "" \
+    "(register-command! (make-command \"prefix-cmd unknown-prefix subcmd\"" "" \
+    "  #:command-class COMMAND_OBSCURE" "" \
+    "  #:invoke (lambda (self arg from-tty)" "" \
+    "    (display \"called prefix-cmd unknown-prefix subcmd\"))))" "" \
+    "end" \
+    [multi_line \
+        "Out of range: could not find command prefix 'prefix-cmd unknown-prefix' in position 1: \"prefix-cmd unknown-prefix subcmd\"" \
+        "Error while executing Scheme code\\."]
+
 # Test a subcommand in an existing GDB prefix.
 
 gdb_test_multiline "input new subcommand" \
index 8ab5d939e2ab8aceab1a9ca16c1b2bf1481f6808..94db5a7a12ff34e0bc08a8904c0b1389638731d9 100644 (file)
@@ -388,6 +388,51 @@ with_test_prefix "previously-ambiguous" {
     gdb_test "help set print" "set print s -- This command is not documented.*" "general help"
 }
 
+gdb_test_multiline "create set/show foo1 prefix commands" \
+    "guile" "" \
+    "(register-command! (make-command \"set foo1\"" "" \
+    "   #:command-class COMMAND_OBSCURE" "" \
+    "   #:prefix? #t))" "" \
+    "(register-command! (make-command \"show foo1\"" "" \
+    "   #:command-class COMMAND_OBSCURE" "" \
+    "   #:prefix? #t))" "" \
+    "end"
+
+gdb_test_multiline "create 'foo bar' parameter" \
+    "guile" "" \
+    "(register-parameter! (make-parameter \"foo bar\"" "" \
+    "   #:command-class COMMAND_OBSCURE" "" \
+    "   #:parameter-type PARAM_BOOLEAN" "" \
+    "   #:show-func (lambda (self value)" "" \
+    "      (format #f \"The state of 'foo bar' is ~a.\" value))" "" \
+    "   #:initial-value #t))" "" \
+    "end"
+
+gdb_test "show foo1 bar" "^The state of 'foo bar' is on\\." "show parameter 'foo bar'"
+
+gdb_test_multiline "create set/show foo2 prefix commands" \
+    "guile" "" \
+    "(register-command! (make-command \"set foo2\"" "" \
+    "   #:command-class COMMAND_OBSCURE" "" \
+    "   #:prefix? #t))" "" \
+    "(register-command! (make-command \"show foo2\"" "" \
+    "   #:command-class COMMAND_OBSCURE" "" \
+    "   #:prefix? #t))" "" \
+    "end" ""
+
+gdb_test_multiline "create ambiguous 'foo baz' parameter" \
+    "guile" "" \
+    "(register-parameter! (make-parameter \"foo baz\"" "" \
+    "   #:command-class COMMAND_OBSCURE" "" \
+    "   #:parameter-type PARAM_BOOLEAN" "" \
+    "   #:show-func (lambda (self value)" "" \
+    "      (format #f \"The state of 'foo baz' is ~a.\" value))" "" \
+    "   #:initial-value #t))" "" \
+    "end" \
+    [multi_line \
+        "Out of range: could not find command prefix 'foo' in position 1: \"foo baz\"" \
+        "Error while executing Scheme code."]
+
 rename scm_param_test_maybe_no_output ""
 
 # Test a color parameter.
index f76c17622fa2656c451578647e5bb829c5f942e1..5ed52a2fc67f63f3fc5bef5353d7f48b2d4d1c01 100644 (file)
@@ -328,4 +328,31 @@ proc_with_prefix test_command_redefining_itself {} {
        "call command redefining itself 2"
 }
 
+# Try to create commands using unknown prefixes and check GDB gives an
+# error.  There's also a test in here for an ambiguous prefix, which
+# gives the same error.
+proc_with_prefix test_unknown_prefix {} {
+    clean_restart
+
+    gdb_test_no_output "python gdb.Command('foo1', gdb.COMMAND_NONE, prefix=True)"
+    gdb_test_no_output "python gdb.Command('foo cmd', gdb.COMMAND_NONE)"
+
+    foreach prefix { "xxx" "foo xxx" "foo1 xxx" } {
+       gdb_test "python gdb.Command('$prefix cmd', gdb.COMMAND_NONE)" \
+           [multi_line \
+                "Python Exception <class 'RuntimeError'>: Could not find command prefix $prefix\\." \
+                "Error occurred in Python: Could not find command prefix $prefix\\."]
+    }
+
+    gdb_test_no_output "python gdb.Command('foo2', gdb.COMMAND_NONE, prefix=True)"
+
+    foreach prefix { "foo" "foo xxx" "foo1 xxx" "foo2 xxx" } {
+       gdb_test "python gdb.Command('$prefix cmd2', gdb.COMMAND_NONE)" \
+           [multi_line \
+                "Python Exception <class 'RuntimeError'>: Could not find command prefix $prefix\\." \
+                "Error occurred in Python: Could not find command prefix $prefix\\."]
+    }
+}
+
 test_command_redefining_itself
+test_unknown_prefix
index c15bef15a2bb336d2a559ac4a850b6f3c26b0ddb..39a24ca1663ea4f0da1a4864f76974d29758937f 100644 (file)
@@ -669,6 +669,43 @@ proc_with_prefix test_ambiguous_parameter {} {
        "Parameter .* is ambiguous.*Error occurred in Python.*"
     gdb_test "python print(gdb.parameter('test-ambiguous-value-1a'))" \
        "Could not find parameter.*Error occurred in Python.*"
+
+    # Create command prefixs 'set foo1' and 'show foo1'.
+    gdb_test_no_output "python gdb.Command('set foo1', gdb.COMMAND_NONE, prefix=True)"
+    gdb_test_no_output "python gdb.Command('show foo1', gdb.COMMAND_NONE, prefix=True)"
+
+    # Create a parameter under 'foo1', but use a truncated prefix.  At
+    # this point though, the prefix is not ambiguous.
+    gdb_test_no_output "python gdb.Parameter('foo bar', gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)"
+    gdb_test "python print(gdb.parameter('foo1 bar'))" "False"
+
+    # Create another prefix command, similar in name to the first.
+    gdb_test_no_output "python gdb.Command('set foo2', gdb.COMMAND_NONE, prefix=True)"
+    gdb_test_no_output "python gdb.Command('show foo2', gdb.COMMAND_NONE, prefix=True)"
+
+    # An attempt to create a parameter using an ambiguous prefix will give an error.
+    gdb_test "python gdb.Parameter('foo baz', gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" \
+       [multi_line \
+            "Python Exception <class 'RuntimeError'>: Could not find command prefix foo\\." \
+            "Error occurred in Python: Could not find command prefix foo\\."]
+}
+
+# Check that creating a gdb.Parameter with an unknown command prefix results in an error.
+proc_with_prefix test_unknown_prefix {} {
+    gdb_test_multiline "create parameter" \
+       "python" "" \
+       "class UnknownPrefixParam(gdb.Parameter):" "" \
+       "   def __init__ (self, name):" "" \
+       "      super().__init__ (name, gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \
+       "      self.value = True" "" \
+       "end"
+
+    foreach prefix { "unknown-prefix" "style unknown-prefix" "style disassembler unknown-prefix"} {
+       gdb_test "python UnknownPrefixParam('$prefix new-param')" \
+           [multi_line \
+                "Python Exception <class 'RuntimeError'>: Could not find command prefix $prefix\\." \
+                "Error occurred in Python: Could not find command prefix $prefix\\."]
+    }
 }
 
 test_directories
@@ -685,5 +722,6 @@ test_integer_parameter
 test_throwing_parameter
 test_language
 test_ambiguous_parameter
+test_unknown_prefix
 
 rename py_param_test_maybe_no_output ""