linuxmint / linuxmint/cinnamon
Network applet: null SSID on resume crashes `_createSection`, leaking signal handlers into an unbounded GJS GC-callback log storm that freezes the desktop
Nobody has claimed this yet.
- Dominant language
- JavaScript
- Stars
- 5.6k
- Forks
- 915
- Avg merge
- 5d 22h
- Merged PRs (30d)
- 3
Description
Summary
After suspending and resuming my laptop, and typing my password, the screen went black forever,
only the mouse cursor visible. The LLM-aided analysis of the issue is provided below.
A TypeError: ssid is null in the network applet aborts a menu rebuild partway through,
leaving Clutter actors and NetworkManager signal handlers connected but unreachable from JS.
GJS then emits "Attempting to run a JS callback during garbage collection" plus a stack dump
on every subsequent collection, forever. On my machine this produced 409 million log
messages in 65 minutes (peak ~87,000/s) and left the desktop black with a live mouse
cursor until Cinnamon was killed manually.
The crash itself is a one-line null dereference. The consequence is a full desktop
denial-of-service that a normal user cannot diagnose -- the screen is black, so there is no
way to reach a terminal without switching to a text VT.
Versions
- Cinnamon 6.0.4-4 (Ubuntu 24.04, x86_64)
- Xorg 21.1.11, session type x11, muffin/Mutter 6.0.1
- Kernel 6.17.0-1030-oem, Intel Meteor Lake (i915)
- NetworkManager, wifi device
wlp0s20f3
The relevant code is unchanged in current master (checked 2026-09-13).
How it triggers
Suspend the laptop with wifi associated, then resume. On resume NetworkManager tears the
wifi device down and back up:
device (wlp0s20f3): state change: unmanaged -> unavailable (reason 'managed', ...)
and re-scans. This is not reliably reproducible on demand -- it depends on the applet's
retained access-point list going stale across the device teardown -- but the failing path is
deterministic once an AP in a retained group returns null from get_ssid().
Evidence
The crash, 17 times within 10 seconds of resume:
szept 13 10:17:20 cinnamon[6907]: JS ERROR: TypeError: ssid is null
ssidToLabel@/usr/share/cinnamon/applets/network@cinnamon.org/applet.js:88:17
_init@/usr/share/cinnamon/applets/network@cinnamon.org/applet.js:114:21
NMNetworkMenuItem@/usr/share/cinnamon/applets/network@cinnamon.org/applet.js:95:16
_createNetworkItem@/usr/share/cinnamon/applets/network@cinnamon.org/applet.js:1662:26
_createSection@/usr/share/cinnamon/applets/network@cinnamon.org/applet.js:1708:18
_deviceStateChanged@/usr/share/cinnamon/applets/network@cinnamon.org/applet.js:673:14
Ten seconds later the GC storm starts and runs for 65 minutes. Message counts from a single
journal capture:
1847727 cinnamon.desktop[6907]: == Stack trace for context 0x613560320170 ==
944071 cinnamon[6907]: Attempting to run a JS callback during garbage collection...
944019 cinnamon[6907]: The offending callback was SourceFunc().
951 cinnamon[6907]: Attempting to call back into JSAPI during the sweeping phase of GC...
Plus what journald dropped on top of that:
222 suppression events, 408.901.590 messages dropped
The dangling handlers are overwhelmingly the network applet's own:
210 window-monitor-changed on MetaDisplay
140 access-point-added on NMDeviceWifi
102 notify on NMAccessPoint
94 access-point-removed on NMDeviceWifi
94 g-signal on GDBusProxy
Effect: the main loop spent all its time formatting and writing log messages, so muffin never
composited a frame. All X clients stayed alive and mapped (_NET_CLIENT_LIST intact, every
window IsViewable) -- the screen was simply black. After 65 minutes the JS context collapsed
entirely and the process went silent at 0% CPU, still holding WM_S0, so cinnamon --replace
hung waiting for the selection and the process had to be SIGKILLed.
Not a GPU fault: the i915 resume is clean, no hang, reset or error on any DRM line.
Analysis
Every entry point into the network list guards against a null SSID:
applet.js:1144 -- building the initial list:
if (ap.get_ssid() == null) {
// hidden access point cannot be added, ...
ap._notifySsidId = ap.connect('notify::ssid', Lang.bind(this, this._notifySsidCb));
continue;
}
applet.js:1389 -- _accessPointAdded, and applet.js:1378 -- _findNetwork: same pattern.
But nothing re-validates an AP after it has been admitted to obj.accessPoints. The consumer
is unguarded -- _createNetworkItem, applet.js:1723:
apObj.item = new NMNetworkMenuItem(apObj.accessPoints);
which reaches NMNetworkMenuItem._init, applet.js:107-113:
accessPoints = sortAccessPoints(accessPoints);
this.bestAP = accessPoints[0];
if (!title) {
let ssid = this.bestAP.get_ssid();
title = ssidToLabel(ssid);
}
and ssidToLabel, applet.js:85:
function ssidToLabel(ssid) {
let label = NM.utils_ssid_to_utf8(ssid.get_data()); // ssid may be null
if (!label)
label = _("<unknown>");
return label;
}
Two things make the guards insufficient:
- An AP admitted with a valid SSID can later return null -- across a device teardown on
resume, the applet keeps itsaccessPointsarrays while the underlying NM AP objects go
stale. sortAccessPointsorders by signal strength, sobestAPis not necessarily the AP that
was validated at insertion time. Any AP in the group can become the one dereferenced.
Note that ssidToLabel already has an "unknown network" fallback for the case where
utils_ssid_to_utf8 yields nothing -- it just never considers that the SSID object itself
may be absent.
The escalation from "one menu item fails" to "desktop unusable for an hour" is the part worth
addressing beyond the null check: an exception thrown mid-_createSection abandons
already-constructed menu items without destroy(), and GJS has no rate limiting on the
resulting GC diagnostics.
Proposed fix
Minimal, and consistent with the function's existing intent:
function ssidToLabel(ssid) {
let label = ssid ? NM.utils_ssid_to_utf8(ssid.get_data()) : null;
if (!label)
label = _("<unknown>");
return label;
}
Worth considering in addition:
- Skip access points whose SSID has become null when rebuilding in
_createNetworkItem/
NMNetworkMenuItem._init, rather than labelling them<unknown>, so stale APs drop out of
the list instead of accumulating. - Wrap the per-item work in
_createSectionso one failing item cannot abandon the rest of
the rebuild with actors still connected.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in applet.js at ssidToLabel, NMNetworkMenuItem._init, _createNetworkItem, and _createSection, then trace the existing null-SSID guards and resume-triggered rebuild path. Reproduce the suspend/resume scenario if possible and inspect the Cinnamon logs. Done means a stale access point no longer raises the reported TypeError or leaves the desktop in the described GC log storm.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript
- Domain
- desktop, networking
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 76/100