Files
libfreemkv/docs/nav.md
T
Matthew Jackson 6e14fc0c0c libfreemkv: 1.7.0 groundwork — sector-recycle soundness + CI hardening
Security
- Remove the unsafe Vec::set_len over recycled sector buffers — UB reachable
  from safe code (GHSA-j8ww-f5fg-9pmh, low). Uses Vec::resize; pooling unchanged.

CI / tooling
- clippy on stable with the MSRV single-sourced from Cargo.toml; cargo-deny
  dependency-audit gate with first-party git sources allowed (#15); audience-based
  comment-guard (#17).

Docs
- README (keydb ownership, BD-J parser count), drive-access transport/reset
  notes, mux timeline module-doc links.
2026-09-02 12:40:55 -07:00

4.1 KiB

dvdnav::nav — First-Play navigation executor

The DVD arm of "mimic a real player" title selection (issue #40).

A player enters a disc at the First-Play PGC (FP_PGC): a short program of VM commands (parsed by super::vmcmd) that runs before any menu draws and typically dispatches straight to the feature title. This module reads that program out of VIDEO_TS.IFO (VMGI), executes its pre-command list on a minimal register machine, and — when the program deterministically reaches a title dispatch (JumpTT) — maps the selected VMG title through the title search pointer table (TT_SRPT) to its (VTS number, title-in-set) coordinates. That is the title a player would land on, so DVD title selection can prefer it over the structural leading-cell heuristic.

Every table count and offset is attacker-controlled, so all arithmetic is checked and all reads are bounds-guarded — the entry point resolve_from_vmg never panics on any input (exercised by the never-panic harness).

The Vm register machine

Vm models the subset of the DVD-Video VM register file the First-Play resolver needs: 16 general parameters (GPRMs) and the system parameters (SPRMs). Execution starts from a cold machine (all zero) — the same starting point a player has before any user interaction. The SPRMs a First-Play routine might branch on (region, parental level, language) are player-/session-specific and unknown at scan time; leaving them zero means the resolver only commits to a title when the program reaches one without depending on them (an unconditional dispatch, or a conditional whose zero-register path a player also takes). When a branch genuinely depends on an unknown SPRM the routine is treated as undecidable and the caller falls back — see the module contract above.

gprm_tainted tracks per-GPRM taint: set when the register holds a value derived from a system parameter (SPRM). An SPRM read taints a compare directly (is_sprm); this bit tracks an SPRM laundered through a GPRM store so a later GPRM-only compare that reads it is still recognised as undecidable.

Vm::eval — compare taint

eval evaluates a compare predicate and returns the predicate result together with a taint flag set when either operand read a system parameter (SPRM). An SPRM's value is player-/session-specific (region, parental level, language) and unknown at scan time, so a branch decision that depends on one is undecidable — the caller abstains rather than commit to the cold-power-on arm. A GPRM read taints only when that register was itself loaded from an SPRM (tracked by gprm_tainted); a GPRM holding a program-established value does not taint.

Test fixture: build_vmgi

build_vmgi assembles a VMGI (VIDEO_TS.IFO) image with a First-Play PGC whose pre-command list is pre, and a TT_SRPT at sector tt_srpt_sector mapping each title to (vtsn, vts_ttn).

Layout: the fixed VMGI header (magic + the FP_PGC and TT_SRPT pointers), then the FP_PGC (with a command table holding pre), then the TT_SRPT at its sector. Kept minimal but spec-shaped so the same code path a real disc takes is exercised.

Regression test rationale

  • sprm_gated_dispatch_abstains: a dispatch gated on an SPRM is undecidable at scan time. if SPRM0 == g0 -> JumpTT 1 is true on the cold machine (both read 0), so without SPRM taint-tracking the resolver would wrongly return title 1; with it, the SPRM read taints the branch and it abstains.

  • sprm_guarded_store_then_unconditional_dispatch_resolves (Defect-1 regression, over-abstention): a store guarded by an SPRM-tainted compare does NOT gate a control transfer, so it must not abstain. if SPRM0 == g0 -> g0 = 5 (a guarded store) is followed by an UNCONDITIONAL JumpTT 1; the tainted predicate only decides whether the store runs, so the resolver must resolve to title 1.

  • sprm_laundered_through_gprm_abstains (Defect-2 regression, under-abstention): an SPRM laundered through a GPRM must still taint a later branch. SetGPRM g0 = SPRM20 followed by if g0 == g1 -> JumpTT 1 — the compare reads only GPRMs, but g0 now holds an SPRM-derived value, so the decision is undecidable and the resolver must abstain.