Fix segfault with nested download prompts

For an unknown reason, if a download prompt is triple-nested, Qt segfaults here:

    #0  add_lazy_attrs (td=0x7ffff52e0dd1 <QtPrivate::sizedFree(void*, unsigned long)+32>) at sip_core.c:6255
    #1  sip_add_all_lazy_attrs (td=0x7ffff52e0dd1 <QtPrivate::sizedFree(void*, unsigned long)+32>) at sip_core.c:6304
    #2  0x00007ffff636598e in sip_api_is_py_method_12_8 (gil=gil@entry=0x7fffffff8b8c, pymc=pymc@entry=0x7fffffff8b8b "", sipSelfp=sipSelfp@entry=0x7fffffff8ba8, cname=cname@entry=0x0, mname=mname@entry=0x7ffff636d57b "__dtor__") at sip_core.c:7402
    #3  0x00007ffff6365c2c in sip_api_is_py_method_12_8 (mname=0x7ffff636d57b "__dtor__", cname=0x0, sipSelfp=0x7fffffff8ba8, pymc=0x7fffffff8b8b "", gil=0x7fffffff8b8c) at sip_core.c:7356
    #4  callPyDtor (self=<optimized out>) at sip_core.c:5375
    #5  sip_api_instance_destroyed_ex (sipSelfp=0x7fffffff8c40) at sip_core.c:5311
    #6  0x00007ffff5fc9967 in sipQEventLoop::~sipQEventLoop() () from [...]/python3.13/site-packages/PyQt6/QtCore.abi3.so
    #7  0x00007ffff0bcd749 in QFileInfoGatherer::getInfo (this=0x5555583f9bc0, fileInfo=...) at [...]/qt5/qtbase/src/gui/itemmodels/qfileinfogatherer.cpp:349
    #8  0x00007ffff0be2629 in QFileSystemModelPrivate::fileSystemChanged (this=0x5555583f9870, path="/tmp/qbdl/download", updates=QList<std::pair<QString, QFileInfo>> (size = 3) = {...}) at [...]/qt5/qtbase/src/gui/itemmodels/qfilesystemmodel.cpp:1966
    (full stacktrace has 183 frames)

After a lot of experimentation, I figured out that moving the `NullIconProvider`
to a class variable (or removing it entirely) seems to help. Note that making it
`global` (but keeping the instanciation inside `FilenamePrompt._init_fileview()`)
did not!

This is semi-blind fix: It's unclear to me if this is a proper fix, or if the
changed memory layout just causes the issue to temporary disappear.

However, given that `QFileInfoGatherer::getInfo()` calls into the icon provider,
it might very well be the real deal.

Closes #8674
This commit is contained in:
Florian Bruhin 2025-09-21 18:07:34 +02:00
parent 18bea32975
commit c6b486ce81
2 changed files with 21 additions and 1 deletions

View File

@ -654,6 +654,12 @@ class FilenamePrompt(_BasePrompt):
"""A prompt for a filename."""
# Note: This *must* be a class variable! If it's not, for unknown reasons,
# we get a segfault in Qt/PyQt in QFileInfoGatherer::getInfo() if we have
# nested download prompts (i.e. trigger a download while a download prompt
# is open already).
_null_icon_provider = NullIconProvider()
def __init__(self, question, parent=None):
super().__init__(question, parent)
self._init_texts(question)
@ -753,7 +759,7 @@ class FilenamePrompt(_BasePrompt):
self._file_model = QFileSystemModel(self)
# avoid icon and mime type lookups, they are slow in Qt6
self._file_model.setIconProvider(NullIconProvider())
self._file_model.setIconProvider(self._null_icon_provider)
self._file_view.setModel(self._file_model)
self._file_view.clicked.connect(self._insert_path)

View File

@ -669,6 +669,20 @@ Feature: Downloading things from a website.
Then the downloaded file download.bin should exist
And the downloaded file download2.bin should not exist
Scenario: Nested download prompts (#8674)
When I set downloads.location.prompt to true
And I open data/downloads/download.bin without waiting
And I wait for "Asking question <qutebrowser.utils.usertypes.Question default='*' mode=<PromptMode.download: 5> option=None text=* title='Save file to:'>, *" in the log
And I open data/downloads/download.bin without waiting
And I wait for "Asking question <qutebrowser.utils.usertypes.Question default='*' mode=<PromptMode.download: 5> option=None text=* title='Save file to:'>, *" in the log
And I open data/downloads/download.bin without waiting
And I wait for "Asking question <qutebrowser.utils.usertypes.Question default='*' mode=<PromptMode.download: 5> option=None text=* title='Save file to:'>, *" in the log
And I run :prompt-accept
And I run :mode-leave
And I run :mode-leave
And I wait until the download is finished
Then the downloaded file download.bin should exist
@qtwebengine_skip # We can't get the UA from the page there
Scenario: user-agent when using :download
When I open user-agent