This is an automated email from the ASF dual-hosted git repository.
morrySnow pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/master by this push:
new 6a2f56a1d98 [fix](fe) Bound length in MysqlProto.readLenEncodedString
(#63604)
6a2f56a1d98 is described below
commit 6a2f56a1d98c69441e6005e337035daa2299b615
Author: MarkLee131 <[email protected]>
AuthorDate: Wed Jun 3 14:46:24 2026 +0800
[fix](fe) Bound length in MysqlProto.readLenEncodedString (#63604)
### What problem does this PR solve?
Issue Number: close #63603
Problem Summary:
`MysqlProto.readLenEncodedString` reads a length-encoded integer and
passes it straight to `new byte[(int) length]` with no bound. The length
is fully attacker-controlled (a `0xFE` lead byte carries an 8-byte
value), and it is read before authentication from
`MysqlAuthPacket.readFrom` (the auth-response field at
`MysqlAuthPacket.java:93` and the connection-attributes loop at
`MysqlAuthPacket.java:110-118`). A small handshake response can
therefore request
a ~2 GiB allocation, and a length with the high bit set casts to a
negative size (`NegativeArraySizeException`).
This PR rejects a length that is negative or larger than the bytes
remaining in the buffer before allocating. A well-formed length-encoded
string's payload always fits in the remaining buffer, so valid input is
unaffected. One guard covers both reach paths.
---
.../java/org/apache/doris/mysql/MysqlProto.java | 8 +++
.../doris/mysql/MysqlProtoLenEncStringTest.java | 61 ++++++++++++++++++++++
2 files changed, 69 insertions(+)
diff --git a/fe/fe-core/src/main/java/org/apache/doris/mysql/MysqlProto.java
b/fe/fe-core/src/main/java/org/apache/doris/mysql/MysqlProto.java
index 4f8914ef9fc..4e495a428ed 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/mysql/MysqlProto.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/mysql/MysqlProto.java
@@ -329,6 +329,14 @@ public class MysqlProto {
public static byte[] readLenEncodedString(ByteBuffer buffer) {
long length = readVInt(buffer);
+ // The string payload must fit in the bytes actually remaining in the
packet.
+ // Without this bound an attacker-controlled length is passed straight
to
+ // new byte[(int) length], where the (int) cast can go negative or
request
+ // up to ~2 GiB from a few bytes on the wire.
+ if (length < 0 || length > buffer.remaining()) {
+ throw new IllegalArgumentException("invalid length-encoded string
length: " + length
+ + ", remaining: " + buffer.remaining());
+ }
byte[] buf = new byte[(int) length];
buffer.get(buf);
return buf;
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/mysql/MysqlProtoLenEncStringTest.java
b/fe/fe-core/src/test/java/org/apache/doris/mysql/MysqlProtoLenEncStringTest.java
new file mode 100644
index 00000000000..237632eb00b
--- /dev/null
+++
b/fe/fe-core/src/test/java/org/apache/doris/mysql/MysqlProtoLenEncStringTest.java
@@ -0,0 +1,61 @@
+// 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.doris.mysql;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+
+public class MysqlProtoLenEncStringTest {
+
+ // A length-encoded string whose declared length exceeds the bytes actually
+ // present must be rejected, not passed to new byte[(int) length]. Before
the
+ // bound, a 0xFE lead byte plus an 8-byte length let a handful of bytes
request
+ // ~2 GiB, and a high-bit length cast to a negative size.
+ @Test
+ public void readLenEncodedStringRejectsOversizedLength() {
+ ByteBuffer buffer = ByteBuffer.allocate(9);
+ buffer.put((byte) 0xFE); // 8-byte length follows
+ buffer.put(new byte[] {(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte)
0x7F, 0, 0, 0, 0}); // 0x7FFFFFFF
+ buffer.flip();
+ Assert.assertThrows(IllegalArgumentException.class,
+ () -> MysqlProto.readLenEncodedString(buffer));
+ }
+
+ @Test
+ public void readLenEncodedStringRejectsNegativeCastLength() {
+ ByteBuffer buffer = ByteBuffer.allocate(9);
+ buffer.put((byte) 0xFE);
+ buffer.put(new byte[] {(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte)
0xFF, 0, 0, 0, 0}); // (int) -> -1
+ buffer.flip();
+ Assert.assertThrows(IllegalArgumentException.class,
+ () -> MysqlProto.readLenEncodedString(buffer));
+ }
+
+ @Test
+ public void readLenEncodedStringAcceptsValidPayload() {
+ byte[] payload = "abc".getBytes(StandardCharsets.UTF_8);
+ ByteBuffer buffer = ByteBuffer.allocate(1 + payload.length);
+ buffer.put((byte) payload.length); // single-byte length < 251
+ buffer.put(payload);
+ buffer.flip();
+ Assert.assertArrayEquals(payload,
MysqlProto.readLenEncodedString(buffer));
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]