fix: audit fixes — blur offset, lock-before-IO, FP signal lifecycle, TOCTOU (v0.6.6)
Update PKGBUILD version / update-pkgver (push) Successful in 2s

Third triple audit (quality, performance, security). Key fixes:
- Blur padding offset: texture at (-pad,-pad) prevents edge darkening on all sides
- Wallpaper loads after lock.lock() — disk I/O no longer delays lock acquisition
- begin_verification disconnects old signal handler before registering new one
- resume_async resets failed_attempts to prevent premature exhaustion
- Unknown VerifyStatus with done=true triggers restart instead of hanging
- symlink_metadata() replaces separate is_file()+is_symlink() (TOCTOU)
- faillock_warning dead code removed, blur sigma clamped to [0,100]
- Redundant Zeroizing<Vec<u8>> removed, on_verify_status restricted to pub(crate)
- Warn logging for non-UTF-8 GECOS and avatar path errors
- Default impl for FingerprintListener, 3 new tests (47 total)
This commit is contained in:
2026-03-30 13:09:02 +02:00
parent 65ea523b36
commit 1d8921abee
10 changed files with 116 additions and 37 deletions
+24 -7
View File
@@ -35,10 +35,8 @@ pub struct FingerprintListener {
on_exhausted: Option<Box<dyn Fn() + 'static>>,
}
impl FingerprintListener {
/// Create a lightweight FingerprintListener without any D-Bus calls.
/// Call `init_async().await` afterwards to connect to fprintd.
pub fn new() -> Self {
impl Default for FingerprintListener {
fn default() -> Self {
FingerprintListener {
device_proxy: None,
signal_id: None,
@@ -50,6 +48,14 @@ impl FingerprintListener {
on_exhausted: None,
}
}
}
impl FingerprintListener {
/// Create a lightweight FingerprintListener without any D-Bus calls.
/// Call `init_async().await` afterwards to connect to fprintd.
pub fn new() -> Self {
Self::default()
}
/// Connect to fprintd and get the default device asynchronously.
pub async fn init_async(&mut self) {
@@ -171,6 +177,7 @@ impl FingerprintListener {
listener: &Rc<RefCell<FingerprintListener>>,
username: &str,
) {
listener.borrow_mut().failed_attempts = 0;
Self::begin_verification(listener, username).await;
}
@@ -181,11 +188,16 @@ impl FingerprintListener {
username: &str,
) {
let proxy = {
let inner = listener.borrow();
match inner.device_proxy.clone() {
let mut inner = listener.borrow_mut();
let proxy = match inner.device_proxy.clone() {
Some(p) => p,
None => return,
};
// Disconnect any previous signal handler to prevent duplicates on resume
if let Some(old_id) = inner.signal_id.take() {
proxy.disconnect(old_id);
}
proxy
};
// Claim the device
@@ -265,7 +277,7 @@ impl FingerprintListener {
}
/// Process a VerifyStatus signal from fprintd.
pub fn on_verify_status(&mut self, status: &str, done: bool) {
pub(crate) fn on_verify_status(&mut self, status: &str, done: bool) {
if !self.running {
return;
}
@@ -305,6 +317,9 @@ impl FingerprintListener {
}
log::debug!("Unhandled fprintd status: {status}");
if done {
self.restart_verify_async();
}
}
/// Restart fingerprint verification asynchronously after a completed attempt.
@@ -403,11 +418,13 @@ mod tests {
let called_clone = called.clone();
let mut listener = FingerprintListener::new();
listener.running = true;
listener.running_flag.set(true);
listener.on_failure = Some(Box::new(move || { called_clone.set(true); }));
listener.on_verify_status("verify-no-match", false);
assert!(called.get());
assert!(listener.running);
assert!(listener.running_flag.get());
assert_eq!(listener.failed_attempts, 1);
}