Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/core/cubic.c
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,7 @@ CubicCongestionControlGetNetworkStatistics(
NetworkStatistics->IdealBytes = Connection->SendBuffer.IdealBytes;
NetworkStatistics->SmoothedRTT = Path->SmoothedRtt;
NetworkStatistics->CongestionWindow = Cubic->CongestionWindow;
NetworkStatistics->Bandwidth = Cubic->CongestionWindow / Path->SmoothedRtt;
NetworkStatistics->Bandwidth = Path->SmoothedRtt == 0 ? 0 : Cubic->CongestionWindow / Path->SmoothedRtt;
}

_IRQL_requires_max_(DISPATCH_LEVEL)
Expand Down Expand Up @@ -698,7 +698,7 @@ CubicCongestionControlOnDataAcknowledged(
Event.NETWORK_STATISTICS.IdealBytes = Connection->SendBuffer.IdealBytes;
Event.NETWORK_STATISTICS.SmoothedRTT = Path->SmoothedRtt;
Event.NETWORK_STATISTICS.CongestionWindow = Cubic->CongestionWindow;
Event.NETWORK_STATISTICS.Bandwidth = Cubic->CongestionWindow / Path->SmoothedRtt;
Event.NETWORK_STATISTICS.Bandwidth = Path->SmoothedRtt == 0 ? 0 : Cubic->CongestionWindow / Path->SmoothedRtt;

QuicTraceLogConnVerbose(
IndicateDataAcked,
Expand Down
82 changes: 82 additions & 0 deletions src/core/unittest/CubicTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1274,6 +1274,88 @@ TEST_F(CubicTest, GetNetworkStatistics_RetrieveStats)
ASSERT_EQ(NetworkStats.IdealBytes, 0u);
}

//
// Test: GetNetworkStatistics - Zero SmoothedRtt returns zero Bandwidth
// Scenario: Verifies no division-by-zero occurs when SmoothedRtt is 0 at the
// time GetNetworkStatistics is called (e.g. before the first RTT sample).
// Bandwidth must be reported as 0 rather than crashing.
//
TEST_F(CubicTest, GetNetworkStatistics_ZeroSmoothedRtt_BandwidthIsZero)
{
// Initialize without an RTT sample so SmoothedRtt stays 0.
InitializeWithDefaults(
/*WindowPackets=*/10,
/*HyStart=*/false,
/*Mtu=*/1280,
/*IdleTimeoutMs=*/1000,
/*GotRttSample=*/false
);

ASSERT_EQ(Connection.Paths[0].SmoothedRtt, 0u);

CC->QuicCongestionControlOnDataSent(CC, 5000);

QUIC_NETWORK_STATISTICS NetworkStats;
CxPlatZeroMemory(&NetworkStats, sizeof(NetworkStats));

// Must not crash with divide-by-zero when SmoothedRtt == 0.
CC->QuicCongestionControlGetNetworkStatistics(&Connection, CC, &NetworkStats);

ASSERT_EQ(NetworkStats.Bandwidth, 0u);
ASSERT_EQ(NetworkStats.SmoothedRTT, 0u);
ASSERT_EQ(NetworkStats.CongestionWindow, Cubic->CongestionWindow);
}

//
// Test: OnDataAcknowledged NetStats path - Zero SmoothedRtt returns zero Bandwidth
// Scenario: Verifies no division-by-zero occurs inside the NetStatsEventEnabled
// path of OnDataAcknowledged when an ACK arrives before the first RTT sample
// (SmoothedRtt == 0). The NETWORK_STATISTICS event must fire without crashing.
//
static QUIC_STATUS
QUIC_API
NetStatsDummyCallback(
_In_ HQUIC /* Connection */,
_In_opt_ void* /* Context */,
_Inout_ QUIC_CONNECTION_EVENT* /* Event */)
{
return QUIC_STATUS_SUCCESS;
}

TEST_F(CubicTest, OnDataAcknowledged_NetStats_ZeroSmoothedRtt_BandwidthIsZero)
{
// Initialize without an RTT sample so SmoothedRtt stays 0.
InitializeWithDefaults(
/*WindowPackets=*/10,
/*HyStart=*/false,
/*Mtu=*/1280,
/*IdleTimeoutMs=*/1000,
/*GotRttSample=*/false
);

// Enable the NetStats event path and supply a no-op callback so
// QuicConnIndicateEvent does not dereference a null function pointer.
Connection.Settings.NetStatsEventEnabled = TRUE;
Connection.ClientCallbackHandler = NetStatsDummyCallback;

ASSERT_EQ(Connection.Paths[0].SmoothedRtt, 0u);

Cubic->BytesInFlight = 5000;

// Construct an ACK event with SmoothedRtt == 0 (no RTT sample yet).
QUIC_ACK_EVENT AckEvent = MakeAckEvent(
/*TimeNow=*/1000000,
/*LargestAck=*/5,
/*LargestSentPacketNumber=*/10,
/*BytesAcked=*/1000,
/*SmoothedRtt=*/0,
/*MinRtt=*/0,
/*MinRttValid=*/FALSE);

// Must not crash with STATUS_INTEGER_DIVIDE_BY_ZERO.
CC->QuicCongestionControlOnDataAcknowledged(CC, &AckEvent);
}

//
// Test: Spurious Congestion Event - No-Op When Not In Recovery
// Scenario: Verifies OnSpuriousCongestionEvent returns FALSE with no state change
Expand Down
Loading