fix(core): remaining 15 sweep lows — client, clipboard, C ABI (v10), FEC, GSO, GTK
ci / docs-site (push) Successful in 58s
ci / web (push) Successful in 1m11s
apple / swift (push) Successful in 1m20s
decky / build-publish (push) Successful in 29s
android / android (push) Has been cancelled
apple / screenshots (push) Has been cancelled
arch / build-publish (push) Has been cancelled
ci / rust (push) Has been cancelled
ci / bench (push) Has been cancelled
deb / build-publish (push) Has been cancelled
deb / build-publish-host (push) Has been cancelled
docker / deploy-docs (push) Has been cancelled
docker / build-push (ci, ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Has been cancelled
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Has been cancelled
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Has been cancelled
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Has been cancelled
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Has been cancelled
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Has been cancelled
flatpak / build-publish (push) Has been cancelled
release / apple (push) Has been cancelled
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Has been cancelled
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Has been cancelled
windows-host / package (push) Has been cancelled
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (push) Has been cancelled
windows-msix / package (x64, C:\Users\Public\ffmpeg, , x86_64-pc-windows-msvc, C:\t) (push) Has been cancelled
windows / build (aarch64-pc-windows-msvc) (push) Has been cancelled
windows / build (x86_64-pc-windows-msvc) (push) Has been cancelled
ci / docs-site (push) Successful in 58s
ci / web (push) Successful in 1m11s
apple / swift (push) Successful in 1m20s
decky / build-publish (push) Successful in 29s
android / android (push) Has been cancelled
apple / screenshots (push) Has been cancelled
arch / build-publish (push) Has been cancelled
ci / rust (push) Has been cancelled
ci / bench (push) Has been cancelled
deb / build-publish (push) Has been cancelled
deb / build-publish-host (push) Has been cancelled
docker / deploy-docs (push) Has been cancelled
docker / build-push (ci, ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Has been cancelled
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Has been cancelled
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Has been cancelled
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Has been cancelled
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Has been cancelled
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Has been cancelled
flatpak / build-publish (push) Has been cancelled
release / apple (push) Has been cancelled
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Has been cancelled
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Has been cancelled
windows-host / package (push) Has been cancelled
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (push) Has been cancelled
windows-msix / package (x64, C:\Users\Public\ffmpeg, , x86_64-pc-windows-msvc, C:\t) (push) Has been cancelled
windows / build (aarch64-pc-windows-msvc) (push) Has been cancelled
windows / build (x86_64-pc-windows-msvc) (push) Has been cancelled
Second and final batch from the 2026-07-20 core-sweep lows: client: - connect() timeout now sets `quit` before shutdown, so a handshake that completes after the deadline closes with QUIT_CLOSE_CODE instead of leaving the host lingering (virtual display up) for a reconnect that never comes. - probe_result: saturating_add on the wire-supplied wire_packets + send_dropped counters (debug-build overflow panic / release wrap). - the standing-latency bleed no longer sets flush_in_window: that flag is the ABR's SEVERE (×0.7) verdict, and the bleed fires only on provably loss-free windows the controller itself scores as fine. clipboard: - fetch_cancels pruned on every new fetch (was: one dead oneshot per paste for the session). - serve chunks gated on a parked waiter + capped at CLIP_FETCH_CAP with an Error event (was: unbounded silent accumulation under any req_id). - serve_inbound park bounded by FETCH_STALL_SECS + send.stopped() (was: an unanswered FetchRequest parked the task, waiter, and bi-stream forever; ~100 of them exhaust the connection's bidi budget). C ABI (ABI_VERSION 9 → 10, header regenerated, additive only): - new punktfunk_connection_clock_offset_now_ns — the LIVE re-synced offset (Swift/Kotlin latency math read the frozen connect-time value ~40ms wrong after a wall-clock step). - to_config: checked u64→usize narrowing of max_frame_bytes (32-bit armeabi truncated >4GiB to a plausible residue). - host_poll_input: no &mut held across the embedder callback (re-entry aliased it — UB under noalias); mid-drain callback clears now stick. - next_audio_pcm: DTX (empty) payloads skipped — decode synthesized 120ms of concealment per 5ms slot and grew the playout ring forever. - next_clipboard releases the parked payload on an empty poll (a one-off 50MiB paste stayed resident all session). - frames_dropped / wants_decode_latency write their documented 0/false defaults before the NULL-handle check. - gamepad constant docs match pick_gamepad() reality (DualSenseEdge/ SwitchPro landed; DualSense/DS4 honored on Windows UMDF too). FEC: - gf8 reconstruct/reconstruct_into reuse the (k,m) codec cache like encode_into (was: fresh 230×200 generator + decode inversion per lossy block on the pump thread). - vendored fec-rs: the 8 safe wrap_mul_slice shims assert equal lengths (x86 SIMD callees bound stores on input.len() — safe-code OOB write); ReconstructShard's safety contract gains the len()==get().len() clause and reconstruct_internal sizes raw slices from the slice. transport/GTK: - Linux GSO super-buffer capped at the real UDP payload ceiling (65487) not 65535 — seg sizes ≥1024 could EMSGSIZE and latch GSO off process-wide, blamed on the network. - GTK settings dialog no longer rewrites an unlisted-but-valid stored gamepad preference to "auto" on close. Already fixed on main (verified stale, skipped): the reassembler FEC ceiling, wants_decode_latency's third term, request_probe rollback + the generalized probe watchdog. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -187,34 +187,58 @@ pub fn detect_mul_slice() -> (MulSliceFn, MulSliceFn) {
|
||||
// Safe wrappers for SIMD functions (used as function pointer targets)
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
fn wrap_mul_slice_gfni_avx2(c: u8, input: &[u8], out: &mut [u8]) {
|
||||
// The unsafe callee bounds every load AND store on input.len(); unequal
|
||||
// lengths would write past `out`. Same invariant mul_slice/mul_slice_xor enforce.
|
||||
assert_eq!(input.len(), out.len());
|
||||
unsafe { mul_slice_gfni_avx2(c, input, out) }
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
fn wrap_mul_slice_xor_gfni_avx2(c: u8, input: &[u8], out: &mut [u8]) {
|
||||
// The unsafe callee bounds every load AND store on input.len(); unequal
|
||||
// lengths would write past `out`. Same invariant mul_slice/mul_slice_xor enforce.
|
||||
assert_eq!(input.len(), out.len());
|
||||
unsafe { mul_slice_xor_gfni_avx2(c, input, out) }
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
fn wrap_mul_slice_avx2(c: u8, input: &[u8], out: &mut [u8]) {
|
||||
// The unsafe callee bounds every load AND store on input.len(); unequal
|
||||
// lengths would write past `out`. Same invariant mul_slice/mul_slice_xor enforce.
|
||||
assert_eq!(input.len(), out.len());
|
||||
unsafe { mul_slice_avx2(c, input, out) }
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
fn wrap_mul_slice_xor_avx2(c: u8, input: &[u8], out: &mut [u8]) {
|
||||
// The unsafe callee bounds every load AND store on input.len(); unequal
|
||||
// lengths would write past `out`. Same invariant mul_slice/mul_slice_xor enforce.
|
||||
assert_eq!(input.len(), out.len());
|
||||
unsafe { mul_slice_xor_avx2(c, input, out) }
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
fn wrap_mul_slice_gfni_sse(c: u8, input: &[u8], out: &mut [u8]) {
|
||||
// The unsafe callee bounds every load AND store on input.len(); unequal
|
||||
// lengths would write past `out`. Same invariant mul_slice/mul_slice_xor enforce.
|
||||
assert_eq!(input.len(), out.len());
|
||||
unsafe { mul_slice_gfni_sse(c, input, out) }
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
fn wrap_mul_slice_xor_gfni_sse(c: u8, input: &[u8], out: &mut [u8]) {
|
||||
// The unsafe callee bounds every load AND store on input.len(); unequal
|
||||
// lengths would write past `out`. Same invariant mul_slice/mul_slice_xor enforce.
|
||||
assert_eq!(input.len(), out.len());
|
||||
unsafe { mul_slice_xor_gfni_sse(c, input, out) }
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
fn wrap_mul_slice_ssse3(c: u8, input: &[u8], out: &mut [u8]) {
|
||||
// The unsafe callee bounds every load AND store on input.len(); unequal
|
||||
// lengths would write past `out`. Same invariant mul_slice/mul_slice_xor enforce.
|
||||
assert_eq!(input.len(), out.len());
|
||||
unsafe { mul_slice_ssse3(c, input, out) }
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
fn wrap_mul_slice_xor_ssse3(c: u8, input: &[u8], out: &mut [u8]) {
|
||||
// The unsafe callee bounds every load AND store on input.len(); unequal
|
||||
// lengths would write past `out`. Same invariant mul_slice/mul_slice_xor enforce.
|
||||
assert_eq!(input.len(), out.len());
|
||||
unsafe { mul_slice_xor_ssse3(c, input, out) }
|
||||
}
|
||||
|
||||
|
||||
+20
-4
@@ -624,8 +624,15 @@ impl ReedSolomon {
|
||||
for (i_input, &valid_idx) in valid_indices.iter().enumerate() {
|
||||
// SAFETY: valid_idx and missing indices are disjoint sets,
|
||||
// so we can safely read from valid_idx while writing to missing indices.
|
||||
let input_ptr = shards[valid_idx].get().unwrap().as_ptr();
|
||||
let input_slice = unsafe { std::slice::from_raw_parts(input_ptr, shard_len) };
|
||||
// Pointer AND length from the same `get()` — an impl whose `len()`
|
||||
// disagrees with its slice then panics in `mul_slice` instead of this
|
||||
// fabricating an out-of-bounds slice from the trait-reported length.
|
||||
let (input_ptr, input_len) = {
|
||||
let input = shards[valid_idx].get().unwrap();
|
||||
debug_assert_eq!(input.len(), shard_len);
|
||||
(input.as_ptr(), input.len())
|
||||
};
|
||||
let input_slice = unsafe { std::slice::from_raw_parts(input_ptr, input_len) };
|
||||
|
||||
for (i_out, &missing_idx) in missing_data_indices.iter().enumerate() {
|
||||
let c = matrix_rows[i_out][i_input];
|
||||
@@ -659,8 +666,13 @@ impl ReedSolomon {
|
||||
|
||||
for i_input in 0..self.data_shard_count {
|
||||
// SAFETY: data shards (0..data_shard_count) are disjoint from parity shards.
|
||||
let input_ptr = shards[i_input].get().unwrap().as_ptr();
|
||||
let input_slice = unsafe { std::slice::from_raw_parts(input_ptr, shard_len) };
|
||||
// Same discipline as the data-shard loop above: length from the slice.
|
||||
let (input_ptr, input_len) = {
|
||||
let input = shards[i_input].get().unwrap();
|
||||
debug_assert_eq!(input.len(), shard_len);
|
||||
(input.as_ptr(), input.len())
|
||||
};
|
||||
let input_slice = unsafe { std::slice::from_raw_parts(input_ptr, input_len) };
|
||||
|
||||
for (i_out, &missing_idx) in missing_parity_indices.iter().enumerate() {
|
||||
let c = matrix_rows[i_out][i_input];
|
||||
@@ -702,6 +714,10 @@ impl ReedSolomon {
|
||||
/// yield non-overlapping memory regions from `get()`/`get_mut()`. Specifically:
|
||||
/// - `get()` on element `i` must not alias `get_mut()` on element `j` when `i != j`.
|
||||
/// - The returned slices must remain valid and not be moved/reallocated while borrows are active.
|
||||
/// - `len()` must return `Some(n)` exactly when `get()`/`get_mut()` return `Some(s)`, and then
|
||||
/// `s.len() == n`; after `initialize(n)`, `get_mut()` must return a slice of length `n`.
|
||||
/// `reconstruct_internal` sizes its raw-pointer reads from `len()`, so a disagreement would
|
||||
/// otherwise read past the allocation.
|
||||
///
|
||||
/// This is required because `reconstruct_internal` uses raw pointers to read from
|
||||
/// some shard indices while writing to others simultaneously.
|
||||
|
||||
Reference in New Issue
Block a user