From: Andrew Burgess Date: Fri, 11 Apr 2025 15:01:02 +0000 (+0100) Subject: gdb/python/guile: check for invalid prefixes in Command/Parameter creation X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=3e87f196c5c35e3e071ef0cdd14666d2c97d3f51;p=thirdparty%2Fbinutils-gdb.git gdb/python/guile: check for invalid prefixes in Command/Parameter creation 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 : 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. --- diff --git a/gdb/guile/scm-cmd.c b/gdb/guile/scm-cmd.c index 47578729425..453394ccb19 100644 --- a/gdb/guile/scm-cmd.c +++ b/gdb/guile/scm-cmd.c @@ -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 (); diff --git a/gdb/python/py-cmd.c b/gdb/python/py-cmd.c index 5d98d03e363..c53138a525f 100644 --- a/gdb/python/py-cmd.c +++ b/gdb/python/py-cmd.c @@ -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 ()); diff --git a/gdb/testsuite/gdb.guile/scm-cmd.exp b/gdb/testsuite/gdb.guile/scm-cmd.exp index 9caca247cb3..3709cb19042 100644 --- a/gdb/testsuite/gdb.guile/scm-cmd.exp +++ b/gdb/testsuite/gdb.guile/scm-cmd.exp @@ -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" \ diff --git a/gdb/testsuite/gdb.guile/scm-parameter.exp b/gdb/testsuite/gdb.guile/scm-parameter.exp index 8ab5d939e2a..94db5a7a12f 100644 --- a/gdb/testsuite/gdb.guile/scm-parameter.exp +++ b/gdb/testsuite/gdb.guile/scm-parameter.exp @@ -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. diff --git a/gdb/testsuite/gdb.python/py-cmd.exp b/gdb/testsuite/gdb.python/py-cmd.exp index f76c17622fa..5ed52a2fc67 100644 --- a/gdb/testsuite/gdb.python/py-cmd.exp +++ b/gdb/testsuite/gdb.python/py-cmd.exp @@ -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 : 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 : Could not find command prefix $prefix\\." \ + "Error occurred in Python: Could not find command prefix $prefix\\."] + } +} + test_command_redefining_itself +test_unknown_prefix diff --git a/gdb/testsuite/gdb.python/py-parameter.exp b/gdb/testsuite/gdb.python/py-parameter.exp index c15bef15a2b..39a24ca1663 100644 --- a/gdb/testsuite/gdb.python/py-parameter.exp +++ b/gdb/testsuite/gdb.python/py-parameter.exp @@ -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 : 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 : 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 ""