Skip to content

Commit 4f8f399

Browse files
committed
fix: guard _handleRenegotiationNeeded against non-stable signaling state
Check RTCSignalingStateStable before creating offer and re-check after the async createOffer gap to prevent TOCTOU race. Remove outdated TODO.
1 parent 08d6ada commit 4f8f399

File tree

1 file changed

+9
-18
lines changed

1 file changed

+9
-18
lines changed

lib/features/call/bloc/call_bloc.dart

Lines changed: 9 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2788,29 +2788,20 @@ class CallBloc extends Bloc<CallEvent, CallState> with WidgetsBindingObserver im
27882788
}
27892789

27902790
Future<void> _handleRenegotiationNeeded(String callId, int? lineId, RTCPeerConnection peerConnection) async {
2791-
// TODO(Serdun): Handle renegotiation needed
2792-
// This implementation does not handle all possible signaling states.
2793-
// Specifically, if the current state is `have-remote-offer`, calling
2794-
// setLocalDescription with an offer will throw:
2795-
// WEBRTC_SET_LOCAL_DESCRIPTION_ERROR: Failed to set local offer sdp: Called in wrong state: have-remote-offer
2796-
//
2797-
// Known case: when CalleeVideoOfferPolicy.includeInactiveTrack is used,
2798-
// the callee may trigger onRenegotiationNeeded before the current remote offer is processed.
2799-
// This causes a race where the local peer is still in 'have-remote-offer' state,
2800-
// leading to the above error. Currently this does not severely affect behavior,
2801-
// since the offer includes only an inactive track, but it should still be handled correctly.
2802-
//
2803-
// Proper handling should include:
2804-
// - Waiting until the signaling state becomes 'stable' before creating and setting a new offer
2805-
// - Avoiding renegotiation if a remote offer is currently being processed
2806-
// - Ensuring renegotiation is coordinated and state-aware
2807-
28082791
final pcState = peerConnection.signalingState;
28092792
_logger.fine(() => 'onRenegotiationNeeded signalingState: $pcState');
2810-
if (pcState != null) {
2793+
if (pcState == RTCSignalingState.RTCSignalingStateStable) {
28112794
final localDescription = await peerConnection.createOffer({});
28122795
sdpMunger?.apply(localDescription);
28132796

2797+
final currentState = peerConnection.signalingState;
2798+
if (currentState != RTCSignalingState.RTCSignalingStateStable) {
2799+
_logger.fine(
2800+
() => 'onRenegotiationNeeded: state changed to $currentState after createOffer, skipping setLocalDescription',
2801+
);
2802+
return;
2803+
}
2804+
28142805
// According to RFC 8829 5.6 (https://datatracker.ietf.org/doc/html/rfc8829#section-5.6),
28152806
// localDescription should be set before sending the offer to transition into have-local-offer state.
28162807
await peerConnection.setLocalDescription(localDescription);

0 commit comments

Comments
 (0)