dcsctp: Avoid integer overflow in HEARTBEAT-ACK
When a HEARTBEAT is sent out, the current timestamp is stored in the
parameter that will be returned by the HEARTBEAT-ACK. If the timestamp
from the HEARTBEAT-ACK would be from the future (either by jumping
clocks, bit errors, exploiting peer or a fuzzer), the measured RTT would
become really large, and when it was calculated, it would result in an
integer overflow; a wrapping subtraction.
This isn't a problem, as RetransmissionTimeout::ObserveRTT method would
discard measurements that were negative or too large, but it would
trigger an UBSAN violation.
Adding an additional check so that the subtraction doesn't ever wrap.
Bug: chromium:1252515
Change-Id: I1f97b1e773a4639b8193a528716ebd6a27fcb740
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/232904
Reviewed-by: Florent Castelli <orphis@webrtc.org>
Commit-Queue: Victor Boivie <boivie@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#35095}
diff --git a/net/dcsctp/socket/heartbeat_handler.cc b/net/dcsctp/socket/heartbeat_handler.cc
index 87baa14..8f41b9d 100644
--- a/net/dcsctp/socket/heartbeat_handler.cc
+++ b/net/dcsctp/socket/heartbeat_handler.cc
@@ -153,9 +153,10 @@
return;
}
- DurationMs duration(*ctx_->callbacks().TimeMillis() - *info->created_at());
-
- ctx_->ObserveRTT(duration);
+ TimeMs now = ctx_->callbacks().TimeMillis();
+ if (info->created_at() <= now) {
+ ctx_->ObserveRTT(now - info->created_at());
+ }
// https://tools.ietf.org/html/rfc4960#section-8.1
// "The counter shall be reset each time ... a HEARTBEAT ACK is received from
diff --git a/net/dcsctp/socket/heartbeat_handler_test.cc b/net/dcsctp/socket/heartbeat_handler_test.cc
index 2c5df9f..83a7355 100644
--- a/net/dcsctp/socket/heartbeat_handler_test.cc
+++ b/net/dcsctp/socket/heartbeat_handler_test.cc
@@ -135,6 +135,29 @@
handler_.HandleHeartbeatAck(std::move(ack));
}
+TEST_F(HeartbeatHandlerTest, DoesntObserveInvalidHeartbeats) {
+ AdvanceTime(options_.heartbeat_interval);
+
+ // Grab the request, and make a response.
+ std::vector<uint8_t> payload = callbacks_.ConsumeSentPacket();
+ ASSERT_HAS_VALUE_AND_ASSIGN(SctpPacket packet, SctpPacket::Parse(payload));
+ ASSERT_THAT(packet.descriptors(), SizeIs(1));
+
+ ASSERT_HAS_VALUE_AND_ASSIGN(
+ HeartbeatRequestChunk req,
+ HeartbeatRequestChunk::Parse(packet.descriptors()[0].data));
+
+ HeartbeatAckChunk ack(std::move(req).extract_parameters());
+
+ EXPECT_CALL(context_, ObserveRTT).Times(0);
+
+ // Go backwards in time - which make the HEARTBEAT-ACK have an invalid
+ // timestamp in it, as it will be in the future.
+ callbacks_.AdvanceTime(DurationMs(-100));
+
+ handler_.HandleHeartbeatAck(std::move(ack));
+}
+
TEST_F(HeartbeatHandlerTest, IncreasesErrorIfNotAckedInTime) {
DurationMs rto(105);
EXPECT_CALL(context_, current_rto).WillOnce(Return(rto));