This is an automated email from the ASF dual-hosted git repository.
dongjoon-hyun pushed a commit to branch branch-4.1
in repository https://gitbox.apache.org/repos/asf/spark.git
The following commit(s) were added to refs/heads/branch-4.1 by this push:
new 7070360d3a2d [SPARK-57426][CORE] Validate length before allocation in
`Encoders.Strings.decode`
7070360d3a2d is described below
commit 7070360d3a2da8c6ac0cd6a09eb55fb7035504f5
Author: Dongjoon Hyun <[email protected]>
AuthorDate: Sat Jun 13 16:58:38 2026 -0700
[SPARK-57426][CORE] Validate length before allocation in
`Encoders.Strings.decode`
### What changes were proposed in this pull request?
`Encoders.Strings.decode()` now validates the length prefix before
allocating the array, requiring `0 <= length <= buf.readableBytes()` and
throwing `IndexOutOfBoundsException` otherwise. After `readInt()`, the reader
index is past the prefix, so `readableBytes()` equals the remaining payload
bytes.
- Note that we use the Java built-in `Objects.checkFromIndexSize` because
we cannot use **Netty's `checkReadableBytes`**. However, both methods throws
`IndexOutOfBoundsException` identically for the error cases.
-
https://netty.io/4.2/api/io/netty/buffer/AbstractByteBuf.html#checkReadableBytes(int)
-
https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/Objects.html#checkFromIndexSize(int,int,int)
### Why are the changes needed?
`decode()` allocated `new byte[length]` from an untrusted length.
- A negative value throws an opaque `NegativeArraySizeException`.
- An oversized value can trigger `OutOfMemoryError` on a corrupt or hostile
frame.
Validating first fails fast with a clear error.
Note that `OutOfMemoryError` can happen even in a secured environment at
the first parse on an unauthenticated channel.
https://github.com/apache/spark/blob/67eafbddf7962185b2399b97241a008cf8d0d333/common/network-common/src/main/java/org/apache/spark/network/sasl/SaslRpcHandler.java#L72-L80
https://github.com/apache/spark/blob/67eafbddf7962185b2399b97241a008cf8d0d333/common/network-common/src/main/java/org/apache/spark/network/sasl/SaslMessage.java#L68-L74
### Does this PR introduce _any_ user-facing change?
No.
### How was this patch tested?
Pass the CIs with newly added test cases.
Without this PR,
`testStringsDecodeShouldFailWhenLengthExceedsReadableBytes` fails with
`OutOfMemoryError`.
```
[info] Test
org.apache.spark.network.protocol.EncodersSuite#testStringsDecodeShouldFailWhenLengthExceedsReadableBytes()
started
[0.473s][warning][oom,vendor] java.lang.OutOfMemoryError occurred:
Requested array size exceeds VM limit
[error] java.lang.OutOfMemoryError: Requested array size exceeds VM limit
```
### Was this patch authored or co-authored using generative AI tooling?
Generated-by: Claude Code (Claude Opus 4.8)
Closes #56488 from dongjoon-hyun/SPARK-57426.
Authored-by: Dongjoon Hyun <[email protected]>
Signed-off-by: Dongjoon Hyun <[email protected]>
(cherry picked from commit 768569d6913be88bcb7af4a653ed2a52a0b568e7)
Signed-off-by: Dongjoon Hyun <[email protected]>
---
.../apache/spark/network/protocol/Encoders.java | 2 ++
.../spark/network/protocol/EncodersSuite.java | 22 ++++++++++++++++++++++
2 files changed, 24 insertions(+)
diff --git
a/common/network-common/src/main/java/org/apache/spark/network/protocol/Encoders.java
b/common/network-common/src/main/java/org/apache/spark/network/protocol/Encoders.java
index 8bab808ad686..488e36004f2f 100644
---
a/common/network-common/src/main/java/org/apache/spark/network/protocol/Encoders.java
+++
b/common/network-common/src/main/java/org/apache/spark/network/protocol/Encoders.java
@@ -20,6 +20,7 @@ package org.apache.spark.network.protocol;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
+import java.util.Objects;
import io.netty.buffer.ByteBuf;
import org.roaringbitmap.RoaringBitmap;
@@ -41,6 +42,7 @@ public class Encoders {
public static String decode(ByteBuf buf) {
int length = buf.readInt();
+ Objects.checkFromIndexSize(0, length, buf.readableBytes());
byte[] bytes = new byte[length];
buf.readBytes(bytes);
return new String(bytes, StandardCharsets.UTF_8);
diff --git
a/common/network-common/src/test/java/org/apache/spark/network/protocol/EncodersSuite.java
b/common/network-common/src/test/java/org/apache/spark/network/protocol/EncodersSuite.java
index 127835d29bc0..55b09b66aef6 100644
---
a/common/network-common/src/test/java/org/apache/spark/network/protocol/EncodersSuite.java
+++
b/common/network-common/src/test/java/org/apache/spark/network/protocol/EncodersSuite.java
@@ -48,6 +48,28 @@ public class EncodersSuite {
() -> Encoders.Bitmaps.encode(buf, bitmap));
}
+ @Test
+ public void testStringsEncodeDecode() {
+ String s = "spark";
+ ByteBuf buf = Unpooled.buffer(Encoders.Strings.encodedLength(s));
+ Encoders.Strings.encode(buf, s);
+ assertEquals(s, Encoders.Strings.decode(buf));
+ }
+
+ @Test
+ public void testStringsDecodeShouldFailWhenLengthIsNegative() {
+ ByteBuf buf = Unpooled.buffer();
+ buf.writeInt(-1);
+ assertThrows(IndexOutOfBoundsException.class, () ->
Encoders.Strings.decode(buf));
+ }
+
+ @Test
+ public void testStringsDecodeShouldFailWhenLengthExceedsReadableBytes() {
+ ByteBuf buf = Unpooled.buffer();
+ buf.writeInt(Integer.MAX_VALUE);
+ assertThrows(IndexOutOfBoundsException.class, () ->
Encoders.Strings.decode(buf));
+ }
+
@Test
public void testBitmapArraysEncodeDecode() {
RoaringBitmap[] bitmaps = new RoaringBitmap[] {
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]