Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
From: Kevin Buettner <kevinb@redhat.com>
To: Andrew Burgess <aburgess@redhat.com>
Cc: gdb-patches@sourceware.org
Subject: Re: [PATCH] gdb: pass inferior argument to inf_child_target::maybe_unpush_target
Date: Thu, 14 May 2026 13:17:05 -0700	[thread overview]
Message-ID: <20260514131705.3d99bdfd@f42-zbm-amd> (raw)
In-Reply-To: <4a06ca5986e36021a3ca4c47020cd3f8daee16a7.1778600715.git.aburgess@redhat.com>

On Tue, 12 May 2026 16:45:25 +0100
Andrew Burgess <aburgess@redhat.com> wrote:

> Bug PR gdb/29944 highlights an issue where this assertion can trigger:
> 
>   thread-iter.c:109: internal-error: all_matching_threads_iterator:
> Assertion `filter_target != nullptr' failed.
> 
> The problem occurs when GDB is configured with these settings:
> 
>   set detach-on-fork off
>   set schedule-multiple on
>   set non-stop on
> 
> There is a Python exit event listener registered like this:
> 
>   def exit_handler(_):
>     gdb.execute('inferior 1')
> 
>   gdb.events.exited.connect(exit_handler)
> 
> Then the user runs an inferior which forks a child process, the
> child (at some point) exits while the parent process continues
> running.  Then, at some future time, the parent process also exits.
> 
> Initially we only have inferior 1, but when this inferior forks we now
> have inferior's 1 and 2.  Due to the settings GDB follows both, and
> resumes both inferiors.
> 
> On GNU/Linux, when the child, inferior 2, exits, we eventually end up
> in inf_child_target::mourn_inferior.  This then calls
> generic_mourn_inferior which operates on the current inferior.  This
> includes calling exit_inferior, which is where the inferior_exit
> observer is notified, and it is this that runs the Python 'exit' event
> handlers.
> 
> In our case the Python exit handler runs 'inferior 1', which changes
> the current inferior.
> 
> Back in inf_child_target::mourn_inferior we now call
> maybe_unpush_target which potentially unpushes the inf_child_target
> from the target stack of the current inferior.
> 
> But notice, we already switched the current inferior to inferior 1.
> This means that we just unpushed the inf_child_target from inferior 1,
> which is still running, not inferior 2, which has exited.
> 
> Later, when inferior 1 exits we end up in normal_stop and, because we
> are in non-stop mode, we try to find the finish_ptid and finish_target
> based on the inferior which just exited.  In this case inferior 1.
> But remember, inferior 1 no longer has a process stratum target, we
> incorrectly unpushed it earlier when inferior 2 exited.
> 
> We now initialise maybe_finish_thread_state, a
> scoped_finish_thread_state object, using the finish_ptid, which is the
> ptid of inferior 1, and finish_target, which is NULL.
> 
> Eventually, later in normal_stop, the scoped_finish_thread_state runs,
> which calls finish_thread_state, which calls all_non_exited_threads.
> 
> This eventually calls
> all_matching_threads_iterator::all_matching_threads_iterator to
> iterate over the applicable threads, and, as the filter_ptid is that
> of inferior 1 (i.e. not minus_one_ptid), and filter_target is NULL,
> the assert triggers.
> 
> The solution is simple enough, have
> inf_child_target::maybe_unpush_target take the inferior to operate on,
> rather than relying on the current_inferior being correct.  It looks
> like we might have run into something like this before as in
> inf_child_target::follow_exec we temporarily change the current
> inferior back prior to calling maybe_unpush_target.  In that case it
> is GDB's follow-exec-mode that was causing the problem. See:
> 
>   commit 737358ba1ed8b28820cc965f62027bb7417b132b
>   Date:   Thu May 13 15:28:42 2021 -0400
> 
>       gdb: maybe unpush target from old inferior in
> inf_child_target::follow_exec
> 
> I did consider having inf_child_target::maybe_unpush_target perform
> the inferior switch for us, but after looking at what
> maybe_unpush_target actually does, I don't think it is really
> necessary for the current inferior to be set "correctly", given an
> inferior pointer, we can just operate on that.
> 
> So that's what I've done.  maybe_unpush_target takes an 'inferior *'
> argument, and it is that inferior from which we unpush the target.  It
> is no longer necessary to switch inferiors in follow_exec.
> 
> There's a test that exposes the original assertion failure, which
> passes with this patch.  The gdb.base/foll-exec-mode.exp test, which
> was added in commit 737358ba1ed8b288 also still passes after this
> change.
> 
> Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=29944

Thanks for that comprehensive explanation.

Approved-by: Kevin Buettner <kevinb@redhat.com>


  reply	other threads:[~2026-05-14 20:17 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-05-12 15:45 Andrew Burgess
2026-05-14 20:17 ` Kevin Buettner [this message]
2026-05-14 20:26 ` Simon Marchi
2026-05-15 10:22   ` Andrew Burgess

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=20260514131705.3d99bdfd@f42-zbm-amd \
    --to=kevinb@redhat.com \
    --cc=aburgess@redhat.com \
    --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