CVE-2025-1019
Overview
Background
- `nsWindow::Show`
- The Windows widget entry point that maps a native
HWNDon-screen and positions it in the window z-order via::SetWindowPos. - `::SetWindowPos`
- The Win32 API that changes a window’s size, position, and z-order relative to a supplied insert-after window handle.
- `SWP_NOOWNERZORDER`
- A
::SetWindowPosflag that suppresses the default behavior of also repositioning the target window’s owner in the z-order. - Fullscreen notification
- The Firefox-drawn overlay warning the user that a page has entered fullscreen mode, which must remain visible above content to prevent spoofing.
Root Cause Analysis
When nsWindow::Show displays a non-PopupLevel::Top popup, it calls ::SetWindowPos twice: once to place the popup (mWnd) relative to its owner, and once to place the owner relative to the popup. Neither call originally passed SWP_NOOWNERZORDER, so Windows was free to additionally reorder the owner’s own owner (an ancestor top-level window) as a side effect of positioning these popups. Because the fullscreen notification is realized as an owned window layered above the browser content, this implicit owner-chain z-order shuffling could push a window above the notification, hiding it. The invariant violated is that showing a child popup must not alter the relative z-order of unrelated windows in the owner chain.
The fix adds SWP_NOOWNERZORDER to both the base flags and the second ::SetWindowPos call, so only the explicitly named windows move and the owner-chain ordering — and thus the notification’s stacking — is preserved.
SWP_NOOWNERZORDER on popup-positioning ::SetWindowPos calls, which let Windows silently reorder owner-chain windows; adding the flag confines each call’s z-order effect to exactly the two windows named, so the fullscreen notification can no longer be reordered beneath other windows.Attack Path
- Enter fullscreen A malicious page requests fullscreen, causing Firefox to display the fullscreen notification as an owned window layered above the content.
- Trigger popup display
The page induces creation and showing of a popup window (e.g. a select dropdown or other non-top popup) so that
nsWindow::Showruns its::SetWindowPossequence. - Exploit implicit owner z-ordering
Because the calls lack
SWP_NOOWNERZORDER, Windows reshuffles the owner chain, moving another window above the fullscreen notification. - Hide the notification With the notification obscured, the fullscreen state is no longer signaled to the user.
- Spoof the UI The attacker renders fake browser chrome or address-bar content in the now-unannounced fullscreen view to deceive the user.
Impact Assessment
::SetWindowPos path executes; it is limited to the Windows widget backend. The result is a moderate-severity spoofing risk rather than memory corruption or code execution.Changed Functions
| Function | Change | Notes |
|---|---|---|
ifwidget/windows/nsWindow.cpp |
modified |
Files Changed
widget/windows/nsWindow.cpp
Audit Directions
- Missing `SWP_NOOWNERZORDER`Review every
::SetWindowPoscall in the Windows widget code for cases involving owned windows where implicit owner-chain z-order changes are undesired. - Security-overlay stackingAudit any code that renders trust-critical overlays (fullscreen, permission, and pointer-lock notifications) as owned windows to confirm nothing can reorder them beneath other windows.
- Z-order side effectsLook for window-management APIs (
BringWindowToTop,SetWindowPos, owner/owned relationships) whose documented default side effects on relative ordering are not explicitly constrained by flags.
Patch
diff --git a/widget/windows/nsWindow.cpp b/widget/windows/nsWindow.cpp
index 2d0d45afed5..ba71007965a 100644
--- a/widget/windows/nsWindow.cpp
+++ b/widget/windows/nsWindow.cpp
@@ -1688,7 +1688,7 @@ void nsWindow::Show(bool aState) {
// layer. Remember not to set the SWP_NOZORDER
// flag as that might allow the taskbar to overlap
// the popup.
- flags |= SWP_NOACTIVATE;
+ flags |= SWP_NOACTIVATE | SWP_NOOWNERZORDER;
HWND owner = ::GetWindow(mWnd, GW_OWNER);
if (owner) {
// PopupLevel::Top popups should be above all else. All other
@@ -1696,8 +1696,9 @@ void nsWindow::Show(bool aState) {
// changing the owner's z-level relative to other windows.
if (mPopupLevel != PopupLevel::Top) {
::SetWindowPos(mWnd, owner, 0, 0, 0, 0, flags);
- ::SetWindowPos(owner, mWnd, 0, 0, 0, 0,
- SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
+ ::SetWindowPos(
+ owner, mWnd, 0, 0, 0, 0,
+ SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOOWNERZORDER);
} else {
::SetWindowPos(mWnd, HWND_TOP, 0, 0, 0, 0, flags);
}