- The paper introduces Petrify, a verification tool that encodes Java bytecode into Petri nets for automatic detection of concurrency faults like deadlocks.
- It leverages a custom intermediate representation and integrates with the LoLA model checker to achieve competitive precision against tools like JaDA and JPF.
- Experimental results on diverse Java and Kotlin artifacts validate its scalability and forward compatibility with evolving JVM language features.
Petri-Net Based Bytecode-Level Analysis of Java Concurrency
Introduction
The paper "Petrify: Petri-net Based Analysis of Concurrency Properties in Java Bytecode" (2607.00830) introduces a verification technique and tool for the analysis of concurrency properties in JVM-based programs, with a primary focus on Java and selected Kotlin artifacts. The proposed methodology, encapsulated in the Petrify system, leverages an encoding of Java bytecode-level concurrency semantics into Petri nets, enabling the automatic analysis of concurrency faults, notably deadlocks, via state-of-the-art Petri net model checkers (such as LoLA).
Methodology
Petrify’s analysis pipeline is distinctive in several key ways:
- Bytecode-Level Analysis: By operating on bytecode (specifically Soot's Jimple IR), the approach decouples tool applicability from Java language version idiosyncrasies. Thus, it maintains compatibility as the underlying language evolves, including support for a subset of Kotlin.
- Custom Intermediate Representation: The translation of bytecode to Petri nets is mediated by Rock Bottom (RB), a flow- and context-sensitive (but path-insensitive) imperative intermediate language. This design reduces the semantic gap and supports over-approximation, ensuring potential deadlocks are not omitted except in well-defined cases (e.g., reentrant locks are not fully encoded).
- Petri Net Encoding: The RB program is compiled into a low-level Petri net that precisely models the control flow, thread creation, lock acquisition/release, and procedure call/return steps. The Petri net marking captures the global state of thread execution and locking. Locks are tracked per-thread and per-variable using a fixed set of places. The encoding includes special monitoring constructs and supports the expression of key concurrency properties (termination, deadlock, livelock) as temporal logic formulas interpreted over the Petri net.
- Tool Integration: Petrify incorporates the Soot framework for bytecode analysis and targets the LoLA Petri net model checker, converting non-trivial concurrent bytecode into succinct, analyzable Petri nets.
The soundness of the translation pipeline is explicitly formalized: under precise alias analysis and absence of unsupported features (notably reentrant locks, unresolved invokedynamic, and exceptions), the Petri net is a sound over-approximation of the possible executions of the original Java program.
Path-insensitivity is controlled—conditional branches are translated into nondeterministic choices, ensuring all feasible paths are present (possibly introducing spurious paths, but not missing actual deadlocks). Procedure calls are context-sensitive in as much as Petri nets allow; however, the unbounded call stack is approximated, introducing potential precision degradation only in deeply recursive or highly dynamic call/return scenarios.
Experimental Evaluation
The evaluation encompasses 39 concurrent program artifacts, including both Java and Kotlin programs, with a spectrum of concurrency idioms: classic deadlocks, dining philosophers, recursive thread creation, complex aliasing, modern language features (records, sealed classes, virtual threads), and scalability stressors (large thread counts, deep call stacks). The test suite is drawn from public corpora targeting state-of-the-art analyzers (JaDA, JPF), further extended to stress language and control-flow modularity.
Notable outcomes:
- Precision: Petrify matched or exceeded the precision of JPF and JaDA on almost all examples, except in cases where imprecision in Soot's alias analysis yielded state-space explosions or dropped genuine alias distinctions. Notably, Petrify avoided false positives/negatives in practice on this suite, unlike JaDA, which exhibited both.
- Language Support: Petrify can process Java versions and substantial subsets of Kotlin that are not analyzable by source-level tools (e.g., JaDA/JPF's lack of support for Java 17, 21 features).
- Scalability: State-space size (in Petri net places) remains tractable due to the succinct encoding. The limiting factor in large instances was primarily Soot’s analysis time, rather than Petri net construction or model checking.
- Expressiveness and Limitations: Absence of reentrant lock modeling leads to localized unsoundness in programs utilizing such constructs. Context-sensitivity is bounded by the Petri net model (no unbounded return stacks), but for standard idioms and non-reentrant code, this is sufficient. The approach is currently limited with respect to exceptions, advanced reflection, low-level array manipulation, and poorly-aliased shared state.
Theoretical and Practical Implications
Petrify demonstrates that Petri nets can serve as a middle ground between the expressiveness of explicit-state and theorem-proving techniques, and the scalability of more coarse static analyses. The model captures fine-grained thread interleavings, parallelism, and lock manipulation with a greater degree of semantic correspondence to the bytecode than pattern-based static analyses, but with lower annotation and modeling overhead than deductive verifiers.
From a practical standpoint, operating at the bytecode level enables forward compatibility with the rapidly evolving Java ecosystem. The succinct Petri net encoding allows integration with mature verification backends, facilitating rapid prototyping and comparative benchmarking. The tool exposes the limits of alias analysis and context sensitivity as principal sources of unsoundness or imprecision, pointing toward the integration of advanced pointer analyses or colored/inhibitor Petri nets for future enhancements.
Future Directions
Several avenues are indicated for future work:
- Petri Net Variations: Exploring colored nets or nets with inhibitor arcs to mitigate the precision loss in call/return modeling, at the expense of model-checking complexity.
- Support for Reentrant Locks and Exceptions: Extending the intermediate language and subsequent encoding to support precise modeling of reentrant lock semantics and exception-induced control flow.
- Advanced Analysis: Extending the repertoire beyond deadlock detection, e.g., data races, atomicity violations, and property-specific livelock detection, facilitated by further augmentation of the monitoring places and formula generation.
- Enhanced Aliasing: Integrating context- or type-sensitive alias analyses to reduce spurious lock aliasing and corresponding state-space explosion.
Conclusion
Petrify positions itself as a robust, automated concurrency analysis platform for JVM bytecode artifacts, achieving a balance between expressiveness and scalability via Petri net-based modeling. Experimental results demonstrate both practical effectiveness (including broad language support and competitive analysis outcomes) and delineate clear targets for precision and feature support advances. The approach validates Petri nets as a feasible, efficient semantic abstraction for concurrent program verification and model checking of real-world bytecode, especially as JVM languages continue to evolve (2607.00830).