rc2: macOS cross-compile fix + security/recovery hardening

- build.rs: pass target -arch to cc so macos_shim cross-compiles (x86_64-apple-darwin)
- AACS/CSS: unit-aligned decrypting sweep; per-VTS CSS title keys (hard-fail on wrong VTS);
  reject truncated Unit_Key_RO; AACS 2.0 sig-verify skip; CSS bus-auth random nonce
- recovery: gap-filling mapfile load; sweep/copy resume reconciliation; stale-mapfile abort;
  patch wedge/damage-window range reset
- mux: TS continuity + PSI CC desync guards; HEVC numTemporalLayers clamp; MPEG-2 pending
  byte-cap; PS parse_pts marker-bit validation; HdrFormat strict parse; Unknown-variant metadata
- net/keydb: network:// SSRF parity (IPv4-mapped, CGNAT, 0.0.0.0/8, Class-E); bounded keydb
  header read + size cap + error context
- io: durable mapfile fsync; NFS writeback degrade; sync_file_range error capture;
  Windows SCSI u32 transfer guard
This commit is contained in:
Matthew Jackson
2026-06-22 08:58:10 -07:00
parent 5941c059c6
commit 337e77951c
25 changed files with 1722 additions and 153 deletions
+19 -2
View File
@@ -191,12 +191,22 @@ impl WritebackPipeline {
// path (NFS, degraded, normal) — it's nominally non-blocking
// by spec and gives the kernel an early hint that this range
// is ready to flush.
unsafe {
let kickoff_rc = unsafe {
libc::sync_file_range(
self.fd,
chunk_off as i64,
chunk_len as i64,
libc::SYNC_FILE_RANGE_WRITE,
)
};
if kickoff_rc != 0 {
// Non-fatal: the async write-out hint failed, but the data is
// still in the page cache and will be flushed by later fsync /
// kernel writeback. Surface it for diagnosability.
tracing::warn!(
target: "freemkv::io",
errno = std::io::Error::last_os_error().raw_os_error().unwrap_or(0),
"sync_file_range(WRITE) kickoff failed"
);
}
if let Some((prev_off, prev_len)) = self.pending.take() {
@@ -233,9 +243,16 @@ impl WritebackPipeline {
// NOT call DONTNEED — if WAIT_AFTER hasn't
// returned, the pages aren't safely flushed.
self.degraded.store(true, Ordering::Relaxed);
// Once degraded we skip DONTNEED, so every subsequent
// chunk's pages stay resident until close — the same
// page-cache exposure profile as NFS. Shrink to the
// floor so that exposure window is as small as the NFS
// path keeps it, instead of whatever the adaptive sizing
// had grown chunk_bytes to (up to 256 MiB).
self.chunk_bytes = CHUNK_BYTES_MIN;
tracing::error!(
target: "mux",
"WritebackPipeline WAIT_AFTER timed out after {}s on chunk off={} len={}, marking writeback degraded (subsequent chunks will skip WAIT_AFTER + DONTNEED)",
"WritebackPipeline WAIT_AFTER timed out after {}s on chunk off={} len={}, marking writeback degraded (subsequent chunks will skip WAIT_AFTER + DONTNEED, chunk_bytes lowered to floor)",
WAIT_AFTER_TIMEOUT.as_secs(),
prev_off,
prev_len