fix: harden release profile, drop dead struct fields (v0.6.18)

Security-audit follow-up. The release profile had silently drifted from
the hardened profile (v0.6.12): v0.6.14 bundled lto fat->thin, strip
true->false, and debug=true into an unrelated refactor — a debug aid for
the suspend/resume SIGSEGV hunt. That crash is fixed (v0.6.17), so
restore lto=fat + strip=true and drop the debug symbols, which on a
security-critical auth binary only ease reverse-engineering of the auth
path and bloat the binary.

Also remove two vestigial struct fields the audit surfaced: never read,
no behavior change.
- LockscreenHandles.password_entry: the entry is fully wired via internal
  closures before the handles return; no caller read the field.
- User.uid: superseded by getuid() (root check) and username lookups.
This commit is contained in:
2026-06-17 10:46:14 +02:00
parent baae17e1d8
commit d292eaa4c8
5 changed files with 12 additions and 10 deletions
Generated
+1 -1
View File
@@ -575,7 +575,7 @@ dependencies = [
[[package]] [[package]]
name = "moonlock" name = "moonlock"
version = "0.6.17" version = "0.6.18"
dependencies = [ dependencies = [
"gdk-pixbuf", "gdk-pixbuf",
"gdk4", "gdk4",
+3 -4
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "moonlock" name = "moonlock"
version = "0.6.17" version = "0.6.18"
edition = "2024" edition = "2024"
description = "A secure Wayland lockscreen with GTK4, PAM and fingerprint support" description = "A secure Wayland lockscreen with GTK4, PAM and fingerprint support"
license = "MIT" license = "MIT"
@@ -28,7 +28,6 @@ tempfile = "3"
glib-build-tools = "0.22" glib-build-tools = "0.22"
[profile.release] [profile.release]
lto = "thin" lto = "fat"
codegen-units = 1 codegen-units = 1
strip = false strip = true
debug = true
+7
View File
@@ -2,6 +2,13 @@
Architectural and design decisions for Moonlock, in reverse chronological order. Architectural and design decisions for Moonlock, in reverse chronological order.
## 2026-06-17 Restore hardened release profile after the crash hunt (v0.6.18)
- **Who**: ClaudeCode, Dom
- **Why**: A security audit found the `[profile.release]` in `Cargo.toml` had silently drifted from the hardened profile decided on 2026-04-24 (`lto = "fat"`, `strip = true`). Git blame traced the drift to v0.6.14 (commit `85cf039`, "refactor: power-confirm via PowerAction table"): `lto` was reverted `fat``thin`, `strip` flipped `true``false`, and `debug = true` was added — all bundled into an unrelated refactor commit, with no commit-message mention and no entry here. The pattern (no strip + debug symbols + faster thin LTO) was a debug aid for the suspend/resume SIGSEGV hunt that ran v0.6.9v0.6.17, giving symbolized coredump backtraces. That crash is fixed as of v0.6.17, so the debug profile has outlived its purpose, while shipping debug symbols on a security-critical auth binary eases reverse-engineering of the auth path and bloats the binary.
- **Tradeoffs**: Restoring `lto = "fat"` roughly doubles release build time (~30 s → ~60 s) for better cross-crate inlining; acceptable for a binary compiled once per release. Dropping `strip = false` + `debug = true` means field coredumps are no longer symbolized out of the box — a deliberate trade now that the crash is resolved; debug symbols can be re-enabled temporarily if a new crash needs hunting. Not chosen: keeping `lto = "fat"` but retaining the debug symbols — rejected because the symbols' only justification (the crash hunt) is gone.
- **How**: `Cargo.toml` `[profile.release]` restored to `lto = "fat"`, `strip = true`, with the `debug = true` line removed; `codegen-units = 1` unchanged. Verified via `file target/release/moonlock` reporting a stripped binary.
## 2026-06-02 Real fix for the unlock SIGSEGV: quit in ::unlocked, never destroy windows ourselves (v0.6.17) ## 2026-06-02 Real fix for the unlock SIGSEGV: quit in ::unlocked, never destroy windows ourselves (v0.6.17)
- **Who**: ClaudeCode, Dom - **Who**: ClaudeCode, Dom
-3
View File
@@ -24,7 +24,6 @@ use crate::users;
pub struct LockscreenHandles { pub struct LockscreenHandles {
pub window: gtk::ApplicationWindow, pub window: gtk::ApplicationWindow,
pub fp_label: gtk::Label, pub fp_label: gtk::Label,
pub password_entry: gtk::PasswordEntry,
pub unlock_callback: Rc<dyn Fn()>, pub unlock_callback: Rc<dyn Fn()>,
pub username: String, pub username: String,
state: Rc<RefCell<LockscreenState>>, state: Rc<RefCell<LockscreenState>>,
@@ -67,7 +66,6 @@ pub fn create_lockscreen_window(
return LockscreenHandles { return LockscreenHandles {
window, window,
fp_label, fp_label,
password_entry: gtk::PasswordEntry::new(),
unlock_callback, unlock_callback,
username: String::new(), username: String::new(),
state: Rc::new(RefCell::new(LockscreenState { state: Rc::new(RefCell::new(LockscreenState {
@@ -357,7 +355,6 @@ pub fn create_lockscreen_window(
LockscreenHandles { LockscreenHandles {
window, window,
fp_label, fp_label,
password_entry: password_entry.clone(),
unlock_callback, unlock_callback,
username: user.username, username: user.username,
state: state.clone(), state: state.clone(),
+1 -2
View File
@@ -12,7 +12,6 @@ pub struct User {
pub username: String, pub username: String,
pub display_name: String, pub display_name: String,
pub home: PathBuf, pub home: PathBuf,
pub uid: u32,
} }
pub fn get_current_user() -> Option<User> { pub fn get_current_user() -> Option<User> {
@@ -29,7 +28,7 @@ pub fn get_current_user() -> Option<User> {
let first = gecos.split(',').next().unwrap_or(""); let first = gecos.split(',').next().unwrap_or("");
if first.is_empty() { nix_user.name.clone() } else { first.to_string() } if first.is_empty() { nix_user.name.clone() } else { first.to_string() }
} else { nix_user.name.clone() }; } else { nix_user.name.clone() };
Some(User { username: nix_user.name, display_name, home: nix_user.dir, uid: uid.as_raw() }) Some(User { username: nix_user.name, display_name, home: nix_user.dir })
} }
pub fn get_avatar_path(home: &Path, username: &str) -> Option<PathBuf> { pub fn get_avatar_path(home: &Path, username: &str) -> Option<PathBuf> {