blob: 8ddf5ef2e0735add3db797b4773d89d6d8d36223 [file] [view]
---
name: webrtc/net/avoid-serializing-redundant-socket-handover-state
description: When implementing dcSCTP socket handover serialization and restoration (AddHandoverState and RestoreFromState), restore all fields, omit redundant state, and ensure backward compatibility for legacy snapshots
rubric: correctness
severity: should_fix
---
**Rule.** In dcSCTP socket handover implementations (AddHandoverState and
RestoreFromState), ensure that every field populated into
DcSctpSocketHandoverState during serialization is read and restored in the
corresponding RestoreFromState method, and do not serialize redundant fields
that can be deterministically recomputed upon restoration. When adding new
handover fields or opt-in capabilities, guard restoration behind the
corresponding feature flag and provide fallback reconstruction when restoring
legacy state snapshots where newly added fields are absent or empty.
**Why.** Omitting serialized fields during deserialization leaves restored
components in a default or inconsistent state, while persisting redundant
derivable fields inflates snapshot payloads and risks desynchronization.
Restoring newly added fields without feature flag guards or backward-compatible
fallbacks corrupts socket state when handling legacy snapshots from older
versions where those fields are absent.
## Examples
Bad:
```cpp
// 1. Unconditional restoration ignoring feature flags and legacy snapshots:
void RetransmissionQueue::RestoreFromState(
Timestamp now, const DcSctpSocketHandoverState& state) {
outstanding_data_.RestoreFromState(
now, tsn_unwrapper_.Unwrap(TSN(state.tx.last_cumulative_tsn_ack)), state);
}
// 2. Missing deserialization of a serialized field:
void OutstandingData::RestoreFromState(const DcSctpSocketHandoverState& state) {
for (const auto& msg : state.tx.outstanding_data) {
// msg.retransmission_count is ignored and lost!
outstanding_data_.emplace_back(msg.message_id, std::move(data));
}
}
// 3. Serializing redundant derivable state:
void RetransmissionQueue::AddHandoverState(DcSctpSocketHandoverState& state) {
state.tx.next_tsn = next_tsn().value();
state.tx.last_cumulative_tsn_ack = last_cumulative_tsn_ack_.Wrap().value();
}
```
Good:
```cpp
// 1. Guarding new features and falling back for legacy/empty state:
void RetransmissionQueue::RestoreFromState(
Timestamp now, const DcSctpSocketHandoverState& state) {
if (options_.enable_handover_with_outstanding_data) {
UnwrappedTSN last_ack = state.tx.outstanding_data.empty()
? tsn_unwrapper_.Unwrap(TSN(state.tx.next_tsn - 1))
: tsn_unwrapper_.Unwrap(TSN(state.tx.last_cumulative_tsn_ack));
outstanding_data_.RestoreFromState(now, last_ack, state);
} else {
outstanding_data_.ResetSequenceNumbers(
tsn_unwrapper_.Unwrap(TSN(state.tx.next_tsn - 1)));
}
}
// 2. Restoring all populated serialized fields:
void OutstandingData::RestoreFromState(const DcSctpSocketHandoverState& state) {
for (const auto& msg : state.tx.outstanding_data) {
outstanding_data_.emplace_back(msg.message_id, std::move(data),
msg.retransmission_count);
}
}
// 3. Omitting redundant derivable fields from handover state:
void RetransmissionQueue::AddHandoverState(DcSctpSocketHandoverState& state) {
state.tx.next_tsn = next_tsn().value();
}
```