Skip to content

fix: value-initialize NOTIFYICONDATA and clear hIcon after destroy - #103

Open
JulianPscheid wants to merge 1 commit into
leanflutter:mainfrom
JulianPscheid:fix/windows-notifyicondata-init
Open

fix: value-initialize NOTIFYICONDATA and clear hIcon after destroy#103
JulianPscheid wants to merge 1 commit into
leanflutter:mainfrom
JulianPscheid:fix/windows-notifyicondata-init

Conversation

@JulianPscheid

@JulianPscheid JulianPscheid commented Jul 31, 2026

Copy link
Copy Markdown

Two problems in the Windows plugin, both stemming from nid never being initialized.

Uninitialized szTip becomes the tooltip

nid and niif are declared without initializers. On the first setIcon() call, _ApplyIcon() copies nid.szTip into a backup before it zeroes the struct, restores it afterwards, then sets NIF_TIP if the first byte is non-null:

WCHAR szTipBackup[128];
StringCchCopy(szTipBackup, _countof(szTipBackup), nid.szTip);

ZeroMemory(&nid, sizeof(NOTIFYICONDATA));
...
StringCchCopy(nid.szTip, _countof(nid.szTip), szTipBackup);
nid.uFlags = NIF_MESSAGE | NIF_ICON;
if (nid.szTip[0] != '\0') {
  nid.uFlags |= NIF_TIP;
}

That backup reads uninitialized memory. When those bytes happen to be non-zero, NIF_TIP gets set and Windows renders them as the tooltip. One of our users hovered the tray icon and saw a truncated copy of their PATH. Apps that call setToolTip() overwrite the garbage and never notice, but an app that never sets a tooltip will show whatever was in that memory.

hIcon left dangling after DestroyIcon

Destroy() and the WM_DESTROY handler both call DestroyIcon(nid.hIcon) without clearing the handle afterwards. SetIcon() checks nid.hIcon != nullptr before destroying the old icon, but since nothing ever sets it back to null, a destroy followed by re-initialization calls DestroyIcon twice on the same handle. If Windows recycled that handle value in between, the second call frees an unrelated icon.

Value-initializing both members fixes the first problem and makes the existing null check in SetIcon() meaningful. Clearing hIcon at both destroy sites fixes the second.

nid and niif were declared without initializers. _ApplyIcon() backs up
nid.szTip before zeroing the struct and sets NIF_TIP when the first byte is
non-null, so on the first setIcon() call it reads uninitialized memory and can
register it as the tooltip. An app that never calls setToolTip() then shows
whatever happened to be in that buffer.

Destroy() and the WM_DESTROY handler also left nid.hIcon dangling after
DestroyIcon(), so the null check in SetIcon() could never fire and a
destroy/re-init cycle destroyed the same handle twice.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant