From be37c33b6657060232555d17386557145f3b6666 Mon Sep 17 00:00:00 2001 From: SulfurNitride Date: Wed, 13 May 2026 21:33:25 -0500 Subject: nxm: drain socket lines before emitting to dodge use-after-free MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit processSocketData iterated socket->canReadLine() and emit'd nxmReceived inside the loop. nxmReceived routes to OrganizerCore which can show a modal dialog (the "Wrong Game" warning fires whenever the link's game domain doesn't match the active instance). Modal dialogs spin the event loop, and the loop processes the disconnected → deleteLater queued for the same socket. The socket gets freed, then control returns to the while loop and the next canReadLine() runs on a dangling pointer. Repro hits cleanly when an nxm:// for a non-matching game arrives — the "Wrong Game" dialog appears and as soon as the user dismisses it MO2 crashes with SIGSEGV. Read into a QStringList first, then emit after the read loop ends so the socket is no longer touched once handlers run. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/src/nxmhandler_linux.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/src/nxmhandler_linux.cpp b/src/src/nxmhandler_linux.cpp index c7db527..7b70ff4 100644 --- a/src/src/nxmhandler_linux.cpp +++ b/src/src/nxmhandler_linux.cpp @@ -301,12 +301,20 @@ void NxmHandlerLinux::onNewConnection() void NxmHandlerLinux::processSocketData(QLocalSocket* socket) { + // Drain all available lines before emitting anything. Slot handlers for + // nxmReceived/directDownloadReceived can show modal dialogs (e.g. the + // "Wrong Game" warning) that spin the event loop. That re-entry would + // process the disconnected → deleteLater queued for this socket and free + // it, leaving the canReadLine() loop iterating on a dangling pointer. + QStringList lines; while (socket->canReadLine()) { const QString line = QString::fromUtf8(socket->readLine()).trimmed(); - if (line.isEmpty()) { - continue; + if (!line.isEmpty()) { + lines.append(line); } + } + for (const QString& line : lines) { log::info("received link on socket: {}", line); // Try NXM-style parse first (nxm:// or modl:// with /mods/ID/files/ID path). -- cgit v1.3.1