diff --git a/20260815-tasks.md b/20260815-tasks.md index 57bdbc2..eee5b0b 100644 --- a/20260815-tasks.md +++ b/20260815-tasks.md @@ -180,6 +180,44 @@ Kết quả review toàn bộ diff G2 (`git diff 1fe2d26~1..b59e416`, 15 files; **Kết luận Linux**: code G2 nhất quán với báo cáo Windows; không có bug sản phẩm; 2 findings minor (F1, F2) + 1 cosmetic (F3) không chặn merge. Trạng thái G2.0–G2.6: ✅ (Win xong + Linux review xác nhận). +### 5.2 Audit bảo mật / bộ nhớ / conflict child process (2026-08-16, Linux — chỉ review/ghi chú, KHÔNG sửa code) + +Đọc toàn bộ: `native_bridge/src/main.cpp` (1410), `NativeInstrumentEngine.cpp` (439), `Vst3Instrument.cpp` (873), `StateStore.cpp` (288), `SandboxVst3Host.cpp` (192), `plugin_host_main.cpp` (163), `SharedMemoryIPC.cpp` (125), `src-tauri/src/shm.rs` (201), `src-tauri/src/lib.rs` (791), `app/static/js/services/nativeBridgeService.js` (180), `app/static/js/app.precompiled.js` (luồng bridge). Mục tiêu: race UAF / double-buffer / conflict 2 bridge cùng SHM / orphan / command injection / luồng có đúng thiết kế. + +**KẾT LUẬN: không có bug chặn release; 2 MEDIUM (F-MEM-5 UAF, F-PROC-1 injection), 1 MEDIUM (F-PROC-2 race restart×watchdog), 1 MEDIUM audio-correctness (F-MEM-8), 2 LOW (F-PROC-3, F5). Tất cả nằm ở đường hiếm (deschedule >10ms, path thù địch, restart trùng stall) — đề xuất Windows xử lý theo ưu tiên F-PROC-1 > F-MEM-5 > F-PROC-2.** + +**F-MEM-5 (MEDIUM — UAF trên audio thread):** `InstrumentEngineManager::get()` (NativeInstrumentEngine.cpp:300) lock `mu_` rồi trả raw `unique_ptr::get()` sau unlock. `assign()`/`unload()` (dòng 182/240) swap map DƯỚI `mu_` nhưng destroy `oldInst` NGOÀI lock, sau `sleep 10ms`. Drain loop control queue chạy trên **audio thread** (main.cpp:1115) gọi `instruments.get(ch)` rồi deref sau unlock: handleOpenGui dedupe `i0->hasAttachedView()` (main.cpp:794), pending-GUI sweep (884/909/927/953), STOP transport `inst->controlChange(ch,64,0)` (main.cpp:1290). Nếu UiThread assign xong + ngủ 10ms + reset oldInst trong lúc audio thread bị deschedule giữa get() và deref → deref instance đã free → UAF (cửa sổ ~10ms+). Các site trong attach job (823) an toàn vì cùng UiThread với assign. Hướng fix: giữ `mu_` quanh get+use như MIDI dispatch (đã làm đúng ở dòng 338), hoặc get() trả shared_ptr. + +**F-MEM-8 (MEDIUM — audio correctness):** double-buffer G4.3 không handshake (SharedMemoryIPC.h): bridge đọc `writeSlot` + `audioLeft/Right` trong lúc child flip slot → block rách/glitch (không memory corruption — mảng cố định 256). `processAudioBlock` đọc con trỏ `ipc_` non-atomic trong lúc watchdog respawn child (UB lý thuyết, x86 OK). Ghi nhận theo dõi. + +**F-PROC-1 (MEDIUM — command injection):** `SandboxVst3Host::spawnChild()` (SandboxVst3Host.cpp:76-78) nội suy `--path "..."` + `--sr` vào cmdline, gọi `CreateProcessA(nullptr, buf)` — path lấy từ LOAD control / state file. Path chứa `"` + metachar (VD `" && calc`) → thực thi lệnh tuỳ ý (cùng user). Fix: `lpApplicationName` + lpCommandLine arg đã quote đúng, hoặc truyền path qua env/SHM. Severity giữ MEDIUM vì attacker cần kiểm soát path (file .vst3 thù địch / state file sửa tay). + +**F-PROC-2 (MEDIUM — race restart × watchdog → 2 bridge cùng SHM):** `restart_bridge_with_sample_rate` (invoke thread) và pump watchdog (thread riêng) đều chạy `kill_bridge()` → `spawn_bridge()`. `kill_bridge` serialize bằng `BridgeProcess` mutex nhưng **kill→spawn không nguyên tử**: 2 luồng cùng đến `spawn_bridge` → 2 `daw_vst_bridge.exe` cùng map SHM (tên `SonicForge_DAW_IPC_` — CÙNG tên) → double LOAD / OPEN_GUI trùng / control queue rối. `set_bridge_child` overwrite child cũ → rò rỉ bridge đầu (không kill, log redirect task sống theo pipe). Khả năng overlap cao hơn vì cả 2 luồng đều ngủ 300ms trong `flush_bridge_state`. Kích hoạt thực tế: bootstrap JS gọi restart (mỗi lần webview load) trùng lúc bridge mới không kịp heartbeat → watchdog stall 3s. Fix: 1 mutex phủ cả kill+spawn, hoặc spawn_bridge kiểm tra "không còn child tracked" trước khi spawn. + +**F-PROC-3 (LOW — orphan race khi đóng app):** pump thread check `SHUTTING_DOWN` ở đầu vòng, nhưng giữa `kill_bridge` và `spawn_bridge` trong cùng 1 vòng không check lại → Destroyed lấy `None` (pump vừa kill), pump spawn bridge sau khi app đang tắt → orphan tạm thời. Child có `--parent` monitor nên tự thoát khi Tauri exit → transient, không nghiêm trọng. Fix: check SHUTTING_DOWN lần nữa trước spawn_bridge. + +**F5 (LOW — observability):** `open_vst_gui` command (lib.rs:626) luôn trả `Ok(())` kể cả khi SHM unavailable / push fail — lỗi chỉ `eprintln` vào stderr Tauri (không vào bridge.log) → JS không biết thất bại, retry vô ích. Nên trả Err để JS dừng sớm. + +**F6 (LOW — comment lệch code):** `openVstGuiRetry` cap thực tế `n=min(attempts,2)` (2 lần) trong khi comment ghi "Cap 5" (bản cũ ghi 20). Không lỗi hành vi, chỉ lệch comment. + +**Luồng xác nhận ĐÚNG thiết kế:** +- Control queue drain TRƯỚC audio render trong cùng loop (main.cpp:1115-1315) — control không bị đói. +- LOAD job post lên UiThread, serialize `g_loadMutex`; audio thread không stall (comment G3.1) — writeIndex stall 3s chỉ khi heartbeat thread kẹt thật. +- OPEN_GUI: defer khi PLAY (G0.1) + pending sweep khi `transportStopped && instruments.get(ch)`; dedupe `hasAttachedView` chống spam retry; attach job trên UiThread (COM STA) + g_loadMutex + AttachInFlightGuard + same-path silence + destroy child window trên owner thread (0xc000041d fix). +- STOP transport: CC64=0 release sustain TRƯỚC allNotesOff (V8 bug 3) — đúng. +- DUMP_STATE (type=5) post saveStateJob trực tiếp, không chờ debounce; kill_bridge flush 300ms; Rust pump drop guard trước kill (chống deadlock G3.1). +- G2.4 LOAD-skip guard (lower-path compare) — idempotent sau respawn, preset giữ. +- Rust pump: liveness = heartbeat chuyên dụng (không phải writeIndex), phân loại HANG/CRASH, max 1 restart/incident; `restart_attempts` reset khi hb advance. +- Destroyed: SHUTTING_DOWN trước kill → pump dừng (trừ race F-PROC-3). +- JS: `__bridgePlaying` guard openVstGuiRetry; load instrument im lặng khi PLAY (không tự mở GUI); `__bridgeLoadedChannels` reset cả connected lẫn !connected (V8 bug 4) → reconnect reload đúng, không mở GUI; C++ LOAD-skip làm reload idempotent. +- shm.rs layout khớp C++ (selfcheck 12/1040/11168/15268); `push_control` zero-fill arg2 → `std::string(c.arg2)` an toàn với mọi writer hiện tại; MIDI count non-atomic (race benign, volatile). + +**Verify Linux:** +- `g++ -std=c++17 -Wall -Wextra -fanalyzer` StateStore.cpp: compile sạch (0 warning) + `test_state_store.cpp` run **"state_store round-trip PASS"**. +- `SharedMemoryIPC.cpp` / `SandboxVst3Host.cpp` / `plugin_host_main.cpp`: Windows-only (xung đột POSIX `shm_open`, thiếu HANDLE/CreateProcessA) — không build được trên Linux, đúng kỳ vọng. +- `node --check` app.precompiled.js + nativeBridgeService.js: PASS. +- Rust: không có toolchain (như G2.6). + ### Nhật ký thực hiện - **G2.0** (2026-08-16, Windows): tao `native_bridge/include/StateStore.h` + `native_bridge/src/StateStore.cpp` (CRLF, JSON viet tay, atomic write .tmp→rename, escape \ " , load parser an toan). Them StateStore.cpp vao CMakeLists target daw_vst_bridge. Build bridge PASS. Test round-trip `native_bridge/debug/test_state_store.cpp` PASS (compile cl vcvars64 + chay). Commit `G2.0: StateStore (JSON state file + atomic write)`. @@ -190,6 +228,7 @@ Kết quả review toàn bộ diff G2 (`git diff 1fe2d26~1..b59e416`, 15 files; - **G2.5** (2026-08-16, Windows): tao native_bridge/debug/test_g2_stress.py (khung test_g15_stress.py). N vong: LOAD Nexus ch5 + EZkeys ch6 (real hoac skip deu OK) -> PLAY -> taskkill daw_vst_bridge -> cho watchdog "[tauri] bridge restarted" + "restored ch=5/6" -> LOAD lai -> verify "LOAD skip" (guard G2.4) + preset giu (ch5 exact, ch6 sim>=0.7 — EZkeys nhung PlayHeadPos vao state, ratio 0.872 giua save thong thuong) + writeIndex advancing. Chay 5 + 10 iterations: PASS (20 lan kill/respawn, khong crash, bridge alive). Kem sua test_g15_stress.py: LOAD ch5 chap nhan "LOAD skip" (guard G2.4 doi log). Commit `G2.5: stress driver for persistence restart (N loop + kill/respawn)`. - **G2.6** (2026-08-16, Windows): verify tong. - **G2.0–G2.6 review + verify** (2026-08-16, Linux — máy Linux chỉ review/ghi chú, KHÔNG sửa code): đọc toàn bộ diff G2 (`1fe2d26~1..b59e416`, 15 files) — Vst3Instrument base64 + loadSerializedState (bounds-check đủ), NativeInstrumentEngine snapshot (mu_ chặn race với audio loop, skip reloading), main.cpp saveStateJob/restore/LOAD-guard/DUMP/debounce, lib.rs flush_bridge_state + env SF_STATE_FILE, test_g2_stress.py + test_state_store.cpp + test_g15_stress.py. Verify Linux: py_compile 2 stress scripts PASS; g++ compile + run test_state_store PASS (StateStore portable); node --check app.precompiled.js PASS; rustc/cargo không có default toolchain → không cargo check (Windows đã check PASS). Findings: F1 double flush_bridge_state khi window-close (minor), F2 SF_STATE_FILE thiếu Linux fallback (portability, không ảnh hưởng target Windows), F3 log "bad ctrlLen" trùng cho 2 check (cosmetic). Không sửa code (đúng phân máy). Chi tiết ở mục 5.1. G1.5 regression PASS 5/5 (LOAD/OPEN_GUI/PLAY/STOP/WM_CLOSE, writeIndex delta 307-551, bridge alive). G2.5 PASS 5+5+10 (20 lan kill/respawn, preset giu, khong EXCEPTION/FAILED). Probe G0.4 (ban cap nhat: PID dong, LOAD guard-aware, OPEN_GUI full path): PASS — LOAD skip -> OPEN_GUI hwnd -> WM_CLOSE -> "state captured ch=5 component=2628B" -> reload -> "state restored ch=5 component=2628B" -> OPEN_GUI lai attach OK, khong restart. Bug tim duoc: khong co bug san pham; 2 bug test-script (probe PID int vs list -> bao restart gia; preset EZkeys byte-diff do PlayHeadPos runtime). Commit `G2.6: verify — regression G1.5 + stress G2.5 + probe G0.4 all PASS`. +- **Audit bảo mật/bộ nhớ/conflict child process** (2026-08-16, Linux — chỉ review/ghi chú, KHÔNG sửa code): đọc toàn bộ main.cpp/NativeInstrumentEngine/Vst3Instrument/StateStore/SandboxVst3Host/plugin_host_main/SharedMemoryIPC/shm.rs/lib.rs/nativeBridgeService.js/app.precompiled.js. Findings: F-MEM-5 UAF audio-thread get()+deref vs assign() destroy oldInst ngoài lock sau 10ms (MEDIUM, sites 794/884/909/927/953/1290); F-MEM-8 double-buffer không handshake (MEDIUM audio); F-PROC-1 command injection `--path "..."` CreateProcessA (MEDIUM); F-PROC-2 race restart×watchdog → 2 bridge cùng SHM (MEDIUM); F-PROC-3 orphan race Destroyed (LOW); F5 open_vst_gui luôn Ok (LOW); F6 comment cap 2 vs 5 (LOW). Luồng chính đúng thiết kế: drain control trước render, LOAD UiThread+g_loadMutex, OPEN_GUI defer PLAY + pending sweep, STOP flush CC64=0, DUMP_STATE trực tiếp, pump drop-guard trước kill, max 1 restart/incident, JS __bridgePlaying + __bridgeLoadedChannels reset. Verify Linux: StateStore -fanalyzer sạch + round-trip PASS; 3 file Windows-only không build được (đúng); node --check 2 JS PASS. Không sửa code (đúng phân máy). Chi tiết mục 5.2. ---