pietrobraione / jbse

A symbolic Java virtual machine for program analysis, verification and test generation
http://pietrobraione.github.io/jbse/
GNU General Public License v3.0
101 stars 29 forks source link
java-virtual-machine symbolic-execution-engine

JBSE

Project stats at https://www.openhub.net/p/JBSE

About

JBSE is a symbolic Java Virtual Machine for automated program analysis, verification and test generation. JBSE allows to execute an arbitrary Java method with symbolic inputs. A symbolic input stands for an arbitrary primitive or reference value on which JBSE does not make any initial assumption. During the execution JBSE introduces assumptions on the symbolic inputs, e.g. to decide whether at a certain point it must follow the "then" or "else" branch of a conditional statement, or to decide whether accessing a field with a symbolic reference yields a value or raises a NullPointerException. In these situations JBSE splits the possible cases and analyzes all of them, backtracking through the possible cases. This way JBSE can explore how a Java program behaves when fed with possibly infinite classes of inputs, differently from program testing that is always limited in investigating a single behavior a time.

JBSE is a library and can be integrated in any software that needs to perform an analysis of the runtime behavior of a Java bytecode program.

Installing JBSE

Right now JBSE can be installed only by building it from source. Formal releases will be available when JBSE will be more feature-ready and stable.

Building JBSE

JBSE is built with Gradle version 8.4, that is included in the repository. First, ensure that all the dependencies are present, including Z3 (see section "Dependencies"). Then, clone the JBSE git repository. If you work from the command line, this means running git clone. Next, you may need to patch a test class as explained in the section "Patching the tests". Finally, run the build Gradle task, e.g. by invoking gradlew build from the command line.

Dependencies

JBSE has several dependencies. It must be built using a JDK version 8 - neither less, nor more. We suggest to use the latest Eclipse Temurin from Adoptium v8 with HotSpot JVM (note that the JDK with the OpenJ9 JVM currently does not work, because there are some slight differences in the standard library classes). If you are on an Apple Silicon Mac we suggest to use the JDK Zulu distribution from Azul. The Gradle wrapper gradlew included in the repository will take care to select the right version of the JDK. Gradle will automatically resolve and use the following compile-time-only dependency:

The runtime dependencies that are automatically resolved by Gradle and included in the build path are:

There is an additional runtime dependency that is not handled by Gradle so you will need to fix it manually. JBSE needs to interact at runtime with an SMT solver for pruning infeasible program paths. JBSE works well with Z3 and CVC4, but any SMT solver that is compatible with the SMTLIB v2 standard and supports the AUFNIRA logic should work. Both Z3 and CVC4 are distributed as standalone binaries and can be installed almost everywhere. We strongly advise to use Z3 because it is what we routinely use.

Patching the tests

Once cloned the git repository and ensured the dependencies, you might need to fix a dependency that Gradle is not able to fix by itself. Gradle will not build the JBSE project unless the (very small) regression test suite under the src/test directory passes. All tests should pass (we test JBSE before every commit), with the possible exception of the tests in the class jbse.dec.DecisionProcedureTest, that test the interaction with Z3 or CVC4, and therefore might fail if the right solver is not installed. The jbse.dec.DecisionProcedureTest class needs to be configured to use either solver, and to point to the path of the corresponding executable. Modify line 44 of class jbse.dec.DecisionProcedureTest and define the variable COMMAND_LINE to be either Z3_COMMAND_LINE or CVC4_COMMAND_LINE depending on whether you have Z3 or CVC4 installed on your development machine. In both cases, you must also modify line 45 and assign the variable SMT_SOLVER_PATH with the path to the Z3 or CVC4 executable on your development machine.

Working under Eclipse

If you want to build and modify JBSE by using (as we do) the latest Eclipse for Java Developers, you are lucky: All the Eclipse plugins that are necessary to import and build JBSE are already present in the distribution. The only caveat is that, since starting from version 2020-09 Eclipse requires at least Java 11 to run, your development machine will need to have both a Java 11 or greater (to run Eclipse) and a Java 8 setup (to build and run JBSE). Gradle will automatically select the right version of the JDK when building JBSE. If you use a different flavor, or an earlier version, of Eclipse you might need to install the egit and the Buildship plugins, both available from the Eclipse Marketplace. After that, to import JBSE under Eclipse follow these steps:

Deploying JBSE

The gradlew build command will produce a jar file build/libs/jbse-<VERSION>.jar that also includes the jbse.meta package and its subpackages, containing the API that the code under analysis can invoke to issue assertions, assumptions, and otherwise control the analysis process itself. The jar file does not include the runtime dependencies (Javassist and tools.jar), so you need to deploy them together with it. To ease deployment, Gradle will also build an uber-jar build/libs/jbse-<VERSION>-shaded.jar containing Javassist (but not tools.jar). To avoid conflicts the uber jar renames the javassist package as jbse.javassist.

Usage

In the future you will find a full description of JBSE and instructions for using it in its user manual (currently under development). In the meantime, for a showcase of some of JBSE's capabilities you can checkout the JBSE examples project.