Skip to content

Instantly share code, notes, and snippets.

@jwoglom
Last active December 10, 2021 18:48
Show Gist options
  • Save jwoglom/4e083835a2a2bb35b3e480acf22d23f2 to your computer and use it in GitHub Desktop.
Save jwoglom/4e083835a2a2bb35b3e480acf22d23f2 to your computer and use it in GitHub Desktop.
AndroidBugPOC.java
import java.util.*;
import java.lang.*;
/**
Output:
with 75 random hashcode items, 1 / 1000000 (9.999999999999999E-5%) of sorts triggered an exception
java.lang.IllegalArgumentException: Comparison method violates its general contract!
with 93 random hashcode items, 1 / 1000000 (9.999999999999999E-5%) of sorts triggered an exception
java.lang.IllegalArgumentException: Comparison method violates its general contract!
with 193 random hashcode items, 1 / 1000000 (9.999999999999999E-5%) of sorts triggered an exception
java.lang.IllegalArgumentException: Comparison method violates its general contract!
with 214 random hashcode items, 1 / 1000000 (9.999999999999999E-5%) of sorts triggered an exception
java.lang.IllegalArgumentException: Comparison method violates its general contract!
with 258 random hashcode items, 1 / 1000000 (9.999999999999999E-5%) of sorts triggered an exception
java.lang.IllegalArgumentException: Comparison method violates its general contract!
with 273 random hashcode items, 1 / 1000000 (9.999999999999999E-5%) of sorts triggered an exception
java.lang.IllegalArgumentException: Comparison method violates its general contract!
with 286 random hashcode items, 1 / 1000000 (9.999999999999999E-5%) of sorts triggered an exception
java.lang.IllegalArgumentException: Comparison method violates its general contract!
with 302 random hashcode items, 1 / 1000000 (9.999999999999999E-5%) of sorts triggered an exception
java.lang.IllegalArgumentException: Comparison method violates its general contract!
with 334 random hashcode items, 1 / 1000000 (9.999999999999999E-5%) of sorts triggered an exception
java.lang.IllegalArgumentException: Comparison method violates its general contract!
with 379 random hashcode items, 1 / 1000000 (9.999999999999999E-5%) of sorts triggered an exception
java.lang.IllegalArgumentException: Comparison method violates its general contract!
with 380 random hashcode items, 1 / 1000000 (9.999999999999999E-5%) of sorts triggered an exception
java.lang.IllegalArgumentException: Comparison method violates its general contract!
with 389 random hashcode items, 1 / 1000000 (9.999999999999999E-5%) of sorts triggered an exception
java.lang.IllegalArgumentException: Comparison method violates its general contract!
with 416 random hashcode items, 1 / 1000000 (9.999999999999999E-5%) of sorts triggered an exception
java.lang.IllegalArgumentException: Comparison method violates its general contract!
with 450 random hashcode items, 1 / 1000000 (9.999999999999999E-5%) of sorts triggered an exception
java.lang.IllegalArgumentException: Comparison method violates its general contract!
with 478 random hashcode items, 1 / 1000000 (9.999999999999999E-5%) of sorts triggered an exception
java.lang.IllegalArgumentException: Comparison method violates its general contract!
with 480 random hashcode items, 1 / 1000000 (9.999999999999999E-5%) of sorts triggered an exception
java.lang.IllegalArgumentException: Comparison method violates its general contract!
with 504 random hashcode items, 1 / 1000000 (9.999999999999999E-5%) of sorts triggered an exception
java.lang.IllegalArgumentException: Comparison method violates its general contract!
with 511 random hashcode items, 1 / 1000000 (9.999999999999999E-5%) of sorts triggered an exception
java.lang.IllegalArgumentException: Comparison method violates its general contract!
with 520 random hashcode items, 2 / 1000000 (1.9999999999999998E-4%) of sorts triggered an exception
java.lang.IllegalArgumentException: Comparison method violates its general contract!
java.lang.IllegalArgumentException: Comparison method violates its general contract!
*/
public class AndroidBugPOC {
static abstract class Thing {
public String toString() {
return getClass().getName() + " (" + hashCode() + ")";
}
}
static class HigherPriorityThing extends Thing {
}
static class LowerPriorityThing extends Thing {
}
// RandomHashcodeThing has a randomly-determined hashcode that stays constant
// for each instance of itself.
static class RandomHashcodeThing extends Thing {
private int hash;
RandomHashcodeThing() {
this.hash = new Random().ints(Integer.MIN_VALUE, Integer.MAX_VALUE).findFirst().getAsInt();;
}
public int hashCode() {
return hash;
}
}
// NormalHashcodeThing does not override hashCode.
static class NormalHashcodeThing extends Thing {
}
static class StaticHashcodeThing extends Thing {
public int hashCode() {
return 987654;
}
}
public static void main(String[] args) {
for (int i=0; i<1000; i++) {
doesException(i, 1000000);
}
}
public static int doesException(int thingCount, int testCount) {
List<Thing> things = new ArrayList<>();
things.add(new HigherPriorityThing());
things.add(new LowerPriorityThing());
for (int i=0; i<thingCount;i++) {
things.add(new RandomHashcodeThing());
}
int times = 0;
List<Exception> exceptions = new ArrayList<>();
for (int i=0; i<testCount; i++) {
try {
test(things);
} catch (IllegalArgumentException e) {
times++;
exceptions.add(e);
}
}
if (times > 0) {
System.out.println("with "+thingCount+" random hashcode items, "+times+" / "+testCount+" ("+((times*1.0/testCount)*100)+"%) of sorts triggered an exception");
for (Exception e : exceptions) {
System.out.println(e);
}
}
return times;
}
public static void test(List<Thing> things) {
things.sort((a, b) -> {
// This sort is meant to be a loose approximation of sortSimPhoneAccountsForEmergency:
// https://android.googlesource.com/platform/packages/services/Telecomm/+/725404c9d3c5fe4faf4afe87d96d624b3a511a77/src/com/android/server/telecom/CreateConnectionProcessor.java#587
// Prioritize HigherPriorityThing
if (a instanceof HigherPriorityThing ^ b instanceof HigherPriorityThing) {
return a instanceof HigherPriorityThing ? -1 : 1;
}
// Also prioritize LowerPriorityThing
if (a instanceof LowerPriorityThing ^ b instanceof LowerPriorityThing) {
return a instanceof LowerPriorityThing ? -1 : 1;
}
// Otherwise subtract hashcodes (BUG)
return a.hashCode() - b.hashCode();
// Fixed code:
// return Integer.compare(a.hashCode(), b.hashCode());
});
check(things);
}
public static void check(List<Thing> things) {
if (things.get(0) instanceof HigherPriorityThing && things.get(1) instanceof LowerPriorityThing) {
System.out.print("");
} else {
System.out.println("\nBAD - " + things);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment