Last active
December 10, 2021 18:48
-
-
Save jwoglom/4e083835a2a2bb35b3e480acf22d23f2 to your computer and use it in GitHub Desktop.
AndroidBugPOC.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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