This is an automated email from the ASF dual-hosted git repository.
uros-b pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/spark.git
The following commit(s) were added to refs/heads/master by this push:
new 9da9020ebc09 [SPARK-57659][CORE] Fix integer/long overflow in
ArrayWrappers numeric array key comparators
9da9020ebc09 is described below
commit 9da9020ebc092b8d5d18eb3d3066f50f0ca70f11
Author: YangJie <[email protected]>
AuthorDate: Thu Jun 25 00:15:44 2026 +0200
[SPARK-57659][CORE] Fix integer/long overflow in ArrayWrappers numeric
array key comparators
### What changes were proposed in this pull request?
`ComparableIntArray` and `ComparableLongArray` in `ArrayWrappers` compared
keys by subtracting elements (`array[i] - other.array[i]`). That subtraction
overflows for large opposite-sign values and flips the sign of the result.
Comparing `Integer.MIN_VALUE` with `Integer.MAX_VALUE` is enough to trigger it
for int, and `Long.MAX_VALUE - Long.MIN_VALUE` wraps around to `-1` for long.
The long version's `diff > 0 ? 1 : -1` looked like it dealt with this, but it
only kept the long differen [...]
### Why are the changes needed?
These wrappers order keys and index values in `InMemoryStore`.
`AppStatusStore` uses `int[]` keys (stage id and attempt id), so the int
comparator runs for real, but those ids are small so the overflow never
surfaced. No in-tree entity uses `long[]` keys today. The bug is real but
latent, and the fix keeps the ordering correct if larger values ever flow
through.
### Does this PR introduce _any_ user-facing change?
No. It only fixes orderings that were already wrong.
### How was this patch tested?
Added two cases to `ArrayWrappersSuite`, one per comparator, covering
`MIN`/`MAX` and other large opposite-sign values in both directions. To confirm
they actually guard the bug, I reverted each fix on its own and checked that
only that comparator's new test failed.
### Was this patch authored or co-authored using generative AI tooling?
No
Closes #56731 from LuciferYang/SPARK-kvstore-intarray-overflow.
Authored-by: YangJie <[email protected]>
Signed-off-by: Uros Bojanic <[email protected]>
---
.../apache/spark/util/kvstore/ArrayWrappers.java | 14 +++++---
.../spark/util/kvstore/ArrayWrappersSuite.java | 40 ++++++++++++++++++++++
2 files changed, 49 insertions(+), 5 deletions(-)
diff --git
a/common/kvstore/src/main/java/org/apache/spark/util/kvstore/ArrayWrappers.java
b/common/kvstore/src/main/java/org/apache/spark/util/kvstore/ArrayWrappers.java
index a9d6784805f6..477fbdb0d34c 100644
---
a/common/kvstore/src/main/java/org/apache/spark/util/kvstore/ArrayWrappers.java
+++
b/common/kvstore/src/main/java/org/apache/spark/util/kvstore/ArrayWrappers.java
@@ -83,13 +83,15 @@ class ArrayWrappers {
public int compareTo(ComparableIntArray other) {
int len = Math.min(array.length, other.array.length);
for (int i = 0; i < len; i++) {
- int diff = array[i] - other.array[i];
+ // Use Integer.compare; subtraction overflows for large opposite-sign
values
+ // (e.g. Integer.MIN_VALUE vs Integer.MAX_VALUE) and returns the wrong
ordering.
+ int diff = Integer.compare(array[i], other.array[i]);
if (diff != 0) {
return diff;
}
}
- return array.length - other.array.length;
+ return Integer.compare(array.length, other.array.length);
}
}
@@ -122,13 +124,15 @@ class ArrayWrappers {
public int compareTo(ComparableLongArray other) {
int len = Math.min(array.length, other.array.length);
for (int i = 0; i < len; i++) {
- long diff = array[i] - other.array[i];
+ // Use Long.compare; subtraction overflows for large opposite-sign
values
+ // (e.g. Long.MIN_VALUE vs Long.MAX_VALUE) and returns the wrong
ordering.
+ int diff = Long.compare(array[i], other.array[i]);
if (diff != 0) {
- return diff > 0 ? 1 : -1;
+ return diff;
}
}
- return array.length - other.array.length;
+ return Integer.compare(array.length, other.array.length);
}
}
diff --git
a/common/kvstore/src/test/java/org/apache/spark/util/kvstore/ArrayWrappersSuite.java
b/common/kvstore/src/test/java/org/apache/spark/util/kvstore/ArrayWrappersSuite.java
index 54bf5a2e60fd..031d844584de 100644
---
a/common/kvstore/src/test/java/org/apache/spark/util/kvstore/ArrayWrappersSuite.java
+++
b/common/kvstore/src/test/java/org/apache/spark/util/kvstore/ArrayWrappersSuite.java
@@ -56,4 +56,44 @@ public class ArrayWrappersSuite {
assertTrue(ArrayWrappers.forArray(s1).compareTo(ArrayWrappers.forArray(s2))
> 0);
}
+ @Test
+ public void testIntArrayCompareNoOverflow() {
+ // Large opposite-sign values overflow a subtraction-based comparison, so
these
+ // assertions fail unless compareTo uses Integer.compare.
+ Comparable<Object> min = ArrayWrappers.forArray(new int[] {
Integer.MIN_VALUE });
+ Comparable<Object> max = ArrayWrappers.forArray(new int[] {
Integer.MAX_VALUE });
+ Comparable<Object> negOne = ArrayWrappers.forArray(new int[] { -1 });
+
+ assertTrue(min.compareTo(max) < 0);
+ assertTrue(max.compareTo(min) > 0);
+ assertTrue(max.compareTo(negOne) > 0);
+ assertTrue(negOne.compareTo(max) < 0);
+
+ // Ordering must stay correct beyond the first element as well.
+ Comparable<Object> a = ArrayWrappers.forArray(new int[] { 0,
Integer.MIN_VALUE });
+ Comparable<Object> b = ArrayWrappers.forArray(new int[] { 0,
Integer.MAX_VALUE });
+ assertTrue(a.compareTo(b) < 0);
+ assertTrue(b.compareTo(a) > 0);
+ }
+
+ @Test
+ public void testLongArrayCompareNoOverflow() {
+ // Large opposite-sign values overflow a subtraction-based comparison, so
these
+ // assertions fail unless compareTo uses Long.compare.
+ Comparable<Object> min = ArrayWrappers.forArray(new long[] {
Long.MIN_VALUE });
+ Comparable<Object> max = ArrayWrappers.forArray(new long[] {
Long.MAX_VALUE });
+ Comparable<Object> negOne = ArrayWrappers.forArray(new long[] { -1L });
+
+ assertTrue(min.compareTo(max) < 0);
+ assertTrue(max.compareTo(min) > 0);
+ assertTrue(max.compareTo(negOne) > 0);
+ assertTrue(negOne.compareTo(max) < 0);
+
+ // Ordering must stay correct beyond the first element as well.
+ Comparable<Object> a = ArrayWrappers.forArray(new long[] { 0L,
Long.MIN_VALUE });
+ Comparable<Object> b = ArrayWrappers.forArray(new long[] { 0L,
Long.MAX_VALUE });
+ assertTrue(a.compareTo(b) < 0);
+ assertTrue(b.compareTo(a) > 0);
+ }
+
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]