fix: fix the issue on lock screen pw and fp race issue
What changed, and why it matters
This commit fixes a race condition on the lock screen between password verification and fingerprint recognition. Previously, if a fingerprint scan result arrived while a password was being verified, the two flows could interfere with each other. The fix adds checks to ignore or delay fingerprint operations whenever a password verification loading screen is active, and cancels any ongoing fingerprint operation when that loading screen appears.
Treat as a security-relevant bug fix. Review whether similar race conditions exist between other authentication flows (e.g., PIN vs fingerprint, passphrase vs fingerprint). Verify that FpCancelCurOperate() reliably aborts the sensor and that no stale fingerprint events can still be queued after cancellation. Consider regression testing lock/unlock sequences with rapid fingerprint and password interactions.
Security signals we found
Race condition between password and fingerprint authentication flows on lock screen
Fingerprint recognition results processed during password verification loading state
Missing synchronization between biometric and passcode unlock paths
New guard function GuiLockScreenIsVerifyLoading() added across multiple fingerprint entry points
FpCancelCurOperate() invoked when password verification loading UI is shown
Evidence from the diff
The patch introduces GuiLockScreenIsVerifyLoading() to detect when the password verification loading container (g_verifyLoadingCont) is active. It then guards multiple fingerprint-related entry points: FpRecognize(RECOGNIZE_UNLOCK), FpRecognizeRecv(), GuiLockScreenFpRecognize(), GuiNeedFpRecognize(), and the SIG_VERIFY_FINGER_PASS/FAIL event handlers. Additionally, GuiLockScreenShowVerifyLoading() now calls FpCancelCurOperate() before displaying the loading UI. This prevents fingerprint recognition results from being processed while password verification is in progress, eliminating a race condition between the two authentication paths.
Changed components
src/managers/fingerprint_process.csrc/ui/gui_views/gui_lock_view.csrc/ui/gui_widgets/gui_lock_widgets.csrc/ui/gui_widgets/gui_lock_widgets.hInspect captured patch +22 / −3
diff --git a/src/managers/fingerprint_process.c b/src/managers/fingerprint_process.c
index 36973af..d4bd630 100644
--- a/src/managers/fingerprint_process.c
+++ b/src/managers/fingerprint_process.c
@@ -66,6 +66,7 @@ static void FpDelayMsgSend(void);
static void SearchFpAesKeyState(void);
static void SearchFpChipId(void);
bool GuiLockScreenIsTop(void);
+bool GuiLockScreenIsVerifyLoading(void);
static void DecryptFunc(uint8_t *decryptPasscode, uint8_t *encryptPasscode, uint8_t *passwordAesKey, size_t blocks);
static void FpRetryCommand(uint32_t cmdIndex);
static bool FpShouldRetryCommand(uint32_t cmdIndex);
@@ -302,6 +303,9 @@ static void FpRecognizeRecv(char *indata, uint8_t len)
{
int i = 0;
uint8_t result = indata[i++];
+ if (g_fingerRecognizeType == RECOGNIZE_UNLOCK && GuiLockScreenIsVerifyLoading()) {
+ return;
+ }
ClearLockScreenTime();
if (result == FP_SUCCESS_CODE) {
MotorCtrl(MOTOR_LEVEL_MIDDLE, MOTOR_SHAKE_SHORT_TIME);
@@ -761,6 +765,9 @@ void SearchFpNum(void)
void FpRecognize(Recognize_Type type)
{
uint8_t accountNum = 0;
+ if (type == RECOGNIZE_UNLOCK && GuiLockScreenIsVerifyLoading()) {
+ return;
+ }
GetExistAccountNum(&accountNum);
if (accountNum <= 0) {
return;
diff --git a/src/ui/gui_views/gui_lock_view.c b/src/ui/gui_views/gui_lock_view.c
index 952e46c..452a17c 100644
--- a/src/ui/gui_views/gui_lock_view.c
+++ b/src/ui/gui_views/gui_lock_view.c
@@ -51,7 +51,7 @@ int32_t GuiLockViewEventProcess(void *self, uint16_t usEvent, void *param, uint1
case GUI_EVENT_DISACTIVE:
break;
case SIG_VERIFY_FINGER_PASS:
- if (GuiLockScreenIsFirstUnlock() || g_lockDeviceView.isActive) {
+ if (GuiLockScreenIsVerifyLoading() || GuiLockScreenIsFirstUnlock() || g_lockDeviceView.isActive) {
break;
}
SetCurrentAccountIndex();
@@ -71,7 +71,7 @@ int32_t GuiLockViewEventProcess(void *self, uint16_t usEvent, void *param, uint1
#endif
break;
case SIG_VERIFY_FINGER_FAIL:
- if (GuiLockScreenIsFirstUnlock() || g_lockDeviceView.isActive) {
+ if (GuiLockScreenIsVerifyLoading() || GuiLockScreenIsFirstUnlock() || g_lockDeviceView.isActive) {
break;
}
GuiFpRecognizeResult(false);
diff --git a/src/ui/gui_widgets/gui_lock_widgets.c b/src/ui/gui_widgets/gui_lock_widgets.c
index e50470e..62a50eb 100644
--- a/src/ui/gui_widgets/gui_lock_widgets.c
+++ b/src/ui/gui_widgets/gui_lock_widgets.c
@@ -67,8 +67,16 @@ void GuiLockScreenUpdatePurpose(LOCK_SCREEN_PURPOSE_ENUM purpose)
g_purpose = purpose;
}
+bool GuiLockScreenIsVerifyLoading(void)
+{
+ return g_verifyLoadingCont != NULL && lv_obj_is_valid(g_verifyLoadingCont);
+}
+
bool GuiNeedFpRecognize(void)
{
+ if (GuiLockScreenIsVerifyLoading()) {
+ return false;
+ }
if (g_fpErrorCount < FINGERPRINT_EN_SING_ERR_TIMES) {
return true;
} else {
@@ -107,6 +115,9 @@ void GuiFpRecognizeResult(bool en)
void GuiLockScreenFpRecognize(void)
{
+ if (GuiLockScreenIsVerifyLoading()) {
+ return;
+ }
if (g_fpErrorCount < FINGERPRINT_EN_SING_ERR_TIMES) {
FpRecognize(RECOGNIZE_UNLOCK);
}
@@ -209,6 +220,7 @@ void GuiLockScreenShowVerifyLoading(void *param)
return;
}
+ FpCancelCurOperate();
g_verifyLoadingCont = GuiCreateAnimHintBox(480, 278, 82);
lv_obj_t *title = GuiCreateTextLabel(g_verifyLoadingCont, _("seed_check_wait_verify"));
lv_obj_align(title, LV_ALIGN_BOTTOM_MID, 0, -76);
diff --git a/src/ui/gui_widgets/gui_lock_widgets.h b/src/ui/gui_widgets/gui_lock_widgets.h
index c8ab8d6..a296c9d 100644
--- a/src/ui/gui_widgets/gui_lock_widgets.h
+++ b/src/ui/gui_widgets/gui_lock_widgets.h
@@ -19,6 +19,7 @@ void GuiLockScreenTurnOffHandler(lv_event_t *e);
void GuiLockScreenTurnOff(void);
void GuiLockScreenTurnOn(void *param);
bool GuiLockScreenIsTop(void);
+bool GuiLockScreenIsVerifyLoading(void);
void GuiLockScreenUpdatePassCode(void);
void OpenForgetPasswordHandler(lv_event_t *e);
void GuiLockScreenHidden(void);
@@ -44,4 +45,3 @@ void GuiShowGenerateXPubLoading(void);
void GuiHideGenerateXPubLoading(void);
void GuiLockViewRefreshLanguage(void);
#endif /* _GUI_LOCK_WIDGETS_H */
-
Why this scored 57/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.