-
Bug
-
Resolution: Fixed
-
P4
-
18
-
b11
-
generic
-
generic
-
Verified
Issue | Fix Version | Assignee | Priority | Status | Resolution | Resolved In Build |
---|---|---|---|---|---|---|
JDK-8326559 | 17.0.11 | Goetz Lindenmaier | P4 | Resolved | Fixed | b05 |
A DESCRIPTION OF THE PROBLEM :
The ModuleDescriptor hashCode implementation internally calls a modsHashCode method which assumes that sets iterate in a consistent order. By multiplying each sub hash by 43, the computation isn't commutative, leading to inconsistent results.
Under some conditions, this can cause two ModuleDescriptor instances which are equal to in fact be reported as not equal. The compareTo method still returns 0 in that case. The likelihood of this occurring is random, since it depends on enum hash codes, which vary between JVM sessions. This can cause hash collisions, resulting in inconsistent set ordering. In particular, the requires modifier sets are most sensitive to this.
This bug was introduced by https://bugs.openjdk.org/browse/JDK-8275509 which partially addressed the problem. The correct fix is to remove the multiply by 43. Note that the AbstactSet.hashCode method simply adds up the sub hashes without any multiplication step.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Construct two ModuleDescriptor instances which have modifier sets which have the same elements but iteration order differs.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The hash codes for equal ModuleDescriptors should be the same.
ACTUAL -
The hash codes are different:
551086747
1012586953
---------- BEGIN SOURCE ----------
import java.lang.module.ModuleDescriptor;
import java.util.Set;
public class Bug {
public static void main(String[] args) throws Exception {
var md1 = ModuleDescriptor.newModule("test")
.opens(Set.of(ModuleDescriptor.Opens.Modifier.SYNTHETIC,
ModuleDescriptor.Opens.Modifier.MANDATED),
"a.p1", Set.of("a.m1"))
.build();
var md2 = ModuleDescriptor.newModule("test")
.opens(Set.of(ModuleDescriptor.Opens.Modifier.MANDATED,
ModuleDescriptor.Opens.Modifier.SYNTHETIC),
"a.p1", Set.of("a.m1"))
.build();
System.out.println(md1.equals(md2));
System.out.println(md1.compareTo(md2));
System.out.println(md1.hashCode());
System.out.println(md2.hashCode());
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
In cases where the equals method is affected, the compareTo method can be called instead.
FREQUENCY : always
The ModuleDescriptor hashCode implementation internally calls a modsHashCode method which assumes that sets iterate in a consistent order. By multiplying each sub hash by 43, the computation isn't commutative, leading to inconsistent results.
Under some conditions, this can cause two ModuleDescriptor instances which are equal to in fact be reported as not equal. The compareTo method still returns 0 in that case. The likelihood of this occurring is random, since it depends on enum hash codes, which vary between JVM sessions. This can cause hash collisions, resulting in inconsistent set ordering. In particular, the requires modifier sets are most sensitive to this.
This bug was introduced by https://bugs.openjdk.org/browse/JDK-8275509 which partially addressed the problem. The correct fix is to remove the multiply by 43. Note that the AbstactSet.hashCode method simply adds up the sub hashes without any multiplication step.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Construct two ModuleDescriptor instances which have modifier sets which have the same elements but iteration order differs.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The hash codes for equal ModuleDescriptors should be the same.
ACTUAL -
The hash codes are different:
551086747
1012586953
---------- BEGIN SOURCE ----------
import java.lang.module.ModuleDescriptor;
import java.util.Set;
public class Bug {
public static void main(String[] args) throws Exception {
var md1 = ModuleDescriptor.newModule("test")
.opens(Set.of(ModuleDescriptor.Opens.Modifier.SYNTHETIC,
ModuleDescriptor.Opens.Modifier.MANDATED),
"a.p1", Set.of("a.m1"))
.build();
var md2 = ModuleDescriptor.newModule("test")
.opens(Set.of(ModuleDescriptor.Opens.Modifier.MANDATED,
ModuleDescriptor.Opens.Modifier.SYNTHETIC),
"a.p1", Set.of("a.m1"))
.build();
System.out.println(md1.equals(md2));
System.out.println(md1.compareTo(md2));
System.out.println(md1.hashCode());
System.out.println(md2.hashCode());
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
In cases where the equals method is affected, the compareTo method can be called instead.
FREQUENCY : always
- backported by
-
JDK-8326559 ModuleDescriptor.hashCode is inconsistent
-
- Resolved
-
- relates to
-
JDK-8275509 ModuleDescriptor.hashCode isn't reproducible across builds
-
- Closed
-
- links to
-
Commit openjdk/jdk17u-dev/f4fd2cbf
-
Commit openjdk/jdk/4cc6cb9d
-
Review openjdk/jdk17u-dev/2220
-
Review openjdk/jdk/9790
(1 links to)