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>
next prev parent 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