thrift: fix CVE-2026-58662

Improper Validation of Specified Quantity in Input, Out-of-bounds Read
vulnerability in Apache Thrift C++ bindings. This issue affects Apache
Thrift: before 0.24.0.

Backport patch to fix CVE-2026-58662.

Reference:
[https://nvd.nist.gov/vuln/detail/cve-2026-58662]

Upstream Patch:
[https://github.com/apache/thrift/commit/f961cdb44249c293fcce6a840ffa1f7419fd88d0]

Signed-off-by: Abhishek Bachiphale <Abhishek.Bachiphale@windriver.com>
Signed-off-by: Anuj Mittal <anuj.mittal@oss.qualcomm.com>
This commit is contained in:
Abhishek Bachiphale
2026-09-11 08:02:18 +05:30
committed by Anuj Mittal
parent f311c7bdfc
commit a962aa88d3
2 changed files with 121 additions and 0 deletions
@@ -0,0 +1,120 @@
From b9fe622d3e3dd2e376fbb5ccf2acfbfaf498ddb1 Mon Sep 17 00:00:00 2001
From: Javid Khan <dxbjavid@gmail.com>
Date: Tue, 30 Jun 2026 16:34:47 +0200
Subject: [PATCH] fix info-header string bound check in
THeaderTransport::readString Client: cpp Patch: Javid Khan
<dxbjavid@gmail.com>
when reading the key/value info headers of a THeader frame, readString reads
the length varint and then bounds it against the bytes left in the header
section. the comparison uses ptr before it is moved past the varint, so the
remaining count is overstated by the width of the length field, and a negative
length (a varint with the high bit set) is not rejected at all. with a header
section sized to fill the receive buffer, either case lets a wire-supplied
length exceed the header bytes that are actually present.
bound the length against the position that follows the varint, reject a
negative length, and only advance ptr once those checks pass so the documented
advance-on-success behaviour still holds. regression tests covering both the
oversized and the negative length are added to ThrifttReadCheckTests.
This closes #3610
CVE: CVE-2026-58662
Upstream-Status: Backport [https://github.com/apache/thrift/commit/f961cdb44249c293fcce6a840ffa1f7419fd88d0]
Signed-off-by: Abhishek Bachiphale <Abhishek.Bachiphale@windriver.com>
---
.../src/thrift/transport/THeaderTransport.cpp | 12 +++--
lib/cpp/test/ThrifttReadCheckTests.cpp | 52 +++++++++++++++++++
2 files changed, 60 insertions(+), 4 deletions(-)
diff --git a/lib/cpp/src/thrift/transport/THeaderTransport.cpp b/lib/cpp/src/thrift/transport/THeaderTransport.cpp
index 117c8ed..ba2fd5d 100644
--- a/lib/cpp/src/thrift/transport/THeaderTransport.cpp
+++ b/lib/cpp/src/thrift/transport/THeaderTransport.cpp
@@ -183,13 +183,17 @@ void THeaderTransport::readString(uint8_t*& ptr,
int32_t strLen;
uint32_t bytes = readVarint32(ptr, &strLen, headerBoundary);
- if (strLen > headerBoundary - ptr) {
+ // Bound the string against the header bytes that remain once the length varint
+ // itself is accounted for, and reject a negative length so the size_t
+ // conversion in assign() below stays within the buffer. ptr is only advanced
+ // once these checks pass, keeping the "advances on success" contract above.
+ uint8_t* strStart = ptr + bytes;
+ if (strLen < 0 || strLen > headerBoundary - strStart) {
throw TTransportException(TTransportException::CORRUPTED_DATA,
"Info header length exceeds header size");
}
- ptr += bytes;
- str.assign(reinterpret_cast<const char*>(ptr), strLen);
- ptr += strLen;
+ str.assign(reinterpret_cast<const char*>(strStart), strLen);
+ ptr = strStart + strLen;
}
void THeaderTransport::readHeaderFormat(uint16_t headerSize, uint32_t sz) {
diff --git a/lib/cpp/test/ThrifttReadCheckTests.cpp b/lib/cpp/test/ThrifttReadCheckTests.cpp
index 9632861..2a92160 100644
--- a/lib/cpp/test/ThrifttReadCheckTests.cpp
+++ b/lib/cpp/test/ThrifttReadCheckTests.cpp
@@ -270,6 +270,58 @@ BOOST_AUTO_TEST_CASE(test_tthriftjsonprotocol_read_check_exception) {
protocol->readMapEnd();
}
+BOOST_AUTO_TEST_CASE(test_theadertransport_info_header_string_overrun) {
+ using apache::thrift::transport::THeaderTransport;
+ // Header-format frame whose info-header key length (4) does not fit within the
+ // header bytes that remain once the length varint itself is accounted for. The
+ // header section (8 bytes) exactly fills the frame, so the boundary sits at the
+ // buffer end; the key length has to be bounded against the remaining bytes and
+ // rejected.
+ uint8_t frame[] = {
+ 0x00, 0x00, 0x00, 0x12, // frame length = 18
+ 0x0F, 0xFF, 0x00, 0x00, // header magic
+ 0x00, 0x00, 0x00, 0x00, // seqId
+ 0x00, 0x02, // header size field (2 -> 8 bytes)
+ 0x02, // protocol id varint
+ 0x00, // num transforms = 0
+ 0x01, // info id = key/value
+ 0x01, // one key/value pair
+ 0x04, // key length = 4 (only 3 bytes remain)
+ 0xAA, 0xBB, 0xCC // key bytes
+ };
+ std::shared_ptr<TMemoryBuffer> buffer(new TMemoryBuffer(frame, sizeof(frame)));
+ std::shared_ptr<THeaderTransport> trans(new THeaderTransport(buffer));
+
+ uint8_t out[1];
+ BOOST_CHECK_THROW(trans->read(out, sizeof(out)), TTransportException);
+}
+
+BOOST_AUTO_TEST_CASE(test_theadertransport_info_header_string_negative_length) {
+ using apache::thrift::transport::THeaderTransport;
+ // Header-format frame whose info-header key length varint decodes to a
+ // negative int32 (top bit set). The length has to be treated as out of range
+ // rather than converted to a size_t, so the read is rejected instead of
+ // reaching the string assignment. The three trailing bytes only pad the
+ // header section out to its declared size and are never reached.
+ uint8_t frame[] = {
+ 0x00, 0x00, 0x00, 0x16, // frame length = 22
+ 0x0F, 0xFF, 0x00, 0x00, // header magic
+ 0x00, 0x00, 0x00, 0x00, // seqId
+ 0x00, 0x03, // header size field (3 -> 12 bytes)
+ 0x02, // protocol id varint
+ 0x00, // num transforms = 0
+ 0x01, // info id = key/value
+ 0x01, // one key/value pair
+ 0x80, 0x80, 0x80, 0x80, 0x08, // key length varint = INT32_MIN
+ 0x00, 0x00, 0x00 // padding to fill the header section
+ };
+ std::shared_ptr<TMemoryBuffer> buffer(new TMemoryBuffer(frame, sizeof(frame)));
+ std::shared_ptr<THeaderTransport> trans(new THeaderTransport(buffer));
+
+ uint8_t out[1];
+ BOOST_CHECK_THROW(trans->read(out, sizeof(out)), TTransportException);
+}
+
BOOST_AUTO_TEST_CASE(test_theadertransport_zlib_roundtrip) {
using apache::thrift::transport::THeaderTransport;
// A run of identical bytes compresses to far fewer bytes than it occupies
@@ -17,6 +17,7 @@ SRC_URI = "https://downloads.apache.org/${BPN}/${PV}/${BP}.tar.gz \
file://CVE-2026-58023.patch \
file://CVE-2026-48144.patch \
file://CVE-2026-58389.patch \
file://CVE-2026-58662.patch \
"
SRC_URI[sha256sum] = "794a0e455787960d9f27ab92c38e34da27e8deeda7a5db0e59dc64a00df8a1e5"