Skip to content

Commit 692c40f

Browse files
committed
SNP: fix stop-waiting clamp and improve sentinel guard in gap-trim loop
Fixed a latent bug where nPktNumBeforeSentinelGap was declared int instead of int64. On connections that have exchanged more than ~2.1 billion packets, the sentinel key truncates to a large negative value, the clamp misfires unconditionally, and m_nMinPktNumToSendAcks gets set to that negative value -- permanently breaking stop-waiting advancement for that connection. Also rewrote the paranoia guard that protects the gap-trim loop from walking into the sentinel. The old check was buried inside the m_nEnd > nMinPktNumToSendAcks branch (which the sentinel always entered) and contained a dead Assert( h == end() ) that could never be true. The new check uses an explicit itSentinel iterator captured before the loop, tests for it first before consulting m_nEnd, and adds a belt-and-suspenders m_nEnd == INT64_MAX condition. Changed AssertMsg to AssertMsgOnce since this is a paranoia path that should never fire in practice.
1 parent 57e6d08 commit 692c40f

1 file changed

Lines changed: 13 additions & 12 deletions

File tree

src/steamnetworkingsockets/clientlib/steamnetworkingsockets_snp.cpp

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1014,7 +1014,7 @@ bool CSteamNetworkConnectionBase::ProcessPlainTextDataChunk( int usecTimeSinceLa
10141014
// Clamping the requested stop waiting point to the highest packet number we
10151015
// received keeps a lot of code simple. If this clamp activates, the gap map
10161016
// will get totally emptied by the loop below.
1017-
const int nPktNumBeforeSentinelGap = m_receiverState.m_mapPacketGaps.rbegin()->first-1;
1017+
const int64 nPktNumBeforeSentinelGap = m_receiverState.m_mapPacketGaps.rbegin()->first-1;
10181018
Assert( nPktNumBeforeSentinelGap <= m_statsEndToEnd.m_nMaxRecvPktNum );
10191019
if ( unlikely( nMinPktNumToSendAcks > nPktNumBeforeSentinelGap ) )
10201020
{
@@ -1047,20 +1047,21 @@ bool CSteamNetworkConnectionBase::ProcessPlainTextDataChunk( int usecTimeSinceLa
10471047
// Trim from the front of the packet gap list,
10481048
// we can stop reporting these losses to the sender
10491049
auto h = m_receiverState.m_mapPacketGaps.begin();
1050+
const auto itSentinel = std::prev( m_receiverState.m_mapPacketGaps.end() );
10501051
while ( h->first <= m_receiverState.m_nMinPktNumToSendAcks )
10511052
{
10521053
Assert( h->first < h->second.m_nEnd );
1053-
if ( h->second.m_nEnd > m_receiverState.m_nMinPktNumToSendAcks )
1054+
1055+
// We should never reach the sentinel, due to the clamp above.
1056+
// But add a little paranoia check here and don't crash, just in case.
1057+
if ( unlikely( h == itSentinel || h->second.m_nEnd == INT64_MAX ) )
10541058
{
1059+
AssertMsgOnce( false, "SNP stop waiting advanced past sentinel gap. This should never happen!" );
1060+
break;
1061+
}
10551062

1056-
// We should never reach the sentinel, due to the clamp above.
1057-
// But we will add a little paranoia check here, just in case.
1058-
if ( unlikely( h->first > nPktNumBeforeSentinelGap ) )
1059-
{
1060-
Assert( h == m_receiverState.m_mapPacketGaps.end() );
1061-
AssertMsg( false, "SNP stop waiting advanced past sentinel gap. This should never happen!" );
1062-
break;
1063-
}
1063+
if ( h->second.m_nEnd > m_receiverState.m_nMinPktNumToSendAcks )
1064+
{
10641065

10651066
// Ug. You're not supposed to modify the key in a map.
10661067
// I suppose that's legit, since you could violate the ordering.
@@ -3759,7 +3760,7 @@ bool CSteamNetworkConnectionBase::SNP_ReceiveReliableSegment( int64 nPktNum, int
37593760
// would be to make it more clear what happened. Either way, the connection
37603761
// is dead at this point if we get here because of protobuf encoding having
37613762
// too many continuation bytes.)
3762-
//
3763+
//
37633764
// Return true here because the packet containing this segment is OK and can be acked.
37643765
return true;
37653766
}
@@ -3803,7 +3804,7 @@ bool CSteamNetworkConnectionBase::SNP_ReceiveReliableSegment( int64 nPktNum, int
38033804
{
38043805
// We haven't received enough of the message to decode the size
38053806
// (Probably. See note above about the possibility of bogus protobuf data.)
3806-
//
3807+
//
38073808
// Return true here because the packet containing this segment is OK and can be acked.
38083809
return true;
38093810
}

0 commit comments

Comments
 (0)