Configuring and Using LiSA
This website describes LiSA’s architecture and provides guides on how to use and extend it. It is intended to be valid for the latest stable release of LiSA, but should be compatible with versions 0.2 and later. Signatures or packages might differ in older versions, but the overall architecture and design principles should remain the same.
Using and configuring LiSA is straightforward: first, a Program (or more
programs, one for each programming language to analyze) must be obtained,
then a LiSAConfiguration object must be created and customized, and finally
a LiSA instance must be created with the configuration and run on the
program(s). For example:
Program program = ... // use a frontend to parse the code, or build the program programmatically
LiSAConfiguration config = new LiSAConfiguration();
// set configuration options (see below)
LiSA lisa = new LiSA(config);
LiSAReport report = lisa.run(program);
// use the report
Each configuration option can be set individually by changing the value of a
field of the LiSAConfiguration object passed to the LiSA constructor. In the
following sections, when an example usage is provided for a configuration option,
it is assumed that the LiSAConfiguration object is stored in a variable named conf.
Available Options
Setting the Abstract Domain
analysis
AbstractDomain<?>
null
conf.analysis = new SimpleAbstractDomain(new PointBasedHeap(), new Interval(), new InferredTypes())
The Abstract Domain
to execute during the analysis can be selected through the analysis option.
The value of this option decides what analyysis is being run, and what shape
will the computed states have. If no value is set for this option, no semantic
analysis will be executed.
Several abstract domains are available in LiSA:
- it.unive.lisa.analysis.HistoryDomain
- it.unive.lisa.analysis.Reachability
- it.unive.lisa.analysis.SimpleAbstractDomain
- it.unive.lisa.analysis.traces.TracePartitioning
If you adopt the Simple Abstract Domain framework to build your own abstract domain, LiSA also provides alternatives for each component. The out-of-the-box implementations for the heap domain are:
- it.unive.lisa.analysis.NoOpHeap
- it.unive.lisa.analysis.heap.MonolithicHeap
- it.unive.lisa.analysis.heap.TypeBasedHeap
- it.unive.lisa.analysis.heap.pointbased.FieldSensitivePointBasedHeap
- it.unive.lisa.analysis.heap.pointbased.PointBasedHeap
Instead, the bundled alternatives for the value domain are:
- it.unive.lisa.analysis.ConstantValuePropagation
- it.unive.lisa.analysis.NoOpValues
- it.unive.lisa.analysis.combination.constraints.WholeValueAnalysis
- it.unive.lisa.analysis.combination.smash.SmashedSum
- it.unive.lisa.analysis.dataflow.AvailableExpressions
- it.unive.lisa.analysis.dataflow.ConstantPropagation
- it.unive.lisa.analysis.dataflow.Liveness
- it.unive.lisa.analysis.dataflow.ReachingDefinitions
- it.unive.lisa.analysis.informationFlow.NonInterference
- it.unive.lisa.analysis.informationFlow.ThreeLevelsTaint
- it.unive.lisa.analysis.informationFlow.TwoLevelsTaint
- it.unive.lisa.analysis.nonRedundantPowerset.NonRedundantPowerset
- it.unive.lisa.analysis.nonRedundantPowerset.NonRelationalNonRedundantPowerset
- it.unive.lisa.analysis.nonrelational.value.BooleanPowerset
- it.unive.lisa.analysis.numeric.IntegerConstantPropagation
- it.unive.lisa.analysis.numeric.Interval
- it.unive.lisa.analysis.numeric.NonRedundantIntervals
- it.unive.lisa.analysis.numeric.Parity
- it.unive.lisa.analysis.numeric.Pentagon
- it.unive.lisa.analysis.numeric.Sign
- it.unive.lisa.analysis.numeric.Stability
- it.unive.lisa.analysis.numeric.UpperBounds
- it.unive.lisa.analysis.string.BoundedStringSet
- it.unive.lisa.analysis.string.Bricks
- it.unive.lisa.analysis.string.CharInclusion
- it.unive.lisa.analysis.string.Prefix
- it.unive.lisa.analysis.string.StringConstantPropagation
- it.unive.lisa.analysis.string.SubstringDomain
- it.unive.lisa.analysis.string.SubstringDomainWithConstants
- it.unive.lisa.analysis.string.Suffix
- it.unive.lisa.analysis.string.fsa.FSA
- it.unive.lisa.analysis.string.tarsis.Tarsis
Finally, the bundled alternatives for the type domain are:
- it.unive.lisa.analysis.NoOpTypes
- it.unive.lisa.analysis.types.InferredTypes
- it.unive.lisa.analysis.types.StaticTypes
Interprocedural Analysis and Call Graph
interproceduralAnalysis
InterproceduralAnalysis<?, ?>
null
conf.interproceduralAnalysis = new ModularWorstCaseAnalysis<>()
callGraph
CallGraph
null
conf.callGraph = new CHACallGraph()
openCallPolicy
OpenCallPolicy
TopExecutionPolicy.INSTANCE
conf.openCallPolicy = new CustomPolicy()
The Interprocedural Analysis
and the Call Graph
regulate how the program-wide analysis is executed, and how calls are resolved
and computed. The value of the interproceduralAnalysis option determines the
interprocedural analysis to use, while the value of the callGraph option
determines the call graph.
If no value is set for interproceduralAnalysis, no
semantic analysis will be executed. Instead, the value of callGraph is
effectively ignored if the selected does not require
a call graph, as determined by that analysis’ needsCallGraph method.
If instead the selected interprocedural analysis requires a call graph and no value is
set for callGraph, an error will be raised at startup.
The Open Call Policy is used to determine the results of calls that have no targets in the program to analyze.
Several interprocedural analyses are available in LiSA:
- it.unive.lisa.interprocedural.BackwardModularWorstCaseAnalysis
- it.unive.lisa.interprocedural.ModularWorstCaseAnalysis
- it.unive.lisa.interprocedural.context.ContextBasedAnalysis
- it.unive.lisa.interprocedural.context.recursion.BaseCasesFinder
- it.unive.lisa.interprocedural.context.recursion.RecursionSolver
- it.unive.lisa.interprocedural.inlining.InliningAnalysis
Orthogonally, call graphs implementations are also bundled with LiSA:
- it.unive.lisa.interprocedural.callgraph.CHACallGraph
- it.unive.lisa.interprocedural.callgraph.RTACallGraph
For open call policies, LiSA provides:
- it.unive.lisa.interprocedural.ReturnTopPolicy
- it.unive.lisa.interprocedural.TopExecutionPolicy
- it.unive.lisa.interprocedural.WorstCasePolicy
Adding Syntactic and Semantic Checks
syntacticChecks
Collection<SyntacticCheck>
new HashSet<>()
conf.syntacticChecks.add(new VariableNamesCheck())
semanticChecks
Collection<SemanticCheck<?, ?>>
new HashSet<>()
conf.semanticChecks.add(new NullDereferenceCheck())
Syntactic Checks and
Semantic Checks are visitors of
the program under analysis. Syntactic checks only require syntactic information,
and thus can be executed before the analysis, while semantic checks require
semantic information, and thus can only be executed after the analysis.
The collections of syntactic and semantic checks to execute can be customized
through the syntacticChecks and semanticChecks options, respectively.
The checks in these collections will be executed on the program under analysis, and
the results of the checks will be included in the final report. Note that
the order of execution of the checks is not guaranteed, and should not be relied upon.
The collection of syntactic checks to execute should only be added to, and not
replaced with other (possibly immutable) collections, as LiSA might add new
checks depending on the values of other options. The same applies to the
collection of semantic checks.
As of today, LiSA does not include any syntactic or semantic check implementations, as they are highly situational.
Thresholds for Widenings and GLBs
wideningThreshold
int
5
conf.wideningThreshold = 10
recursionWideningThreshold
int
5
conf.recursionWideningThreshold = 10
glbThreshold
int
5
conf.glbThreshold = 10
Fixpoint algorithms that use widenings and greatest lower
bounds (glbs) can be customized by setting the thresholds for the application of
the respective operators. wideningThreshold determines after how many iterations
of the fixpoint algorithm on a given node the widening operator should be applied
instead of the least upper bound operator (lub). recursionWideningThreshold
determines after how many iterations of the fixpoint algorithm on a recursive call
chain the widening operator should be applied instead of the least upper bound
operator (lub). glbThreshold determines how many descending iterations of the
fixpoint algorithm can be performed a given node using the greatest lower bound
operator (glb) before the descending iteration is stopped.
Setting these thresholds to 0 or a negative number causes the respective
operator to be always applied (for widenings) or to never be applied (for glbs).
Selecting the Fixpoint Algorithms
forwardFixpoint
ForwardCFGFixpoint<?, ?>
new ForwardAscendingFixpoint<>()
conf.forwardFixpoint = new CustomForwardFixpoint<>()
forwardDescendingFixpoint
ForwardCFGFixpoint<?, ?>
null
conf.forwardDescendingFixpoint = new CustomForwardDescendingFixpoint<>()
backwardFixpoint
BackwardCFGFixpoint<?, ?>
new BackwardAscendingFixpoint<>()
conf.backwardFixpoint = new CustomBackwardFixpoint<>()
backwardDescendingFixpoint
BackwardCFGFixpoint<?, ?>
null
conf.backwardDescendingFixpoint = new CustomBackwardDescendingFixpoint<>()
fixpointWorkingSet
WorkingSet<Statement>
new OrderBasedWorkingSet()
conf.fixpointWorkingSet = new CustomWorkingSet<>()
All fixpoint algorithms that LiSA executes over
control flow graphs
can be customized.
forwardFixpoint determines the fixpoint to compute forward fixpoints over
CFGs, while backwardFixpoint determines the fixpoint to compute backward
fixpoints over CFGs. The interprocedural analysis selected through the
interproceduralAnalysis option
determines whether forward and/or backward fixpoints are required, and thus
whether the values of these options are relevant.
Optionally, descending fixpoints can be computed after the ascending ones,
to refine the results. forwardDescendingFixpoint determines the fixpoint to
compute descending forward fixpoints over CFGs, while backwardDescendingFixpoint
determines the fixpoint to compute descending backward fixpoints over CFGs. If
no value is set for forwardDescendingFixpoint or backwardDescendingFixpoint,
no descending phase will be executed.
The order in which the nodes of the CFG are visited during fixpoint iterations
is determined by the WorkingSet passed to the fixpointWorkingSet option.
In all options above, the instances passed to the fields are used as factories to create new fixpoint instances or new working sets.
LiSA provides standard forward fixpoint algorithms, alongside their optimized variants:
- it.unive.lisa.program.cfg.fixpoints.forward.ForwardAscendingFixpoint
- it.unive.lisa.program.cfg.fixpoints.forward.ForwardDescendingGLBFixpoint
- it.unive.lisa.program.cfg.fixpoints.forward.ForwardDescendingNarrowingFixpoint
- it.unive.lisa.program.cfg.fixpoints.optforward.OptimizedForwardAscendingFixpoint
- it.unive.lisa.program.cfg.fixpoints.optforward.OptimizedForwardDescendingGLBFixpoint
- it.unive.lisa.program.cfg.fixpoints.optforward.OptimizedForwardDescendingNarrowingFixpoint
These are paired with their backward variants:
- it.unive.lisa.program.cfg.fixpoints.backward.BackwardAscendingFixpoint
- it.unive.lisa.program.cfg.fixpoints.backward.BackwardDescendingGLBFixpoint
- it.unive.lisa.program.cfg.fixpoints.backward.BackwardDescendingNarrowingFixpoint
- it.unive.lisa.program.cfg.fixpoints.optbackward.OptimizedBackwardAscendingFixpoint
- it.unive.lisa.program.cfg.fixpoints.optbackward.OptimizedBackwardDescendingGLBFixpoint
- it.unive.lisa.program.cfg.fixpoints.optbackward.OptimizedBackwardDescendingNarrowingFixpoint
Each fixpoint implementation can be customized by the following bundled working sets:
- it.unive.lisa.util.collections.workset.ConcurrentFIFOWorkingSet
- it.unive.lisa.util.collections.workset.ConcurrentLIFOWorkingSet
- it.unive.lisa.util.collections.workset.DuplicateFreeFIFOWorkingSet
- it.unive.lisa.util.collections.workset.DuplicateFreeLIFOWorkingSet
- it.unive.lisa.util.collections.workset.FIFOWorkingSet
- it.unive.lisa.util.collections.workset.LIFOWorkingSet
- it.unive.lisa.util.collections.workset.OrderBasedWorkingSet
- it.unive.lisa.util.collections.workset.VisitOnceFIFOWorkingSet
- it.unive.lisa.util.collections.workset.VisitOnceLIFOWorkingSet
Optimization-related Options
useWideningPoints
boolean
true
conf.useWideningPoints = false
hotspots
Predicate<Statement>
null
conf.hotspots = stmt -> stmt instanceof Assignment
dumpForcesUnwinding
boolean
false
conf.dumpForcesUnwinding = true
LiSA can be optimized in several ways. A simple optimization is to use
widenings and narrowings only on widening points (i.e., loop conditions),
and to use lubs and glbs on all other nodes regardless of the threshold.
This is typically more efficient, as widening and narrowing are more expensive
than lub and glb, and the results of lubs and glbs are often more precise
than those of widenings and narrowings. This behavior can be enabled by
setting the useWideningPoints option to true. Note that widening points
correspond to the conditions of loops, as identified by CFG.getCycleEntries().
A second optimization is to use
optimized fixpoint algorithms
(i.e., algorithms for which invocations of
AnalysisFixpoint.isOptimized() on forwardFixpoint,
forwardDescendingFixpoint,
backwardFixpoint, or
backwardDescendingFixpoint
yields true — these correspond to the ones having Optimized in their name).
Such algorithms exploit basic blocks, and store the fixpoint results only for
widening points (i.e., loop conditions), return statements, and calls. This is
doable since results for any other instruction can be recontsructed by executing
a single fixpoint iteration local to the CFG that contains the instruction,
that will stabilize in one iteration since the results of widening points is
already a post-fixpoint. This reconstruction is called unwinding in LiSA.
When such algorithms are used, the hotspots predicate can be set to determine
additional statements for which the fixpoint results must be kept to avoid
excessive unwinding. null is a special value corresponding to the predicate t
-> false. Instead, dumpForcesUnwinding can be set to true to
force unwinding of all non-hotspot statements when dumping results to output
files, to ensure that results are available for all program instructions.
Hiding Error and Exceptions
shouldSmashError
Predicate<Type>
null
conf.shouldSmashError = type -> type.getName().equals("java.lang.NullPointerException")
Some error typems might pollute the analysis results, since they might not be
relevant for the properties to prove or they are caused by an excessive
imprecision of the analysis. While it is not possible to completely remove them
(as the modifications they cause to the control flow must be taken into
account), it is possible to smash them, that is, to not have a separate entry
for each of their occurrences in the
AnalysisState errors.
Instead, all occurrences of smashed errors will share a unique ProgramState.
The choice over what error types to smash is determined by the shouldSmashError
predicate, that returns true for the types of errors to smash. null is a
special value corresponding to the predicate t -> false.
Event Listeners
synchronousListeners
List<EventListener>
new LinkedList<>()
conf.synchronousListeners.add(new LoggingListener())
asynchronousListeners
List<EventListener>
new LinkedList<>()
conf.asynchronousListeners.add(new TracingListener())
EventListeners can be
registered to process events emitted during the analysis. Listeners can be
either synchronous or asynchronous, and are registered through the
synchronousListeners and asynchronousListeners options, respectively.
Synchronous listeners will be executed in the same thread as the analysis
itself, and thus will block the analysis until they complete. Asynchronous
listeners will be executed in a separate thread, and thus will not block the
analysis. Synchronous listeners are executed before asynchronous ones, and
the order of execution of the listeners preservers the insertion order into
the respective collection.
The lists of listeners should only be added to, and not
replaced with other (possibly immutable) lists, as LiSA might add new
listeners depending on the values of other options.
LiSA bundles the following event listeners, that can be used both synchronously and asynchronously:
- it.unive.lisa.listeners.BottomTopListener
- it.unive.lisa.listeners.CallResolutionListener
- it.unive.lisa.listeners.FlameGraphListener
- it.unive.lisa.listeners.TracingListener
When implementinc custom listeners, the following events are issued automatically by the analysis:
- it.unive.lisa.analysis.events.AnalysisAssignEnd
- it.unive.lisa.analysis.events.AnalysisAssignStart
- it.unive.lisa.analysis.events.AnalysisAssumeEnd
- it.unive.lisa.analysis.events.AnalysisAssumeStart
- it.unive.lisa.analysis.events.AnalysisErrorsToExecEnd
- it.unive.lisa.analysis.events.AnalysisErrorsToExecStart
- it.unive.lisa.analysis.events.AnalysisExecToErrorEnd
- it.unive.lisa.analysis.events.AnalysisExecToErrorStart
- it.unive.lisa.analysis.events.AnalysisExecToHaltEnd
- it.unive.lisa.analysis.events.AnalysisExecToHaltStart
- it.unive.lisa.analysis.events.AnalysisOnCallReturnEnd
- it.unive.lisa.analysis.events.AnalysisOnCallReturnStart
- it.unive.lisa.analysis.events.AnalysisRemoveCaughtEnd
- it.unive.lisa.analysis.events.AnalysisRemoveCaughtStart
- it.unive.lisa.analysis.events.AnalysisSatisfiesEnd
- it.unive.lisa.analysis.events.AnalysisSatisfiesStart
- it.unive.lisa.analysis.events.AnalysisSmallStepEnd
- it.unive.lisa.analysis.events.AnalysisSmallStepStart
- it.unive.lisa.analysis.events.AnalysisTransferThrowersEnd
- it.unive.lisa.analysis.events.AnalysisTransferThrowersStart
- it.unive.lisa.analysis.events.DomainAssignEnd
- it.unive.lisa.analysis.events.DomainAssignStart
- it.unive.lisa.analysis.events.DomainAssumeEnd
- it.unive.lisa.analysis.events.DomainAssumeStart
- it.unive.lisa.analysis.events.DomainSatisfiesEnd
- it.unive.lisa.analysis.events.DomainSatisfiesStart
- it.unive.lisa.analysis.events.DomainSmallStepEnd
- it.unive.lisa.analysis.events.DomainSmallStepStart
- it.unive.lisa.analysis.events.HeapRewriteEnd
- it.unive.lisa.analysis.events.HeapRewriteStart
- it.unive.lisa.analysis.events.SADSubsEnd
- it.unive.lisa.analysis.events.SADSubsStart
- it.unive.lisa.analysis.nonrelational.events.NRDEvalEnd
- it.unive.lisa.analysis.nonrelational.events.NRDEvalStart
- it.unive.lisa.interprocedural.callgraph.events.CallResolved
- it.unive.lisa.interprocedural.events.CFGFixpointEnd
- it.unive.lisa.interprocedural.events.CFGFixpointStart
- it.unive.lisa.interprocedural.events.CFGFixpointStored
- it.unive.lisa.interprocedural.events.ComputedCallResult
- it.unive.lisa.interprocedural.events.ComputedCallState
- it.unive.lisa.interprocedural.events.FixpointEnd
- it.unive.lisa.interprocedural.events.FixpointIterationEnd
- it.unive.lisa.interprocedural.events.FixpointIterationStart
- it.unive.lisa.interprocedural.events.FixpointStart
- it.unive.lisa.interprocedural.events.PrecomputedCallResult
- it.unive.lisa.interprocedural.events.RecursionEnd
- it.unive.lisa.interprocedural.events.RecursionStart
- it.unive.lisa.program.cfg.fixpoints.events.EdgeTraverseEnd
- it.unive.lisa.program.cfg.fixpoints.events.EdgeTraverseStart
- it.unive.lisa.program.cfg.fixpoints.events.JoinPerformed
- it.unive.lisa.program.cfg.fixpoints.events.LeqPerformed
- it.unive.lisa.program.cfg.fixpoints.events.PreStateComputed
- it.unive.lisa.program.cfg.fixpoints.events.StatementSemanticsEnd
- it.unive.lisa.program.cfg.fixpoints.events.StatementSemanticsStart
Producing Outputs
workdir
String
Paths.get(".").toAbsolutePath().normalize().toString()
conf.workdir = "/tmp/lisa-analysis"
outputs
Collection<LiSAOutput>
new HashSet<>()
conf.outputs.add(new JsonReport())
All Outputs produced by LiSA
are generated inside the working directory specified by the workdir option.
By default, the working directory is the directory where the JVM executing LiSA
was launched, but it can be customized by setting the workdir option to a
different path. To generate a new output, it is sufficient to add it to the
collection of outputs specified by the outputs option. Note that the order of
generation of the outputs is not guaranteed, and should not be relied upon.
The collection of outputs to produce should only be added to, and not replaced with
other (possibly immutable) collections, as LiSA might add new outputs depending on
on the values of other options.
LiSA includes the following output generators:
- it.unive.lisa.outputs.DotCallGraph
- it.unive.lisa.outputs.DotInputs
- it.unive.lisa.outputs.DotResults
- it.unive.lisa.outputs.HtmlCallGraph
- it.unive.lisa.outputs.HtmlInputs
- it.unive.lisa.outputs.HtmlResults
- it.unive.lisa.outputs.JSONCallGraph
- it.unive.lisa.outputs.JSONInputs
- it.unive.lisa.outputs.JSONReportDumper
- it.unive.lisa.outputs.JSONResults
Logging
Logging is not configured through the LiSAConfiguration object. LiSA produces
all logging through log4j2,
and will thus follow the framework’s own configuration. There are a number of
ways to configure log4j2, but the simplest one is to create a log4j2.xml file
in the working directory, with the desired configuration. For example, the
following configuration will log all messages of level DEBUG or higher to a file
the console:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN" name="DefaultLoggingConf">
<Appenders>
<Console name="console">
<PatternLayout pattern="%d [%5level] %m %ex%n"/>
</Console>
</Appenders>
<Loggers>
<Logger name="it.unive.lisa" level="DEBUG" />
<Logger name="org.reflections" level="WARN" />
<Root level="DEBUG">
<AppenderRef ref="console" level="DEBUG"/>
</Root>
</Loggers>
</Configuration>
Please check log4j2’s documentation for more details on how to configure logging.
If no logging is configured, LiSA will set up a default configuration that logs to the console only.
Default and Test Configuration
LiSA also offers a class named DefaultConfiguration, that provides a default
value for interprocedural analysis and call graph. It also offers utility
methods for building an abstract domain following the
Simple Abstract Domain framework.
A common use case when developing static analyzers is to have end-to-end tests
that starts from an input file and the necessary configuration, execute a full
analysis as a black box, and compare the results obtained with some pre-existing
results. LiSA provides a unique infrastructure for this use case to simplify
testing. A TestConfiguration is a LiSAConfiguration extended with the
following fields:
testDirdefines the relative path to the root folder where test files are located;testSubDirdefines an optional path relative totestDirto use as workdir for the analysis, useful to keep output files separated for similar tests;programFileholds the name of the source file to analyze, relative totestDir;forceUpdatespecifies that, should any difference be found between the results of the analysis and the pre-existing results, the pre-existing results should be updated with the new results instead of raising an error;compareWithOptimizationspecifies that, should no difference be found between the results of the analysis and the pre-existing results, the analysis should be executed again with optimizations enabled, and the results of the optimized analysis should be compared with the pre-existing results as well, to check that optimizations do not change the results of the analysis;resultComparerholds a reference to theResultComparerinstance to use to compare the results of the analysis with the pre-existing results, that can be customized to ignore irrelevant differences between the results or additional analyzer-specific settings.
A TestConfiguration can be used with an instance of AnalysisTestExecutor, an
abstract class defined in LiSA to provide the standard workflow for executing
end-to-end tests. The class has a constructor that takes the path to the
expected results folder, where the pre-existing results are located, and a path
to the actual results folder, where test files for produced by the analysis will
be generated. The analysis is started by invoking one of the perform
overloads, each accepting a TestConfiguration and optionally an already parsed
Program. If the program is not provided, the abstract readProgram method
will be invoked to parse the file located at expected-dir/testDir/programFile.
Then, the execution proceeds as follows (in the following, if testSubDir is
null, testDir/testSubDir should be read as testDir):
- the folder
actual-dir/testDir/testSubDiris cleared of all files, and it is set as workdir for the analysis; - an instance of
JSONReportDumperis added to the outputs to produce; - a
LiSAinstance is created with theTestConfigurationas configuration, and it is run on the parsed program; - if no
expected-dir/testDir/testSubDir/report.jsonfile exists andforceUpdateis not set, the execution terminates; - if no
expected-dir/testDir/testSubDir/report.jsonfile exists andforceUpdateis set, the contents of the workdir will be copied toexpected-dir/testDir/testSubDir, and the execution terminates; - if
expected-dir/testDir/testSubDir/report.jsonexists andforceUpdateis not set, theresultCompareris used to compare it withactual-dir/testDir/testSubDir/report.json, raising an exception if there are any differences; - if
expected-dir/testDir/testSubDir/report.jsonexists andforceUpdateis set, theresultCompareris used to compare it withactual-dir/testDir/testSubDir/report.json, and all files where at least one difference is found are copied from the workdir toexpected-dir/testDir/testSubDir, replacing the pre-existing files; - if
compareWithOptimizationis not set, or if it is set but the fixpoints used were already optimized, the execution terminates; - if
compareWithOptimizationis set and the fixpoints used were not already optimized, the whole process is repeated with the same configuration but with optimized fixpoints, and the results of the optimized analysis are compared with the pre-existing results as well, raising an exception if there are any differences (in this run,forceUpdateis ignored).
Alternatively to forceUpdate, the lisa.cron.update system property can be set
to true to achieve the same effect.
Fontends
Frontends for several languages have been developed over the years. Recall that frontends are not part of LiSA, but rather they are fully-fledged static analysers that use LiSA as a library to execute the analysis. They can be used as-is, can be extended, or can be used as examples to build new frontends for other languages. For more details on how to build a frontend, please check the frontend documentation.
GoLiSA
GoLiSA is a frontend for a subset of the Go programming language. It has been developed with the objective of performing analyses targeting security properties of blockchain software and smart contracts, focusing on the frameworks Hyperledger Fabric, Cosmos SDK, Ethereum Client, and Tendermint Core. The properties targeted include harmful usage of non-deterministic APIs and constructs, dangerous cross-contract invocations, and read-write inconsistencies.
For more information on features and usage, refer to the GitHub repository.
JLiSA
JLiSA is a frontend for a subset of the Java programming language, developed primarely for the SV-COMP competition. It is able to identify failing assertions and uncaught runtime exceptions, and models a subset of the Java APIs.
For more information on features and usage, refer to the GitHub repository.
PyLiSA
PyLiSA is a highly-experimental frontend for a subset of the Python programming language. Specifically, it does not yet support any dynamic features related to changes to the program structure (e.g., dynamic code loading, dynamic attribute access, etc.), and it only supports a subset of the Python APIs. PyLiSA has been developed for two distinct scenarios: the analysis of Data Science scripts using Pandas, and the reconstruction of architectural diagrams for ROS2 robotic networks.
For more information on features and usage, refer to the GitHub repository.
EVMLiSA
EVMLiSA is a frontend for EVM bytecode aimed at soundly reconstructing a full and sound control flow graph from the bytecode itself, that can be used in later analyses. The reconstruction can optionally be made aware of the current global storage to improve the accuracy of the generated control flow graph.
For more information on features and usage, refer to the GitHub repository.
MichelsonLiSA
MichelsonLiSA is a frontend for Michelson bytecode obtained by the compilation of smart contracts for Tezos blockchains. It is based on an SSA translation of the bytecode, and it is aimed at detecting cross-contracts invocations.
For more information on features and usage, refer to the GitHub repository.