InterFuzz is a fuzzing tool designed to systematically generate Java test cases with complex inter-class structures. InterFuzz introduces a novel concept of Heterogeneous Program Graph (HPG) to abstract and manipulate inter-class relationships. It then employs Inter-Class Mutators to construct intricate interactions, and utilizes Graph Complexity to guide test generation toward high-diversity code. Our evaluation shows that InterFuzz effectively uncovers compiler bugs that elude traditional fuzzers, having discovered 24 new bugs across HotSpot, ART, and R8. Among these, 20 bugs are confirmed by developers and 16 bugs hinge on intricate inter-class structures.
- Prepare target Java optimizing compiler
- Install Java as environment for InterFuzz
sudo apt update && apt install -y openjdk-17-jdk - Download InterFuzz and seeds from repository
- Run InterFuzz with the following command:
Example Command:
java -jar InterFuzz.jar [-jdk <arg>] [-seeds_path <arg>] [-target_seed <arg>] [-max_iter <arg>]
java -jar InterFuzz.jar --seeds_path seeds/JavaFuzzer/ --target_seed Test0001 --jdk path/to/targetJavaCompiler1/bin/,path/to/targetJavaCompiler2/bin/ --max_iter 400
In this work, we design a set of Inter-Class Mutators to systematically generate Java test cases with complex inter-class structures. These mutators work by expanding the Heterogeneous Program Graph (HPG) to create compilex inter-class structures, and are categorized into two types: mutators for adding nodes and mutators for adding edges.
- Mutators for Adding Node
- Add Class Node
This mutator adds a new class node (e.g. classes C0) to the HPG, creating a new class in the program without any inter-class structures. While adding a node to the HPG does not directly create inter-class structures, it provides essential building blocks for structures. In subsequent mutations, InterFuzz can apply other mutators to the new class C0. - Add Interface Node
This mutator adds a new interface node (e.g., interface I0) to the HPG. This corresponds to creating a new interface in the program. Similar to adding a class node, this provides a necessary component for subsequent mutations, such as adding implementation or inheritance edges. - Add Method Node
This mutator selects an existing class or interface node (e.g., C0) and adds a new method node to it. In the source code, this translates to declaring a new method within the scope of class C0. This new method serves as a target for subsequent mutations, such as creating method calls (Reference between classes or interfaces). - Add Field Node
This mutator selects an existing class node (e.g., C0) and adds a new field node to it. This corresponds to declaring a new member variable (field) in the source code of class C0. Fields are fundamental to a class's state and are prerequisites for creating Reference edges (e.g., a field in one class referencing an object of another).
- Add Class Node
- Mutators for Adding Edge
- Add Inheritance Edge
This mutator is designed to introduce Inheritance structures into a program. InterFuzz selects two compatible nodes (classes C1 and C2) and connects them with an Inheritance edge, which translates to anextendsdeclaration in the source code. - Add Interface Implementation Edge
This mutator is designed to introduce Interface Implementation structures. InterFuzz selects a class node C1 and an interface node I1 and connects them with an Interface Implementation edge. This translates to adding animplementskeyword to the declaration of class C1 in the source code. - Add Nesting Edge
This mutator is designed to create Nesting structures. It models this as a code refactoring operation on the HPG, represented by adding a directed nesting edge from an "outer" class to an "inner" class. For instance, adding an edge from C1 to C2 corresponds to restructuring the source code, transforming class C2 into a nested type within the scope of class C1. - Add Generic Bounds Edge
This mutator introduces Generic Bounds structures. It selects a generic type parameter (e.g., T) and a class or interface node (e.g., C1) to serve as its bound. It then adds a Generic Bounds edge, which translates to a bounded type parameter like<T extends C1>in the source code. - Add Reference Edge This mutator is designed to create Reference structures, representing a dependency or association. InterFuzz selects two nodes (e.g., C1 and C2) and adds a directed Reference edge from C1 to C2. In the source code, this typically translates to C1 invoke methods defined in C2, access fields in C2, or create objects of C2.
- Add Inheritance Edge
InterFuzz has discovered 24 new bugs across three major Java compilers: HotSpot (4 bugs), ART (7 bugs), and R8 (13 bugs). Among these, 20 bugs have been confirmed by developers, with 13 bugs already fixed. The table below provides a detailed breakdown of all detected bugs, including their symptoms, current status, whether they are related to inter-class structures, and priority levels.
| No. | Bug ID | Compiler | Symptom | Status | Inter-Class Related |
Priority |
|---|---|---|---|---|---|---|
| 1 | 8357782 | HotSpot | Semantic Error | Fixed | ✅ | P3 |
| 2 | 8361699 | HotSpot | Crash | Fixed | ✅ | P3 |
| 3 | 8357381 | HotSpot | Crash | Confirmed | ✅ | P3 |
| 4 | 8357242 | HotSpot | Semantic Error | Duplicate | ❌ | P4 |
| 5 | 405152615 | ART | Semantic Error | Fixed | ❌ | P2 |
| 6 | 418897611 | ART | Semantic Error | Confirmed | ✅ | P2 |
| 7 | 369670481 | ART | Semantic Error | Confirmed | ✅ | P3 |
| 8 | 369739225 | ART | Semantic Error | Confirmed | ✅ | P3 |
| 9 | 405149431 | ART | Semantic Error | Confirmed | ❌ | P3 |
| 10 | 410253904 | ART | Semantic Error | Confirmed | ❌ | P3 |
| 11 | 405149432 | ART | Semantic Error | Duplicate | ✅ | P3 |
| 12 | 412524379 | R8 | Crash | Fixed | ✅ | P1 |
| 13 | 419464490 | R8 | Crash | Fixed | ✅ | P1 |
| 14 | 379347946 | R8 | Semantic Error | Fixed | ✅ | P1 |
| 15 | 420228751 | R8 | Semantic Error | Fixed | ✅ | P2 |
| 16 | 367915233 | R8 | Semantic Error | Fixed | ✅ | P2 |
| 17 | 426351560 | R8 | Semantic Error | Fixed | ✅ | P2 |
| 18 | 418719343 | R8 | Semantic Error | Fixed | ✅ | P2 |
| 19 | 419404081 | R8 | Semantic Error | Confirmed | ✅ | P2 |
| 20 | 372806451 | R8 | Semantic Error | Duplicate | ✅ | P2 |
- Bug Link: https://bugs.openjdk.org/browse/JDK-8357782
- Symptom: Semantic Error
- Status: Fixed
- Priority: P3
- Test Case:
class C2 { class C3 { static String field; static void foo() { new C4(field); } } static class C4 { static { C3.field = "Hello"; } C4(String arg1) { System.out.print(arg1); } } } class C1 { public static void main(String[] args) { C2.C3.foo(); } }
This bug's root cause is a faulty optimization in the JVM's JIT compiler that fails to handle side effects within Reference inter-class structure. The code establishes a critical reference where the static initializer of class C4 modifies a static field in class C3. The JIT compiler's optimization pass fails to recognize this inter-class interaction. When compiling the C2.C3.foo() method, it reads the initially null value of C2.C3.field for the constructor argument. It then incorrectly assumes the new C4() operation has no side effects on this field, failing to account for the fact that this operation's own static initializer is what assigns "Hello" to the field. As a result, the faulty optimization uses the stale null value, causing the incorrect output. The bug is triggered directly by the compiler's inability to track state changes across these tightly-coupled class structures.
- Bug Link: https://issuetracker.google.com/issues/426351560
- Symptom: Semantic Error
- Status: Fixed
- Priority: P2
- Test Case:
public class C1 implements I { public void foo2() { System.out.println("Cr.foo2()"); } } interface I { default String foo() { System.out.println("I.foo2()"); } } class C0 extends C1 { public static void main(java.lang.String[] strArr) { C0 c = new C0(); c.foo(); } }
The root cause of this bug lies in the R8 compiler's failure to correctly handle a complex inter-class structure. This structure is formed by a mix of inheritance (C0 extends C1) and interface implementation (C1 implements I). Within this structure, C0 is meant to access foo() from interface I indirectly, through its parent class C1. This creates a clear invocation chain: C0 → C1 → I. However, when generating compatibility code, the R8 compiler failed to respect this chain. It improperly bypassed the intermediary class C1. Instead, it generated invalid code that caused C0 to attempt a direct call to a method on interface I. This attempt to bypass the class hierarchy violates Java's fundamental access control rules. As a result, the program crashes at runtime with an IllegalAccessError.