Skip to content

Commit 87a0aef

Browse files
authored
Merge 800b4cd into 49b66db
2 parents 49b66db + 800b4cd commit 87a0aef

2 files changed

Lines changed: 58 additions & 67 deletions

File tree

source/core.py

Lines changed: 11 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -419,85 +419,30 @@ def _handleNVDAModuleCleanupBeforeGUIExit():
419419
brailleViewer.destroyBrailleViewer()
420420

421421

422-
def _pollForForegroundHWND() -> int:
423-
"""
424-
@note: The foreground window should usually be fetched on the first try,
425-
however it may take longer if Windows is taking a long time changing window focus.
426-
Times out after 20 seconds (MAX_WAIT_TIME_SECS).
427-
After timing out, NVDA will give up trying to start and exit.
428-
"""
429-
import ui
430-
from utils.blockUntilConditionMet import blockUntilConditionMet
431-
import winUser
432-
433-
# winUser.getForegroundWindow may return NULL in certain circumstances,
434-
# such as when a window is losing activation.
435-
# This should not remain the case for an extended period of time.
436-
# If NVDA is taking longer than expected to fetch the foreground window, perform a warning.
437-
# We must wait a long time after this warning to
438-
# allow for braille / speech to be understood before exiting.
439-
# Unfortunately we cannot block with a dialog as NVDA cannot read dialogs yet.
440-
WARN_AFTER_SECS = 5
441-
MAX_WAIT_TIME_SECS = 20
442-
443-
success, foregroundHWND = blockUntilConditionMet(
444-
getValue=winUser.getForegroundWindow,
445-
giveUpAfterSeconds=WARN_AFTER_SECS,
446-
)
447-
if success:
448-
return foregroundHWND
449-
450-
exitAfterWarningSecs = MAX_WAIT_TIME_SECS - WARN_AFTER_SECS
451-
ui.message(_(
452-
# Translators: Message when NVDA is having an issue starting up
453-
"NVDA is failing to fetch the foreground window. "
454-
"If this continues, NVDA will quit starting in %d seconds." % exitAfterWarningSecs
455-
))
456-
457-
success, foregroundHWND = blockUntilConditionMet(
458-
getValue=winUser.getForegroundWindow,
459-
giveUpAfterSeconds=exitAfterWarningSecs,
460-
)
461-
if success:
462-
return foregroundHWND
463-
log.critical("NVDA could not fetch the foreground window. Exiting NVDA.")
464-
# Raising exception here causes core.main to exit and NVDA to fail to start
465-
raise NVDANotInitializedError("Could not fetch foreground window")
466-
467-
468422
def _initializeObjectCaches():
469423
"""
470424
Caches the desktop object.
471425
This may make information from the desktop window available on the lock screen,
472426
however no known exploit is known for this.
473427
2023.1 plans to ensure the desktopObject is available only when signed-in.
474428
475-
Also initializes other object caches to the foreground window.
476-
Previously the object that was cached was the desktopObject,
477-
however this may leak secure information to the lock screen.
478-
The foreground window is set as the object cache,
479-
as the foreground window would already be accessible on the lock screen (e.g. Magnifier).
480-
It also is more intuitive that NVDA focuses the foreground window,
481-
as opposed to the desktop object.
482-
483-
@note: The foreground window should usually be fetched on the first try,
484-
however it may take longer if Windows is taking a long time changing window focus.
485-
Times out after 20 seconds (MAX_WAIT_TIME_SECS in _pollForForegroundHWND).
486-
After timing out, NVDA will give up trying to start and exit.
429+
The desktop object must be used, as setting the object caches has side effects,
430+
such as focus events.
431+
Side effects from objects may require NVDA to be finished initializing.
432+
The desktop object is an NVDA object without custom code associated with it.
487433
"""
488434
import api
489435
import NVDAObjects
436+
from winAPI.sessionTracking import _IgnoreWindowsLockState
490437
import winUser
491438

492439
desktopObject = NVDAObjects.window.Window(windowHandle=winUser.getDesktopWindow())
493-
api.setDesktopObject(desktopObject)
494-
495-
foregroundHWND = _pollForForegroundHWND()
496-
foregroundObject = NVDAObjects.window.Window(windowHandle=foregroundHWND)
497-
api.setForegroundObject(foregroundObject)
498-
api.setFocusObject(foregroundObject)
499-
api.setNavigatorObject(foregroundObject)
500-
api.setMouseObject(foregroundObject)
440+
with _IgnoreWindowsLockState():
441+
api.setDesktopObject(desktopObject)
442+
api.setForegroundObject(desktopObject)
443+
api.setFocusObject(desktopObject)
444+
api.setNavigatorObject(desktopObject)
445+
api.setMouseObject(desktopObject)
501446

502447

503448
def main():

source/winAPI/sessionTracking.py

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
HWND,
2626
)
2727
import enum
28-
from threading import Lock
28+
from threading import Lock, RLock
2929
from typing import (
3030
Dict,
3131
Set,
@@ -84,6 +84,50 @@
8484
"""
8585

8686

87+
class _IgnoreWindowsLockState:
88+
"""
89+
Used to bypass security checks by forcing isWindowsLocked to return False.
90+
Should only be used where the following conditions all apply:
91+
- security checks are not possible, such as during NVDA initialization/termination.
92+
- usage has no security implications, there are no exploits if the lock state is ignored.
93+
- code does not take a long time to execute.
94+
95+
Using _IgnoreWindowsLockState blocks other threads from:
96+
- checking isWindowsLocked
97+
- using _IgnoreWindowsLockState
98+
99+
Usage
100+
```
101+
with _IgnoreWindowsLockState():
102+
doCodeWhichIsSafe
103+
"""
104+
_shouldIgnoreLockState = False
105+
"""When True, isWindowsLocked is forced to return False"""
106+
_shouldIgnoreLockStateLock = RLock()
107+
"""
108+
This lock is used to protect _shouldIgnoreLockState.
109+
If 2 threads are reading from shouldIgnoreLockState simultaneously,
110+
and 1 thread is using `_IgnoreWindowsLockState`,
111+
then both threads would act as if windows is unlocked.
112+
"""
113+
114+
def __enter__(self) -> bool:
115+
_IgnoreWindowsLockState._shouldIgnoreLockStateLock.acquire()
116+
_IgnoreWindowsLockState.shouldIgnoreLockState = True
117+
return _IgnoreWindowsLockState.shouldIgnoreLockState
118+
119+
def __exit__(self, excType, excVal, traceback):
120+
_IgnoreWindowsLockState.shouldIgnoreLockState = False
121+
_IgnoreWindowsLockState._shouldIgnoreLockStateLock.release()
122+
return _IgnoreWindowsLockState.shouldIgnoreLockState
123+
124+
@classmethod
125+
def shouldIgnoreLockState() -> bool:
126+
"""May block if another thread is using _IgnoreWindowsLockState"""
127+
with _IgnoreWindowsLockState._shouldIgnoreLockStateLock:
128+
return _IgnoreWindowsLockState._shouldIgnoreLockState
129+
130+
87131
class WindowsTrackedSession(enum.IntEnum):
88132
"""
89133
Windows Tracked Session notifications.
@@ -154,6 +198,8 @@ def isWindowsLocked() -> bool:
154198
Checks if the Window lockscreen is active.
155199
Not to be confused with the Windows sign-in screen, a secure screen.
156200
"""
201+
if _IgnoreWindowsLockState.shouldIgnoreLockState():
202+
return False
157203
if _isSecureDesktop():
158204
# If this is the Secure Desktop,
159205
# we are in secure mode and on a secure screen,

0 commit comments

Comments
 (0)