fix 5.2 audit findings on Windows: F-PROC-1 injection, F-MEM-5 UAF, F-PROC-2 restart race, F-PROC-3, F5, F6

This commit is contained in:
2026-08-16 12:29:42 +07:00
parent 54b7a7a6ac
commit 761b48e41f
9 changed files with 134 additions and 69 deletions
+33 -10
View File
@@ -24,6 +24,10 @@ mod shm;
/// không respawn bridge mới sau khi sidecar đã bị giết (fix: app còn sống
/// sau khi đóng window → pump thấy stall 3s → spawn bridge mới → orphan).
static SHUTTING_DOWN: AtomicBool = AtomicBool::new(false);
// F-PROC-2 (audit 5.2): serialize kill_bridge -> spawn_bridge across the pump
// watchdog thread and the sample-rate restart command. Without it both threads
// can kill+spawn concurrently and leave two daw_vst_bridge.exe on the same SHM.
static BRIDGE_RESTART_LOCK: Mutex<()> = Mutex::new(());
use shm::{Shm, ShmState};
struct EngineProcess(Mutex<Option<CommandChild>>);
@@ -513,7 +517,22 @@ pub fn run() {
);
// FIX: kill bridge cũ trước — không được để 2 bridge
// cùng map SHM (race control queue / double load).
// F-PROC-2: serialize with the sample-rate restart
// command (same BRIDGE_RESTART_LOCK).
let _restart_guard = match BRIDGE_RESTART_LOCK.lock() {
Ok(g) => g,
Err(p) => p.into_inner(),
};
// F-PROC-3: re-check after acquiring the lock AND
// after kill — Destroyed may have been set while we
// waited/killed; never respawn during app shutdown.
if SHUTTING_DOWN.load(Ordering::Relaxed) {
break;
}
kill_bridge(&pump_handle);
if SHUTTING_DOWN.load(Ordering::Relaxed) {
break;
}
if spawn_bridge(&pump_handle, &res_dir, &exe_dir, &mut line, &log_path) {
// count restarts into bridge.log (same file as stdout redirect)
let bridge_log = std::path::Path::new(&log_dir)
@@ -672,17 +691,15 @@ fn open_vst_gui(app: AppHandle, plugin_id: String, channel: u8) -> Result<(), St
// B9: bridge tự tạo native Win32 window cho editor VST3 (không qua
// WebView2 — HTML window cũ vẽ ĐÈ lên GUI plugin). push_control(4,0,0,0)
// với hwnd=0 báo bridge tạo window riêng trên ChannelWorker thread.
// F5 (audit 5.2): trả Err khi lệnh không tới được bridge (SHM unavailable /
// lock poisoned / queue full) — frontend openNativeGUI đã try/catch sẵn.
let state = app.state::<ShmState>();
let lock = state.0.lock();
match lock {
Ok(guard) => match guard.as_ref() {
Some(shm) => {
let ok = shm.push_control(4, 0, 0, channel as u32, &plugin_id);
eprintln!("open_vst_gui: push_control type=4 hwnd=0 ch={} ok={}", channel, ok);
}
None => eprintln!("open_vst_gui: bridge shm unavailable"),
},
Err(e) => eprintln!("open_vst_gui: shm lock poisoned: {}", e),
let guard = state.0.lock().map_err(|e| e.to_string())?;
let shm = guard.as_ref().ok_or("bridge shm unavailable")?;
let ok = shm.push_control(4, 0, 0, channel as u32, &plugin_id);
if !ok {
eprintln!("open_vst_gui: push_control type=4 hwnd=0 ch={} failed", channel);
return Err("control queue full".into());
}
Ok(())
}
@@ -770,6 +787,12 @@ fn restart_bridge_with_sample_rate(app: AppHandle, sample_rate: u32) -> Result<(
*g = sample_rate;
}
}
// F-PROC-2: serialize with the pump watchdog restart so two threads can
// never both kill+spawn (two bridges on the same SHM).
let _restart_guard = match BRIDGE_RESTART_LOCK.lock() {
Ok(g) => g,
Err(p) => p.into_inner(),
};
kill_bridge(&app);
let res_dir = app.path().resource_dir().unwrap_or_default();
let exe_dir = std::env::current_exe()