public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
From: Philippe Waroquiers <philippe.waroquiers@skynet.be>
To: gdb-patches@sourceware.org
Subject: Re: [RFA] Fix use after free caused by redefining a python prefix cmd that has aliases.
Date: Tue, 10 Jan 2023 00:37:47 +0100	[thread overview]
Message-ID: <23959381458f70e6c7fa2a4a1880bdba7e39b0cd.camel@skynet.be> (raw)
In-Reply-To: <20221226170823.2744157-1-philippe.waroquiers@skynet.be>

Ping ?
Thanks
Philippe

On Mon, 2022-12-26 at 18:08 +0100, Philippe Waroquiers wrote:
> GDB gets a segfault when redefining a python prefix command that has aliases.
> This is reproduced by running the modified py-cmd.exp (see SEGV stacktrace
> below and some valgring reported errors).
> 
> Fix the problem by ensuring the aliases of the redefined python command get a
> copy of all the new member values of the redefined command.
> 
> Tested on amd64/debian. Also run all tests under valgrind.
> 
> Fatal signal: Segmentation fault
>   ----- Backtrace -----
>   0x564d0f2cff7e gdb_internal_backtrace_1
>           ../../termours/gdb/bt-utils.c:122
>   0x564d0f2d0023 _Z22gdb_internal_backtracev
>           ../../termours/gdb/bt-utils.c:168
>   0x564d0f4bc969 handle_fatal_signal
>           ../../termours/gdb/event-top.c:956
>   0x564d0f4bcae4 handle_sigsegv
>           ../../termours/gdb/event-top.c:1029
>   0x7fb82fe0613f ???
>           ./nptl/../sysdeps/unix/sysv/linux/x86_64/sigaction.c:0
>   0x564d0f3290a5 lookup_cmd_with_subcommands
>           ../../termours/gdb/cli/cli-decode.c:80
>   0x564d0f3290b6 lookup_cmd_with_subcommands
>           ../../termours/gdb/cli/cli-decode.c:80
>   0x564d0f329647 do_add_cmd
>           ../../termours/gdb/cli/cli-decode.c:225
>   0x564d0f32968f _Z7add_cmdPKc13command_classS0_PP16cmd_list_element
>           ../../termours/gdb/cli/cli-decode.c:236
>   0x564d0f6965e4 cmdpy_init
>           ../../termours/gdb/python/py-cmd.c:520
>   0x564d0fb4a87b wrap_init
> 
> Valgrind reports:
>   ==1309561== Invalid read of size 8
>   ==1309561==    at 0x48F12E: lookup_cmd_with_subcommands (cli-decode.c:80)
>   ==1309561==    by 0x48F12E: do_add_cmd(char const*, command_class, char const*, cmd_list_element**) (cli-decode.c:225)
>   ==1309561==    by 0x4906D9: add_cmd (cli-decode.c:247)
>   ==1309561==    by 0x4906D9: add_prefix_cmd(char const*, command_class, void (*)(char const*, int), char const*, cmd_list_element**, int, cmd_list_element**) (cli-decode.c:362)
>   ==1309561==    by 0x6C04DD: cmdpy_init(_object*, _object*, _object*) (py-cmd.c:514)
>   ==1309561==    by 0xA14D2B: wrap_init (typeobject.c:5993)
>   ==1309561==    by 0x9C663F: _PyObject_MakeTpCall (call.c:159)
>   ==1309561==    by 0x3B4052: _PyObject_Vectorcall (abstract.h:125)
>   ==1309561==    by 0x3B4052: _PyObject_Vectorcall (abstract.h:115)
>   ==1309561==    by 0x3B4052: call_function (ceval.c:4987)
>   ...
>   ==1309561==  Address 0x7808f38 is 40 bytes inside a block of size 64 free'd
>   ==1309561==    at 0x483BF4C: free (vg_replace_malloc.c:884)
>   ==1309561==    by 0xA155AA: subtype_dealloc (typeobject.c:1287)
>   ==1309561==    by 0x6BF4E7: _Py_DECREF (object.h:478)
>   ==1309561==    by 0x6BF4E7: decref (py-ref.h:36)
>   ==1309561==    by 0x6BF4E7: ~ref_ptr (gdb_ref_ptr.h:91)
>   ==1309561==    by 0x6BF4E7: cmdpy_destroyer(cmd_list_element*, void*) (py-cmd.c:96)
>   ==1309561==    by 0x48EEFF: delete_cmd (cli-decode.c:1253)
>   ==1309561==    by 0x48EEFF: do_add_cmd(char const*, command_class, char const*, cmd_list_element**) (cli-decode.c:190)
>   ==1309561==    by 0x4906D9: add_cmd (cli-decode.c:247)
>   ==1309561==    by 0x4906D9: add_prefix_cmd(char const*, command_class, void (*)(char const*, int), char const*, cmd_list_element**, int, cmd_list_element**) (cli-decode.c:362)
>   ==1309561==    by 0x6C04DD: cmdpy_init(_object*, _object*, _object*) (py-cmd.c:514)
>   ==1309561==    by 0xA14D2B: wrap_init (typeobject.c:5993)
>   ==1309561==    by 0x9C663F: _PyObject_MakeTpCall (call.c:159)
>   ==1309561==    by 0x3B4052: _PyObject_Vectorcall (abstract.h:125)
>   ...
>   ==1309561==  Block was alloc'd at
>   ==1309561==    at 0x483979B: malloc (vg_replace_malloc.c:393)
>   ==1309561==    by 0xAD2C4D: _PyObject_GC_Alloc (gcmodule.c:1964)
>   ==1309561==    by 0xAD2C4D: _PyObject_GC_Malloc (gcmodule.c:1987)
>   ==1309561==    by 0xA16DE9: PyType_GenericAlloc (typeobject.c:1010)
>   ==1309561==    by 0xA18F44: type_call (typeobject.c:969)
>   ==1309561==    by 0x9C663F: _PyObject_MakeTpCall (call.c:159)
>   ==1309561==    by 0x3B069F: _PyObject_Vectorcall (abstract.h:125)
>   ==1309561==    by 0x3B069F: _PyObject_Vectorcall (abstract.h:115)
>   ==1309561==    by 0x3B069F: call_function (ceval.c:4987)
>   ==1309561==    by 0x3B069F: _PyEval_EvalFrameDefault (ceval.c:3500)
>   ==1309561==    by 0xA75253: PyEval_EvalFrameEx (ceval.c:741)
>   ==1309561==    by 0xA75253: _PyEval_EvalCodeWithName (ceval.c:4298)
>   ==1309561==    by 0xA755B2: PyEval_EvalCodeEx (ceval.c:4327)
>   ==1309561==    by 0xA755B2: PyEval_EvalCode (ceval.c:718)
>   ==1309561==    by 0xAB1402: run_eval_code_obj (pythonrun.c:1117)
>   ==1309561==    by 0xAB1402: run_mod (pythonrun.c:1139)
>   ==1309561==    by 0xAB27F0: PyRun_StringFlags (pythonrun.c:1026)
>   ==1309561==    by 0xAB27F0: PyRun_SimpleStringFlags (pythonrun.c:460)
>   ==1309561==    by 0x6F689B: gdbpy_eval_from_control_command(extension_language_defn const*, command_line*) (python.c:434)
>   ==1309561==    by 0x49F54C: execute_control_command_1(command_line*, int) (cli-script.c:683)
> ---
>  gdb/cli/cli-decode.c                     | 19 ++++++++++------
>  gdb/python/py-cmd.c                      |  9 +++++++-
>  gdb/testsuite/gdb.python/py-cmd-alias.py | 28 ++++++++++++++++++++++++
>  gdb/testsuite/gdb.python/py-cmd.exp      | 17 ++++++++++++++
>  4 files changed, 65 insertions(+), 8 deletions(-)
>  create mode 100644 gdb/testsuite/gdb.python/py-cmd-alias.py
> 
> diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c
> index 7c98029f9f4..132b0ca659a 100644
> --- a/gdb/cli/cli-decode.c
> +++ b/gdb/cli/cli-decode.c
> @@ -179,18 +179,26 @@ cmd_list_element::command_components () const
>  
> 
>  static struct cmd_list_element *
>  do_add_cmd (const char *name, enum command_class theclass,
> +	    cmd_simple_func_ftype *fun,
>  	    const char *doc, struct cmd_list_element **list)
>  {
>    struct cmd_list_element *c = new struct cmd_list_element (name, theclass,
>  							    doc);
>  
> 
> +  set_cmd_simple_func (c, fun);
> +
>    /* Turn each alias of the old command into an alias of the new
>       command.  */
>    c->aliases = delete_cmd (name, list, &c->hook_pre, &c->hookee_pre,
>  			   &c->hook_post, &c->hookee_post);
> -
>    for (cmd_list_element &alias : c->aliases)
> -    alias.alias_target = c;
> +    {
> +      alias.func = c->func;
> +      alias.function = c->function;
> +      alias.subcommands = c->subcommands;
> +      alias.allow_unknown = c->allow_unknown;
> +      alias.alias_target = c;
> +    }
>  
> 
>    if (c->hook_pre)
>      c->hook_pre->hookee_pre = c;
> @@ -233,9 +241,7 @@ struct cmd_list_element *
>  add_cmd (const char *name, enum command_class theclass,
>  	 const char *doc, struct cmd_list_element **list)
>  {
> -  cmd_list_element *result = do_add_cmd (name, theclass, doc, list);
> -  result->func = NULL;
> -  result->function.simple_func = NULL;
> +  cmd_list_element *result = do_add_cmd (name, theclass, NULL, doc, list);
>    return result;
>  }
>  
> 
> @@ -244,8 +250,7 @@ add_cmd (const char *name, enum command_class theclass,
>  	 cmd_simple_func_ftype *fun,
>  	 const char *doc, struct cmd_list_element **list)
>  {
> -  cmd_list_element *result = do_add_cmd (name, theclass, doc, list);
> -  set_cmd_simple_func (result, fun);
> +  cmd_list_element *result = do_add_cmd (name, theclass, fun, doc, list);
>    return result;
>  }
>  
> 
> diff --git a/gdb/python/py-cmd.c b/gdb/python/py-cmd.c
> index 5cc392af175..d66311d623b 100644
> --- a/gdb/python/py-cmd.c
> +++ b/gdb/python/py-cmd.c
> @@ -524,8 +524,15 @@ cmdpy_init (PyObject *self, PyObject *args, PyObject *kw)
>           name_allocated, so release it.  */
>        cmd_name.release ();
>  
> 
> -      /* There appears to be no API to set this.  */
> +      /* There appears to be no API to set the below members.
> +	 In particular, we cannot pass cdmpy_function to add_prefix_cmd and
> +	 add_cmd above.  This means that if cmd has aliases, its aliases have
> +	 been re-targeted to this freshly built CMD with a null func and
> +	 function.simple_func (see cli-decode.c do_add_cmd).  So, we need to set
> +	 the 'func' of the aliases of CMD.  */
>        cmd->func = cmdpy_function;
> +      for (cmd_list_element &alias : cmd->aliases)
> +	alias.func = cmd->func;
>        cmd->destroyer = cmdpy_destroyer;
>        cmd->doc_allocated = 1;
>        cmd->name_allocated = 1;
> diff --git a/gdb/testsuite/gdb.python/py-cmd-alias.py b/gdb/testsuite/gdb.python/py-cmd-alias.py
> new file mode 100644
> index 00000000000..d62628841f4
> --- /dev/null
> +++ b/gdb/testsuite/gdb.python/py-cmd-alias.py
> @@ -0,0 +1,28 @@
> +class some_prefix_cmd (gdb.Command):
> +  def __init__ (self):
> +    super (some_prefix_cmd, self).__init__ ("some_prefix_cmd", gdb.COMMAND_OBSCURE, gdb.COMPLETE_NONE, True)
> +  def invoke (self, arg, from_tty):
> +    print ("some_prefix_cmd output, arg = %s" % arg)
> +
> +some_prefix_cmd ()
> +
> +def def_alias(alias : str, command_name : str) -> None:
> +    """Defines an alias -a ALIAS = COMMAND_NAME.
> +Traps the error if ALIAS is already defined (so as to be able to source
> +this file again)."""
> +    d = "alias " + alias + ' = ' + command_name
> +    try:
> +        gdb.execute (d)
> +    except Exception as inst:
> +        print(str(inst))
> +
> +
> +def_alias("alias_of_some_prefix_cmd", "some_prefix_cmd")
> +def_alias("other_alias_of_some_prefix_cmd", "some_prefix_cmd")
> +
> +class some_subcmd (gdb.Command):
> +  def __init__ (self):
> +    super (some_subcmd, self).__init__ ("some_prefix_cmd some_subcmd", gdb.COMMAND_OBSCURE)
> +  def invoke (self, arg, from_tty):
> +    print ("some_subcmd output, arg = %s" % arg)
> +some_subcmd ()
> diff --git a/gdb/testsuite/gdb.python/py-cmd.exp b/gdb/testsuite/gdb.python/py-cmd.exp
> index 48c3e18f1cc..c7398889fd4 100644
> --- a/gdb/testsuite/gdb.python/py-cmd.exp
> +++ b/gdb/testsuite/gdb.python/py-cmd.exp
> @@ -111,6 +111,23 @@ gdb_test_multiline "input new subcommand" \
>  
> 
>  gdb_test "info newsubcmd ugh" "newsubcmd output, arg = ugh" "call newsubcmd"
>  
> 
> +# Test sourcing twice a file that redefines a prefix command with aliases.
> +set pyfile [gdb_remote_download host ${srcdir}/${subdir}/py-cmd-alias.py]
> +foreach initial_load {1 0} {
> +    if $initial_load {
> +	gdb_test_no_output "source ${pyfile}" "load python file"
> +    } else {
> +	gdb_test "source ${pyfile}" \
> +	    "Alias already exists: alias_of_some_prefix_cmd\r\nAlias already exists: other_alias_of_some_prefix_cmd" \
> +	    "load again python file"
> +    }
> +    foreach cmd {"some_prefix_cmd" "alias_of_some_prefix_cmd" "other_alias_of_some_prefix_cmd"} {
> +	gdb_test "$cmd ugh $initial_load" \
> +	    "some_prefix_cmd output, arg = ugh $initial_load" \
> +	    "call $cmd $initial_load"
> +    }
> +}
> +
>  # Test a command that throws gdb.GdbError.
>  
> 
>  gdb_test_multiline "input command to throw error" \



  reply	other threads:[~2023-01-09 23:37 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-12-26 17:08 Philippe Waroquiers
2023-01-09 23:37 ` Philippe Waroquiers [this message]
2023-01-10 12:16 ` Andrew Burgess
2023-01-11 10:58   ` Philippe Waroquiers

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=23959381458f70e6c7fa2a4a1880bdba7e39b0cd.camel@skynet.be \
    --to=philippe.waroquiers@skynet.be \
    --cc=gdb-patches@sourceware.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).