fix(core): five sweep lows — seal-lane fallback, flush partial, codec echo, idle clamp, pair-name UTF-8
From the 2026-07-20 punktfunk-core quality sweep's adjudicated lows (~/punktfunk-sweeps/punktfunk-core-2026-07-20.json), each with a regression test: - session: the two-lane seal's dead-worker fallback dropped the frame's back half and returned Ok with half an access unit; the corpse lane was also never respawned. The send-failure arm now reclaims the tail (single-lane seals the WHOLE frame) and both failure arms drop the lane so the next large frame respawns it. A recv-side death now surfaces as an error instead of silent truncation. - reassemble: Reassembler::reset() left pending_partial parked, so a pre-flush stale partial survived Session::flush_backlog and was delivered as the first "frame" after a jump-to-live. - caps: resolve_codec echoed a non-conformant multi-bit preferred byte verbatim (downstream from_wire folds it to HEVC — possibly outside the shared set). It now isolates one bit of the intersection. - endpoint: stream_transport_idle only floored the value; an absurd operator-supplied idle timeout blew past QUIC's VarInt ms ceiling and panicked host startup through the expect. Clamped to 1s..1h. - pairing: PairRequest::encode cut the device name at a raw byte-64 boundary, splitting multi-byte UTF-8 (host showed U+FFFD forever); it now shares Hello's char-boundary truncate_to. The frozen-FEC-ceiling finding (reassemble.rs:109) was already fixed on main by the sweep's high-severity commit — skipped as stale. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -78,13 +78,16 @@ fn get_bytes(b: &[u8], off: usize) -> Result<(&[u8], usize)> {
|
||||
|
||||
impl PairRequest {
|
||||
pub fn encode(&self) -> Vec<u8> {
|
||||
let name = self.name.as_bytes();
|
||||
let n = name.len().min(64);
|
||||
// Same cap, same rule as Hello's copy of this field: truncate on a char boundary —
|
||||
// a raw byte cut mid-sequence put invalid UTF-8 on the wire, and the host showed the
|
||||
// name with a permanent replacement char in its paired-clients list.
|
||||
let name = super::handshake::truncate_to(&self.name, HELLO_NAME_MAX).as_bytes();
|
||||
let n = name.len();
|
||||
let mut b = Vec::with_capacity(8 + n + self.spake_a.len());
|
||||
b.extend_from_slice(CTL_MAGIC);
|
||||
b.push(MSG_PAIR_REQUEST);
|
||||
b.push(n as u8);
|
||||
b.extend_from_slice(&name[..n]);
|
||||
b.extend_from_slice(name);
|
||||
put_bytes(&mut b, &self.spake_a);
|
||||
b
|
||||
}
|
||||
@@ -201,4 +204,20 @@ mod tests {
|
||||
bad.push(0);
|
||||
assert!(PairProof::decode(&bad).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pair_request_name_cap_respects_char_boundaries() {
|
||||
// A multi-byte char straddling the 64-byte cap must be dropped whole (Hello's rule),
|
||||
// not split mid-sequence into invalid UTF-8 the host then renders as U+FFFD forever.
|
||||
let pr = PairRequest {
|
||||
name: format!("{}\u{00fc}", "x".repeat(HELLO_NAME_MAX - 1)),
|
||||
spake_a: vec![1, 2, 3],
|
||||
};
|
||||
let dec = PairRequest::decode(&pr.encode()).unwrap();
|
||||
assert!(dec.name.len() <= HELLO_NAME_MAX && dec.name.starts_with('x'));
|
||||
assert!(
|
||||
!dec.name.contains('\u{FFFD}'),
|
||||
"name must never be split mid-char on the wire"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user