diff --git a/src/google/protobuf/io/coded_stream.cc b/src/google/protobuf/io/coded_stream.cc index 2594733e4f913..95c2fd1ebcc99 100644 --- a/src/google/protobuf/io/coded_stream.cc +++ b/src/google/protobuf/io/coded_stream.cc @@ -150,7 +150,10 @@ CodedInputStream::IncrementRecursionDepthAndPushLimit(int byte_limit) { CodedInputStream::Limit CodedInputStream::ReadLengthAndPushLimit() { uint32_t length; - return PushLimit(ReadVarint32(&length) ? length : 0); + return PushLimit(ReadVarint32(&length) && + length <= static_cast(INT_MAX) + ? static_cast(length) + : 0); } bool CodedInputStream::DecrementRecursionDepthAndPopLimit(Limit limit) { diff --git a/src/google/protobuf/io/coded_stream_unittest.cc b/src/google/protobuf/io/coded_stream_unittest.cc index 1959f57f1ccaa..e7d52a968500e 100644 --- a/src/google/protobuf/io/coded_stream_unittest.cc +++ b/src/google/protobuf/io/coded_stream_unittest.cc @@ -1576,6 +1576,21 @@ TEST_F(CodedStreamTest, RecursionLimit) { EXPECT_FALSE(coded_input.IncrementRecursionDepth()); // 7 } +TEST_F(CodedStreamTest, ReadLengthAndPushLimitOverflow) { + // Varint encoding of 0x80000000 (2147483648, which overflows signed int32) + uint8_t bytes[] = {0x80, 0x80, 0x80, 0x80, 0x08, 'h', 'e', 'l', 'l', 'o'}; + ArrayInputStream input(bytes, sizeof(bytes)); + CodedInputStream coded_input(&input); + + // When ReadLengthAndPushLimit encounters a length > INT_MAX, it must push limit 0 + // so the stream is immediately bounded at 0 remaining bytes instead of overflowing + // into an unconstrained negative limit. + CodedInputStream::Limit limit = coded_input.ReadLengthAndPushLimit(); + EXPECT_EQ(0, coded_input.BytesUntilLimit()); + EXPECT_TRUE(coded_input.ConsumedEntireMessage()); + coded_input.PopLimit(limit); +} + class ReallyBigInputStream : public ZeroCopyInputStream { public: