Low CVSS 9.8 firefox Logic Error 🔧 Commit mapped

Overview

Low
Severity
9.8
CVSS
No
Exploited ITW
Fixed
Fix Status
Impactlow
DescriptionA flaw in handling fullscreen transitions may have inadvertently caused the application to become stuck in fullscreen mode when a modal dialog was opened during the transition. This issue left users unable to exit fullscreen mode using standard actions like pressing "Esc" or accessing right-click menus, resulting in a disrupted browsing experience until the browser is restarted. <br>*This bug only affects the application when running on macOS. Other operating systems are unaffected.*
ComponentWidget
Bug ClassLogic Error
Tracker1916152
Fix commitfee51abb5223 (firefox) +31/-0
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
CISA KEVNot listed
CreditedKang Ali of Punggawa Cybersecurity
Disclosed2024-11-26

Background

Cocoa fullscreen transition
The macOS NSWindow fullscreen animation driven by AppKit, which posts delegate notifications such as windowDidEnterFullScreen: and windowDidExitFullScreen: as the window moves between windowed and NSWindowStyleMaskFullScreen states.
`nsCocoaWindow`
Gecko’s macOS widget class that wraps a native NSWindow and mediates between the DOM’s notion of size mode (nsSizeMode_Fullscreen) and the platform window’s actual style mask.
`sWindowInNativeTransition`
A static flag set at the start of a native fullscreen transition and expected to be cleared when the transition completes, gating further fullscreen operations while it is set.
`CocoaWindowDidEnterFullscreen`
The nsCocoaWindow method that reconciles Gecko’s size-mode bookkeeping (including mTransitionCurrent and sWindowInNativeTransition) with the completed transition, taking a boolean for whether the window is now in fullscreen.

Root Cause Analysis

When Gecko began a macOS native fullscreen transition it set sWindowInNativeTransition and mTransitionCurrent and told the DOM the window was entering nsSizeMode_Fullscreen, relying on AppKit’s windowDidEnterFullScreen: (or windowDidExitFullScreen:) delegate callback to later clear that transition state. However, AppKit can abort a transition and instead post windowDidFailToEnterFullScreen: or windowDidFailToExitFullScreen: — for example when a modal dialog is opened mid-transition — and the delegate had no handlers for these failure notifications. As a result the success callbacks never fired, sWindowInNativeTransition and mTransitionCurrent were never cleared, and the window was left in a windowed style mask while Gecko still believed it was fullscreen, leaving the user unable to exit fullscreen via Esc or context menus.

The fix adds delegate methods for both failure notifications that reconcile the mismatched state by calling CocoaWindowDidEnterFullscreen with the correct terminal state — false (windowed) for a failed enter and true (fullscreen) for a failed exit — restoring the invariant that a transition always ends by clearing the transition flags.

Key insight
The core mistake was assuming a native fullscreen transition always completes successfully, so only the success notifications cleared sWindowInNativeTransition and mTransitionCurrent; the fix handles the previously ignored windowDidFailToEnterFullScreen: and windowDidFailToExitFullScreen: notifications and routes them through CocoaWindowDidEnterFullscreen to reconcile Gecko’s size mode with the window’s real style mask.

Attack Path

  1. Initiate fullscreen A page or the user triggers a fullscreen request on macOS, causing nsCocoaWindow to set sWindowInNativeTransition and mTransitionCurrent and mark the DOM size mode as nsSizeMode_Fullscreen.
  2. Interrupt the transition A modal dialog is opened while the AppKit fullscreen animation is still in flight, causing macOS to abort the transition.
  3. Failure notification is dropped AppKit posts windowDidFailToEnterFullScreen: (or windowDidFailToExitFullScreen:), but the delegate has no handler, so CocoaWindowDidEnterFullscreen is never invoked and the transition flags stay set.
  4. State desynchronizes The native window remains in a windowed style mask while Gecko still reports fullscreen, and sWindowInNativeTransition remaining set blocks recovery.
  5. User is trapped Standard exit actions like pressing Esc or using right-click menus do nothing, and the disrupted fullscreen state persists until the browser is restarted.

Impact Assessment

An attacker or an unlucky interaction gains a denial-of-service / user-disruption condition on macOS in which the browser is stuck in a fullscreen-vs-windowed state mismatch, preventing the user from exiting fullscreen until a restart. This occurs in the browser’s UI/widget context and does not yield memory corruption or code execution. The precondition is a modal dialog (or similar interruption) opened during an in-progress native fullscreen transition, and the issue affects only macOS.

Changed Functions

FunctionChangeNotes
if
widget/cocoa/nsCocoaWindow.mm
modified

Files Changed

  • widget/cocoa/nsCocoaWindow.mm

Audit Directions

  • Missing failure-path callbacks
    Audit every platform delegate or async-transition consumer that clears state only on a success notification, and verify the corresponding failure/cancel notifications (did*FailTo*) are also handled.
  • Transition-flag lifetimes
    Look for static or member flags like sWindowInNativeTransition and mTransitionCurrent that are set before an async operation and confirm there is no code path that leaves them set indefinitely.
  • Size-mode vs style-mask consistency
    Check code that updates the DOM nsSizeMode_Fullscreen state independently of the native NSWindowStyleMaskFullScreen style mask for windows where the two can diverge if the platform aborts the change.
diff --git a/widget/cocoa/nsCocoaWindow.mm b/widget/cocoa/nsCocoaWindow.mm
index 0b84bf06f7b..cd1f68e6c61 100644
--- a/widget/cocoa/nsCocoaWindow.mm
+++ b/widget/cocoa/nsCocoaWindow.mm
@@ -2834,6 +2834,37 @@ void nsCocoaWindow::CocoaWindowDidResize() {
   mGeckoWindow->CocoaWindowDidEnterFullscreen(false);
 }
 
+- (void)windowDidFailToEnterFullScreen:(NSNotification*)notification {
+  if (!mGeckoWindow) {
+    return;
+  }
+
+  MOZ_ASSERT((mGeckoWindow->GetCocoaWindow().styleMask &
+              NSWindowStyleMaskFullScreen) == 0);
+  MOZ_ASSERT(mGeckoWindow->SizeMode() == nsSizeMode_Fullscreen);
+
+  // We're in a strange situation. We've told DOM that we are going to
+  // fullscreen by changing our size mode, and therefore the window
+  // content is what we would show if we were properly in fullscreen.
+  // But the window is actually in a windowed style. We have to do
+  // several things:
+  // 1) Clear sWindowInNativeTransition and mTransitionCurrent, both set
+  //    when we started the fullscreen transition.
+  // 2) Change our size mode to windowed.
+  // Conveniently, we can do these things by pretending we just arrived
+  // at windowed mode, and all will be sorted out.
+  mGeckoWindow->CocoaWindowDidEnterFullscreen(false);
+}
+
+- (void)windowDidFailToExitFullScreen:(NSNotification*)notification {
+  if (!mGeckoWindow) {
+    return;
+  }
+  // Similarly to windowDidFailToEnterFullScreen, we can get the right
+  // result by pretending we just entered fullscreen.
+  mGeckoWindow->CocoaWindowDidEnterFullscreen(true);
+}
+
 - (void)windowDidBecomeMain:(NSNotification*)aNotification {
   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
 
Loading diff…