Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
From: Andrew Burgess <andrew.burgess@embecosm.com>
To: Simon Marchi <simon.marchi@efficios.com>
Cc: gdb-patches@sourceware.org
Subject: Re: [PATCH 2/4] gdb: make remote target clear its async event handler in wait
Date: Thu, 24 Dec 2020 17:23:18 +0000	[thread overview]
Message-ID: <20201224172318.GP2945@embecosm.com> (raw)
In-Reply-To: <20201130165251.830482-3-simon.marchi@efficios.com>

* Simon Marchi via Gdb-patches <gdb-patches@sourceware.org> [2020-11-30 11:52:49 -0500]:

> The remote target's remote_async_inferior_event_token field is a flag
> that tells when it has an event to report and it wants its wait method
> to be called so it can report it.  The flag in cleared the

"The flag is cleared in the ...."

> async_event_handler's callback (remote_async_inferior_event_handler),
> just before calling inferior_event_handler.  However, since
> inferior_event_handler may actually call another target's wait method,
> we are not sure if the remote target may still have an event to report,
> so we need check if we need to re-raise the flag after the
> inferior_event_handler call.
> 
> For various reasons, inferior_event_handler can lead to the remote
> target getting closed and deleted.  This can be because of an inferior
> exit, or a serial communication failure.  That leads to a
> use-after-free:
> 
>     static void
>     remote_async_inferior_event_handler (async_event_handler *handler,
>     				     gdb_client_data data)
>     {
>       clear_async_event_handler (handler);
>       inferior_event_handler (INF_REG_EVENT);  <== remote_target gets deleted
> 
>       remote_target *remote = (remote_target *) data;
>       remote_state *rs = remote->get_remote_state ();  <== dereferencing the deleted object
> 
> This is PR26614.  To fix this, move the responsibility of clearing the
> remote_async_inferior_event_token flag to remote_target::wait.  Upon
> return remote_target::wait should now leave the flag cleared or marked,
> depending on whether it has subsequent event to report.
> 
> In the case where remote_async_inferior_event_handler gets called but
> infrun calls another target's wait method, the flag just naturally
> stays marked because nothing touches it.
> 
> Note that this logic is already partially implemented in
> remote_target::wait, since the remote target may have multiple events to
> report (and it can only report one at the time):
> 
>   if (target_is_async_p ())
>     {
>       remote_state *rs = get_remote_state ();
> 
>       /* If there are are events left in the queue tell the event loop
> 	 to return here.  */
>       if (!rs->stop_reply_queue.empty ())
> 	mark_async_event_handler (rs->remote_async_inferior_event_token);
>     }
> 
> However, the code in remote_async_inferior_event_handler checks for for
> pending events in addition to whether the stop reply queue is empty, so
> I've made remote_target::wait check for that as well.  I'm not
> completely sure this is ok, since I don't understand very well yet how
> the pending events mechanism works.  But I figured it was safer to do
> this in order to avoid missing events, worst case it just leads to
> unnecessary calls to remote_target::wait.
> 
> gdb/ChangeLog:
> 
> 	PR gdb/26614
> 	* remote.c (remote_target::wait): Clear async event handler at
> 	beginning, mark if needed at the end.
> 	(remote_async_inferior_event_handler): Don't set or clear async
> 	event handler.
> 
> Change-Id: I20117f5b5acc8a9972c90f16280249b766c1bf37
> ---
>  gdb/remote.c | 26 +++++++++-----------------
>  1 file changed, 9 insertions(+), 17 deletions(-)
> 
> diff --git a/gdb/remote.c b/gdb/remote.c
> index 23c1bab0b27..30694e99b31 100644
> --- a/gdb/remote.c
> +++ b/gdb/remote.c
> @@ -8000,6 +8000,13 @@ ptid_t
>  remote_target::wait (ptid_t ptid, struct target_waitstatus *status,
>  		     target_wait_flags options)
>  {
> +  remote_state *rs = get_remote_state ();
> +
> +  /* Start by clearing the flag that asks for our wait method to be called,
> +     we'll mark it again at the end if needed.  */
> +  if (target_is_async_p ())
> +    clear_async_event_handler (rs->remote_async_inferior_event_token);
> +
>    ptid_t event_ptid;
>  
>    if (target_is_non_stop_p ())
> @@ -8009,11 +8016,10 @@ remote_target::wait (ptid_t ptid, struct target_waitstatus *status,
>  
>    if (target_is_async_p ())
>      {
> -      remote_state *rs = get_remote_state ();
> -
>        /* If there are are events left in the queue tell the event loop
>  	 to return here.  */

It feels like this comment should be updated something like:

  /* If there are events left in the queue, or unacknowledged
      notifications, then tell the event loop to return here.  */

> -      if (!rs->stop_reply_queue.empty ())
> +      if (!rs->stop_reply_queue.empty ()
> +	  || rs->notif_state->pending_event[notif_client_stop.id] != nullptr)
>  	mark_async_event_handler (rs->remote_async_inferior_event_token);
>      }
>  
> @@ -14174,21 +14180,7 @@ remote_async_serial_handler (struct serial *scb, void *context)
>  static void
>  remote_async_inferior_event_handler (gdb_client_data data)
>  {
> -  remote_target *remote = (remote_target *) data;
> -  remote_state *rs = remote->get_remote_state ();
> -  clear_async_event_handler (rs->remote_async_inferior_event_token);
> -
>    inferior_event_handler (INF_REG_EVENT);
> -
> -  /* inferior_event_handler may have consumed an event pending on the
> -     infrun side without calling target_wait on the REMOTE target, or
> -     may have pulled an event out of a different target.  Keep trying
> -     for this remote target as long it still has either pending events
> -     or unacknowledged notifications.  */
> -
> -  if (rs->notif_state->pending_event[notif_client_stop.id] != NULL
> -      || !rs->stop_reply_queue.empty ())
> -    mark_async_event_handler (rs->remote_async_inferior_event_token);

This code was all from commit 96118d114e3c5.  If you check that commit
for the file remote.c you'll see one extra hunk:

  diff --git a/gdb/remote.c b/gdb/remote.c
  index f7f99dc24fe..59075cb09f2 100644
  --- a/gdb/remote.c
  +++ b/gdb/remote.c
  @@ -5605,7 +5605,7 @@ remote_target::open_1 (const char *name, int from_tty, int extended_p)

     /* Register extra event sources in the event loop.  */
     rs->remote_async_inferior_event_token
  -    = create_async_event_handler (remote_async_inferior_event_handler, NULL);
  +    = create_async_event_handler (remote_async_inferior_event_handler, remote);
     rs->notif_state = remote_notif_state_allocate (remote);

     /* Reset the target state; these things will be queried either by

I think this should be reverted as part of this commit too.  Passing
the remote through as the gdb_client_data parameter is bad
(i.e. allows for use after free situations), so lets not do that.

Otherwise, LGTM.

Thanks,
Andreq

>  }
>  
>  int
> -- 
> 2.29.2
> 

  reply	other threads:[~2020-12-24 17:23 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-11-30 16:52 [PATCH 0/4] Clear target async event handlers in wait method Simon Marchi via Gdb-patches
2020-11-30 16:52 ` [PATCH 1/4] gdb: make async event handlers clear themselves Simon Marchi via Gdb-patches
2020-12-24 17:26   ` Andrew Burgess
2021-02-04 17:42   ` Pedro Alves
2021-02-04 18:15     ` Simon Marchi via Gdb-patches
2020-11-30 16:52 ` [PATCH 2/4] gdb: make remote target clear its async event handler in wait Simon Marchi via Gdb-patches
2020-12-24 17:23   ` Andrew Burgess [this message]
2020-12-24 17:44     ` Simon Marchi via Gdb-patches
2021-02-04 18:00   ` Pedro Alves
2021-02-04 18:34     ` Simon Marchi via Gdb-patches
2020-11-30 16:52 ` [PATCH 3/4] gdb: make record-btrace " Simon Marchi via Gdb-patches
2021-01-06  9:50   ` Andrew Burgess
2020-11-30 16:52 ` [PATCH 4/4] gdb: make record-full " Simon Marchi via Gdb-patches
2021-01-06  9:51   ` Andrew Burgess
2020-12-23 21:31 ` [PATCH 0/4] Clear target async event handlers in wait method Simon Marchi via Gdb-patches

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=20201224172318.GP2945@embecosm.com \
    --to=andrew.burgess@embecosm.com \
    --cc=gdb-patches@sourceware.org \
    --cc=simon.marchi@efficios.com \
    /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