mirror of
https://github.com/openembedded/meta-openembedded.git
synced 2026-09-26 00:11:04 +00:00
thrift: fix CVE-2026-45112
Backport patch to fix CVE-2026-45112. References: https://nvd.nist.gov/vuln/detail/CVE-2026-45112 Upstream fix: https://github.com/apache/thrift/commit/16b3673827498d1f4b05f8026d5d580b8e8f5aa2 Signed-off-by: Adarsh Jagadish Kamini <adarsh.jagadish.kamini@est.tech> Signed-off-by: Anuj Mittal <anuj.mittal@oss.qualcomm.com>
This commit is contained in:
@@ -0,0 +1,306 @@
|
||||
From 4d68f215f05905a19dd43865fb97d170b4fc1a3b Mon Sep 17 00:00:00 2001
|
||||
From: Jens Geyer <jensg@apache.org>
|
||||
Date: Fri, 8 May 2026 21:24:30 +0200
|
||||
Subject: [PATCH] Add message byte tracking to consumeBuffer() in Java
|
||||
transports Client: java
|
||||
|
||||
- consumeBuffer() in TMemoryInputTransport and AutoExpandingBufferReadTransport
|
||||
now decrements remainingMessageSize inline so fast-path buffer reads are
|
||||
counted toward the per-message size limit
|
||||
- AutoExpandingBufferReadTransport.fill() resets remainingMessageSize at the
|
||||
start of each frame, matching TMemoryInputTransport.reset() behavior in
|
||||
TFramedTransport
|
||||
- TBinaryProtocol.readString() now checks stringLengthLimit before the
|
||||
fast-path branch
|
||||
- Add TestMessageSizeLimits covering all fixed code paths
|
||||
|
||||
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
||||
|
||||
CVE: CVE-2026-45112
|
||||
Upstream-Status: Backport [https://github.com/apache/thrift/commit/16b3673827498d1f4b05f8026d5d580b8e8f5aa2]
|
||||
|
||||
Signed-off-by: Adarsh Jagadish Kamini <adarsh.jagadish.kamini@est.tech>
|
||||
---
|
||||
.../thrift/protocol/TBinaryProtocol.java | 6 +-
|
||||
.../AutoExpandingBufferReadTransport.java | 6 +
|
||||
.../transport/TMemoryInputTransport.java | 7 +-
|
||||
.../protocol/TestMessageSizeLimits.java | 196 ++++++++++++++++++
|
||||
4 files changed, 213 insertions(+), 2 deletions(-)
|
||||
create mode 100644 lib/java/src/test/java/org/apache/thrift/protocol/TestMessageSizeLimits.java
|
||||
|
||||
diff --git a/lib/java/src/main/java/org/apache/thrift/protocol/TBinaryProtocol.java b/lib/java/src/main/java/org/apache/thrift/protocol/TBinaryProtocol.java
|
||||
index 99c3e9302..49e82ee8a 100644
|
||||
--- a/lib/java/src/main/java/org/apache/thrift/protocol/TBinaryProtocol.java
|
||||
+++ b/lib/java/src/main/java/org/apache/thrift/protocol/TBinaryProtocol.java
|
||||
@@ -458,6 +458,8 @@ public class TBinaryProtocol extends TProtocol {
|
||||
public String readString() throws TException {
|
||||
int size = readI32();
|
||||
|
||||
+ checkStringReadLength(size);
|
||||
+
|
||||
if (trans_.getBytesRemainingInBuffer() >= size) {
|
||||
String s =
|
||||
new String(trans_.getBuffer(), trans_.getBufferPosition(), size, StandardCharsets.UTF_8);
|
||||
@@ -465,7 +467,9 @@ public class TBinaryProtocol extends TProtocol {
|
||||
return s;
|
||||
}
|
||||
|
||||
- return readStringBody(size);
|
||||
+ byte[] buf = new byte[size];
|
||||
+ trans_.readAll(buf, 0, size);
|
||||
+ return new String(buf, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
public String readStringBody(int size) throws TException {
|
||||
diff --git a/lib/java/src/main/java/org/apache/thrift/transport/AutoExpandingBufferReadTransport.java b/lib/java/src/main/java/org/apache/thrift/transport/AutoExpandingBufferReadTransport.java
|
||||
index d59c657db..53b35c207 100644
|
||||
--- a/lib/java/src/main/java/org/apache/thrift/transport/AutoExpandingBufferReadTransport.java
|
||||
+++ b/lib/java/src/main/java/org/apache/thrift/transport/AutoExpandingBufferReadTransport.java
|
||||
@@ -39,6 +39,7 @@ public class AutoExpandingBufferReadTransport extends TEndpointTransport {
|
||||
inTrans.readAll(buf.array(), 0, length);
|
||||
pos = 0;
|
||||
limit = length;
|
||||
+ resetConsumedMessageSize(-1);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -70,6 +71,11 @@ public class AutoExpandingBufferReadTransport extends TEndpointTransport {
|
||||
@Override
|
||||
public final void consumeBuffer(int len) {
|
||||
pos += len;
|
||||
+ if (remainingMessageSize >= len) {
|
||||
+ remainingMessageSize -= len;
|
||||
+ } else {
|
||||
+ remainingMessageSize = 0;
|
||||
+ }
|
||||
}
|
||||
|
||||
@Override
|
||||
diff --git a/lib/java/src/main/java/org/apache/thrift/transport/TMemoryInputTransport.java b/lib/java/src/main/java/org/apache/thrift/transport/TMemoryInputTransport.java
|
||||
index 375e2b72a..2fff0dacc 100644
|
||||
--- a/lib/java/src/main/java/org/apache/thrift/transport/TMemoryInputTransport.java
|
||||
+++ b/lib/java/src/main/java/org/apache/thrift/transport/TMemoryInputTransport.java
|
||||
@@ -96,7 +96,6 @@ public final class TMemoryInputTransport extends TEndpointTransport {
|
||||
if (amtToRead > 0) {
|
||||
System.arraycopy(buf_, pos_, buf, off, amtToRead);
|
||||
consumeBuffer(amtToRead);
|
||||
- countConsumedMessageBytes(amtToRead);
|
||||
}
|
||||
return amtToRead;
|
||||
}
|
||||
@@ -119,7 +118,13 @@ public final class TMemoryInputTransport extends TEndpointTransport {
|
||||
return endPos_ - pos_;
|
||||
}
|
||||
|
||||
+ @Override
|
||||
public void consumeBuffer(int len) {
|
||||
pos_ += len;
|
||||
+ if (remainingMessageSize >= len) {
|
||||
+ remainingMessageSize -= len;
|
||||
+ } else {
|
||||
+ remainingMessageSize = 0;
|
||||
+ }
|
||||
}
|
||||
}
|
||||
diff --git a/lib/java/src/test/java/org/apache/thrift/protocol/TestMessageSizeLimits.java b/lib/java/src/test/java/org/apache/thrift/protocol/TestMessageSizeLimits.java
|
||||
new file mode 100644
|
||||
index 000000000..ab12a0f70
|
||||
--- /dev/null
|
||||
+++ b/lib/java/src/test/java/org/apache/thrift/protocol/TestMessageSizeLimits.java
|
||||
@@ -0,0 +1,196 @@
|
||||
+/*
|
||||
+ * Licensed to the Apache Software Foundation (ASF) under one
|
||||
+ * or more contributor license agreements. See the NOTICE file
|
||||
+ * distributed with this work for additional information
|
||||
+ * regarding copyright ownership. The ASF licenses this file
|
||||
+ * to you under the Apache License, Version 2.0 (the
|
||||
+ * "License"); you may not use this file except in compliance
|
||||
+ * with the License. You may obtain a copy of the License at
|
||||
+ *
|
||||
+ * http://www.apache.org/licenses/LICENSE-2.0
|
||||
+ *
|
||||
+ * Unless required by applicable law or agreed to in writing,
|
||||
+ * software distributed under the License is distributed on an
|
||||
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
+ * KIND, either express or implied. See the License for the
|
||||
+ * specific language governing permissions and limitations
|
||||
+ * under the License.
|
||||
+ */
|
||||
+package org.apache.thrift.protocol;
|
||||
+
|
||||
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
+import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
+
|
||||
+import java.util.Arrays;
|
||||
+import org.apache.thrift.TConfiguration;
|
||||
+import org.apache.thrift.transport.AutoExpandingBufferReadTransport;
|
||||
+import org.apache.thrift.transport.TMemoryInputTransport;
|
||||
+import org.apache.thrift.transport.TTransportException;
|
||||
+import org.junit.jupiter.api.Test;
|
||||
+
|
||||
+/**
|
||||
+ * Tests that message size limits are enforced in the fast-path buffer reads of
|
||||
+ * TBinaryProtocol/TCompactProtocol and that consumeBuffer() correctly tracks consumed bytes.
|
||||
+ */
|
||||
+public class TestMessageSizeLimits {
|
||||
+
|
||||
+ /** Build TBinaryProtocol-encoded bytes for a string: 4-byte big-endian length + raw bytes. */
|
||||
+ private byte[] encodeBinaryString(String s) {
|
||||
+ byte[] data = s.getBytes(java.nio.charset.StandardCharsets.UTF_8);
|
||||
+ byte[] out = new byte[4 + data.length];
|
||||
+ int len = data.length;
|
||||
+ out[0] = (byte) (len >> 24);
|
||||
+ out[1] = (byte) (len >> 16);
|
||||
+ out[2] = (byte) (len >> 8);
|
||||
+ out[3] = (byte) len;
|
||||
+ System.arraycopy(data, 0, out, 4, data.length);
|
||||
+ return out;
|
||||
+ }
|
||||
+
|
||||
+ /**
|
||||
+ * Build TCompactProtocol-encoded bytes for a string: varint length + raw bytes. Assumes length <
|
||||
+ * 128 so it fits in one varint byte.
|
||||
+ */
|
||||
+ private byte[] encodeCompactString(String s) {
|
||||
+ byte[] data = s.getBytes(java.nio.charset.StandardCharsets.UTF_8);
|
||||
+ assert data.length < 128 : "test helper only handles single-byte varints";
|
||||
+ byte[] out = new byte[1 + data.length];
|
||||
+ out[0] = (byte) data.length;
|
||||
+ System.arraycopy(data, 0, out, 1, data.length);
|
||||
+ return out;
|
||||
+ }
|
||||
+
|
||||
+ private static String repeat(char c, int n) {
|
||||
+ char[] chars = new char[n];
|
||||
+ Arrays.fill(chars, c);
|
||||
+ return new String(chars);
|
||||
+ }
|
||||
+
|
||||
+ @Test
|
||||
+ public void testBinaryProtocol_stringLengthLimitEnforcedInFastPath() throws Exception {
|
||||
+ // 100-char string encoded for TBinaryProtocol - all bytes in transport so fast path fires
|
||||
+ byte[] buf = encodeBinaryString(repeat('A', 100));
|
||||
+ TMemoryInputTransport transport = new TMemoryInputTransport(buf);
|
||||
+
|
||||
+ // Protocol limited to 10 bytes per string
|
||||
+ TBinaryProtocol proto = new TBinaryProtocol(transport, 10L, -1L, false, true);
|
||||
+
|
||||
+ assertThrows(
|
||||
+ TProtocolException.class,
|
||||
+ proto::readString,
|
||||
+ "stringLengthLimit must be enforced even when the fast path is taken");
|
||||
+ }
|
||||
+
|
||||
+ @Test
|
||||
+ public void testBinaryProtocol_stringLengthLimitAllowsValidString() throws Exception {
|
||||
+ byte[] buf = encodeBinaryString("Hello");
|
||||
+ TMemoryInputTransport transport = new TMemoryInputTransport(buf);
|
||||
+
|
||||
+ TBinaryProtocol proto = new TBinaryProtocol(transport, 10L, -1L, false, true);
|
||||
+
|
||||
+ assertDoesNotThrow(
|
||||
+ proto::readString, "string within stringLengthLimit must be readable via fast path");
|
||||
+ }
|
||||
+
|
||||
+ @Test
|
||||
+ public void testCompactProtocol_stringLengthLimitEnforcedInFastPath() throws Exception {
|
||||
+ byte[] buf = encodeCompactString(repeat('A', 100));
|
||||
+ TMemoryInputTransport transport = new TMemoryInputTransport(buf);
|
||||
+
|
||||
+ TCompactProtocol proto = new TCompactProtocol(transport, 10L, -1L);
|
||||
+
|
||||
+ assertThrows(
|
||||
+ TProtocolException.class,
|
||||
+ proto::readString,
|
||||
+ "TCompactProtocol stringLengthLimit must reject oversized strings in fast path");
|
||||
+ }
|
||||
+
|
||||
+ @Test
|
||||
+ public void testConsumeBuffer_decrementsRemainingMessageSize() throws Exception {
|
||||
+ // 20-byte transport; updateKnownMessageSize sets remainingMessageSize = 20
|
||||
+ byte[] buf = new byte[20];
|
||||
+ TMemoryInputTransport transport = new TMemoryInputTransport(buf);
|
||||
+
|
||||
+ transport.consumeBuffer(15);
|
||||
+
|
||||
+ // 5 bytes remain; requesting 5 must succeed
|
||||
+ assertDoesNotThrow(() -> transport.checkReadBytesAvailable(5));
|
||||
+
|
||||
+ // requesting 6 must fail
|
||||
+ assertThrows(
|
||||
+ TTransportException.class,
|
||||
+ () -> transport.checkReadBytesAvailable(6),
|
||||
+ "checkReadBytesAvailable must reflect bytes consumed via consumeBuffer");
|
||||
+ }
|
||||
+
|
||||
+ @Test
|
||||
+ public void testBinaryProtocol_fastPathReadsDrainRemainingMessageSize() throws Exception {
|
||||
+ // 10 i32 values = 40 bytes; maxMessageSize exactly 40
|
||||
+ byte[] buf = new byte[40];
|
||||
+ TConfiguration config = TConfiguration.custom().setMaxMessageSize(40).build();
|
||||
+ TMemoryInputTransport transport = new TMemoryInputTransport(config, buf);
|
||||
+ TBinaryProtocol proto = new TBinaryProtocol(transport);
|
||||
+
|
||||
+ // Reading 8 i32 values via fast path consumes 32 bytes
|
||||
+ for (int i = 0; i < 8; i++) {
|
||||
+ proto.readI32();
|
||||
+ }
|
||||
+
|
||||
+ // 8 bytes remain — requesting exactly 8 must succeed
|
||||
+ assertDoesNotThrow(
|
||||
+ () -> transport.checkReadBytesAvailable(8),
|
||||
+ "8 bytes should still be available after 32 consumed via fast path");
|
||||
+
|
||||
+ // requesting 9 must fail — size limit is properly tracked
|
||||
+ assertThrows(
|
||||
+ TTransportException.class,
|
||||
+ () -> transport.checkReadBytesAvailable(9),
|
||||
+ "fast-path reads must decrement remaining message size so limits can be enforced");
|
||||
+ }
|
||||
+
|
||||
+ @Test
|
||||
+ public void testRead_doesNotDoubleCountConsumedBytes() throws Exception {
|
||||
+ // If read() double-counted, consuming 10 bytes via read() on a 20-byte transport
|
||||
+ // would wrongly decrement remainingMessageSize by 20 instead of 10.
|
||||
+ byte[] buf = new byte[20];
|
||||
+ TMemoryInputTransport transport = new TMemoryInputTransport(buf);
|
||||
+ byte[] dest = new byte[10];
|
||||
+ transport.read(dest, 0, 10);
|
||||
+
|
||||
+ // 10 bytes remain; requesting 10 must succeed
|
||||
+ assertDoesNotThrow(() -> transport.checkReadBytesAvailable(10));
|
||||
+
|
||||
+ // requesting 11 must fail
|
||||
+ assertThrows(
|
||||
+ TTransportException.class,
|
||||
+ () -> transport.checkReadBytesAvailable(11),
|
||||
+ "read() must not double-count consumed bytes after consumeBuffer fix");
|
||||
+ }
|
||||
+
|
||||
+ @Test
|
||||
+ public void testAutoExpandingBufferReadTransport_fillResetsMessageSizePerFrame()
|
||||
+ throws Exception {
|
||||
+ // fill() must reset remainingMessageSize so that consumption across frames on a long-lived
|
||||
+ // TFastFramedTransport connection does not accumulate toward the per-message limit.
|
||||
+ TConfiguration config = TConfiguration.custom().setMaxMessageSize(80).build();
|
||||
+ AutoExpandingBufferReadTransport readBuf = new AutoExpandingBufferReadTransport(config, 100);
|
||||
+ TMemoryInputTransport source = new TMemoryInputTransport(new byte[200]);
|
||||
+
|
||||
+ // Frame 1: fill and consume 70 bytes
|
||||
+ readBuf.fill(source, 70);
|
||||
+ readBuf.consumeBuffer(70);
|
||||
+
|
||||
+ // Frame 2: fill resets remainingMessageSize to maxMessageSize (80) before new frame
|
||||
+ readBuf.fill(source, 70);
|
||||
+ readBuf.consumeBuffer(70);
|
||||
+
|
||||
+ // After two frames of 70 bytes each, 10 bytes of budget must still be available
|
||||
+ assertDoesNotThrow(
|
||||
+ () -> readBuf.checkReadBytesAvailable(10),
|
||||
+ "fill() must reset remainingMessageSize so multi-frame connections stay functional");
|
||||
+ assertThrows(
|
||||
+ TTransportException.class,
|
||||
+ () -> readBuf.checkReadBytesAvailable(11),
|
||||
+ "remaining budget after fill-reset and 70-byte consume should be exactly 10");
|
||||
+ }
|
||||
+}
|
||||
@@ -19,6 +19,7 @@ SRC_URI = "https://downloads.apache.org/${BPN}/${PV}/${BP}.tar.gz \
|
||||
file://CVE-2026-58389.patch \
|
||||
file://CVE-2026-58662.patch \
|
||||
file://CVE-2026-41608.patch \
|
||||
file://CVE-2026-45112.patch \
|
||||
"
|
||||
SRC_URI[sha256sum] = "794a0e455787960d9f27ab92c38e34da27e8deeda7a5db0e59dc64a00df8a1e5"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user