fix(review-008 audit): adopted-entry drop guard; explicit-ALPN guards; review 009
Post-landing audit of the 0.7.1 -> 0.8.0 remediation diff: two hardening guards, one rejection-posture fix, two log/message corrections, and the deferred coverage debt filed as review 009. - RelayPlan owns the producer-leg ChannelManager and reclaims the adopted spoke channel_id via a Drop guard (replaces the pump handler's post-pump_bidi explicit reclaim). Closes the leak windows the pump's normal path cannot reach: the wrapper's establishment bound expiring after the adopt, and the pump handler's early-return arms (plan absent, downcast failure, try_unwrap failure, accept_bi failure). The send-half drop still EOFs the spoke leg via the mux pump's implicit-EOF sentinel, so the spoke-side cascade is unchanged. ADR-051 §6 documents the closed post-adopt window (the pre-adopt §6 window and the inside-adopt_channel cancellation point stay as documented). - rebuild_spec_for trims and rejects empty/whitespace channel_open_alpn strings — an empty explicit string previously overrode a sane name-derived ALPN. - op_name_is_standard_channel_open_shape applies the same empty-segment guard as the derivation: channels//sub no longer serializes boolean-only and then reconstructs unmarked (silent stub for a marked op); the explicit string rides instead. - reserved_reply_key_call_error interpolates RESERVED_REPLY_KEY; the establisher-bug log fires at warn! (programming error). - Regression tests: the plan drop guard, the empty-ALPN fallback, the empty-segment shape check (672 tests, 3 new). - CHANGELOG [Unreleased] entry for the audit fixes. - docs/reviews/009 — the audit's deferred test-coverage gaps (template failure arms, filtered/only, batch reserved key, wire failure path, golden pins, derivation edge shapes, builder overwrite semantics), each with the test to add and gates. Verification: cargo test 672 passed; clippy --all-targets -D warnings clean; fmt --check clean; doc --no-deps clean; wasm32 check clean.
This commit is contained in:
1 parent
50182d7298
commit
54c2a3f941
8 files changed
+416
-28
No files matched your search
+39
-21
@@ -66,10 +66,27 @@ pub struct ProducerLeg {
|
||||
/// establisher and the handler agree on this concrete type (the
|
||||
/// `ChannelPlan` downcast contract, ADR-049 amendment 2); alkcall
|
||||
/// never inspects it.
|
||||
///
|
||||
/// **Adopted-entry ownership (ADR-051 §6):** the plan holds the
|
||||
/// producer-leg `ChannelManager` and reclaims the adopted
|
||||
/// `spoke_id` entry on drop — including the teardown windows the
|
||||
/// pump handler never reaches (the wrapper's establishment bound
|
||||
/// expiring after the adopt, the plan dropped before the pump
|
||||
/// spawns). Dropping the plan's `spoke_send` also EOFs the spoke
|
||||
/// side via the mux pump's implicit-EOF path (REQ-CH-01), so the
|
||||
/// spoke handler reclaims through the same cascade the normal path
|
||||
/// uses.
|
||||
struct RelayPlan {
|
||||
spoke_id: u32,
|
||||
spoke_send: MpscSendStream,
|
||||
spoke_recv: MpscRecvStream,
|
||||
producer_manager: ChannelManager,
|
||||
spoke_send: Option<MpscSendStream>,
|
||||
spoke_recv: Option<MpscRecvStream>,
|
||||
}
|
||||
|
||||
impl Drop for RelayPlan {
|
||||
fn drop(&mut self) {
|
||||
let _ = self.producer_manager.teardown_channel(self.spoke_id);
|
||||
}
|
||||
}
|
||||
|
||||
/// The relay component (ADR-051). Holds the producer-leg surface;
|
||||
@@ -141,7 +158,6 @@ impl ChannelRelay {
|
||||
let producer_call = Arc::clone(&self.producer_leg.call);
|
||||
let producer_manager = self.producer_leg.manager.clone();
|
||||
let op_name = spec.name.clone();
|
||||
let handler_manager = producer_manager.clone();
|
||||
let establisher: OpenEstablisher = Arc::new(move |input, per_call_auth| {
|
||||
let producer_call = Arc::clone(&producer_call);
|
||||
let producer_manager = producer_manager.clone();
|
||||
@@ -159,7 +175,6 @@ impl ChannelRelay {
|
||||
});
|
||||
|
||||
let open_handler: OpenHandler = Arc::new(move |_input, plan, channel_conn, _auth| {
|
||||
let producer_manager = handler_manager.clone();
|
||||
tokio::spawn(async move {
|
||||
let Some(plan) = plan else {
|
||||
return;
|
||||
@@ -168,34 +183,36 @@ impl ChannelRelay {
|
||||
// wrapper → handler, moved each hop), so `try_unwrap`
|
||||
// succeeds; on the phantom-holder case the wrapper's
|
||||
// no-accept telemetry fires and the spoke leg EOFs
|
||||
// when the plan finally drops.
|
||||
// when the plan finally drops (its Drop guard reclaims
|
||||
// the adopted entry whenever that drop happens).
|
||||
let Ok(relay_plan) = plan.downcast::<RelayPlan>() else {
|
||||
tracing::debug!("relay: establisher plan of unexpected type");
|
||||
return;
|
||||
};
|
||||
let Ok(relay_plan) = Arc::try_unwrap(relay_plan) else {
|
||||
let Ok(mut relay_plan) = Arc::try_unwrap(relay_plan) else {
|
||||
tracing::debug!("relay: establisher plan held elsewhere; skipping pump");
|
||||
return;
|
||||
};
|
||||
let Ok(bidi) = channel_conn.accept_bi().await else {
|
||||
return;
|
||||
};
|
||||
// Take the streams out of the plan; the plan itself
|
||||
// stays alive as the adopted-entry drop guard (see
|
||||
// `RelayPlan`) until this task's end.
|
||||
let Some(spoke_recv) = relay_plan.spoke_recv.take() else {
|
||||
return;
|
||||
};
|
||||
let Some(spoke_send) = relay_plan.spoke_send.take() else {
|
||||
return;
|
||||
};
|
||||
// The byte-forward hop (ADR-042 layer 2, ADR-050):
|
||||
// two pumps joined inline (R-02). On completion the
|
||||
// pump's drops EOF the spoke leg; the wrapper's
|
||||
// handler-exit teardown covers the consumer leg. The
|
||||
// adopted spoke channel entry is reclaimed here so the
|
||||
// producer-leg manager holds no stale routing state
|
||||
// after the relayed channel is done (the consumer-leg
|
||||
// wrapper's teardown cannot see it — it is on the
|
||||
// producer leg's manager).
|
||||
let RelayPlan {
|
||||
spoke_id,
|
||||
spoke_send,
|
||||
spoke_recv,
|
||||
} = relay_plan;
|
||||
// pump's send-half drops EOF the spoke leg (the mux
|
||||
// pump's implicit-EOF sentinel) and the plan's Drop
|
||||
// guard reclaims the adopted producer-leg entry; the
|
||||
// wrapper's handler-exit teardown covers the consumer
|
||||
// leg.
|
||||
let _ = pump_bidi(bidi, spoke_recv, spoke_send).await;
|
||||
let _ = producer_manager.teardown_channel(spoke_id);
|
||||
})
|
||||
});
|
||||
|
||||
@@ -268,8 +285,9 @@ async fn open_on_producer_leg(
|
||||
|
||||
let establishment = Establishment::new(Arc::new(RelayPlan {
|
||||
spoke_id,
|
||||
spoke_send,
|
||||
spoke_recv,
|
||||
producer_manager: producer_manager.clone(),
|
||||
spoke_send: Some(spoke_send),
|
||||
spoke_recv: Some(spoke_recv),
|
||||
}) as super::operations::ChannelPlan);
|
||||
Ok(if reply_fields.is_empty() {
|
||||
establishment
|
||||
|
||||
Reference in new issue
Block a user