Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
From: Simon Marchi via Gdb-patches <gdb-patches@sourceware.org>
To: gdb-patches@sourceware.org
Cc: Simon Marchi <simon.marchi@efficios.com>
Subject: [PATCH 2/4] gdb: make remote target clear its async event handler in wait
Date: Mon, 30 Nov 2020 11:52:49 -0500	[thread overview]
Message-ID: <20201130165251.830482-3-simon.marchi@efficios.com> (raw)
In-Reply-To: <20201130165251.830482-1-simon.marchi@efficios.com>

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
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.  */
-      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);
 }
 
 int
-- 
2.29.2


  parent reply	other threads:[~2020-11-30 16:52 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 ` Simon Marchi via Gdb-patches [this message]
2020-12-24 17:23   ` [PATCH 2/4] gdb: make remote target clear its async event handler in wait Andrew Burgess
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=20201130165251.830482-3-simon.marchi@efficios.com \
    --to=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