Verification Backend Abstraction
Lotus provides a unified interface to multiple verification backends, allowing you to switch between different tools seamlessly.
Overview
The verification backend abstraction layer provides:
Unified interface: Same API for all verification tools
Automatic backend selection: Choose backend based on property type
Result normalization: Consistent result format across backends
Easy integration: Add new backends without changing client code
Supported Backends
seahorn: SeaHorn CHC-based verification (supports all properties)
sifa: Sifa symbolic abstraction (reachability)
symabs_ai: SymAbsAI framework (reachability, memsafety, overflow)
clam: CLAM abstract interpretation (memsafety, overflow, reachability)
Property Classes
reachability: Target location is reachable/unreachable
memsafety: Memory safety (null pointer, use-after-free, etc.)
overflow: Integer overflow detection
termination: Program termination
Usage
Basic usage with lotus-verify:
# Auto-select backend
lotus-verify input.bc --property unreach-call --backend auto --run
# Specify backend explicitly
lotus-verify input.bc --property memsafety --backend seahorn --run
# With timeout
lotus-verify input.bc --property overflow --backend clam --timeout 60 --run
# Pass extra arguments to backend
lotus-verify input.bc --property unreach-call --backend seahorn \
--backend-arg --horn-cex --run
Result Format
All backends normalize results to a standard format:
true: Property holds (no error found)
false: Property violated (error found)
unknown: Could not determine
error: Verification tool error
timeout: Verification timed out
Example Output
backend: seahorn
property: unreach-call
command: seahorn input.bc
result: true
message: Property holds (no error found)
exit-code: 0
Programmatic Usage
#include "Verification/Backend/Backend.h"
using namespace lotus::verification::backend;
BackendRegistry ® = BackendRegistry::instance();
auto backend = reg.create("seahorn");
VerificationTask task;
task.inputBitcode = "input.bc";
task.property = PropertyClass::Reachability;
task.timeoutSeconds = 60;
auto cmd = backend->buildCommand(task);
// Execute command...
VerificationResultInfo result = backend->parseResult(output, exitCode);
if (result.isSafe()) {
// Property holds
} else if (result.hasError()) {
// Property violated
}
Adding New Backends
To add a new backend, implement the IBackend interface:
class MyBackend final : public IBackend {
public:
const char *name() const override { return "mybackend"; }
bool supports(PropertyClass property) const override {
return property == PropertyClass::Reachability;
}
std::vector<std::string>
buildCommand(const VerificationTask &task) const override {
return {"mybackend", task.inputBitcode};
}
VerificationResultInfo parseResult(const std::string &output,
int exitCode) const override {
VerificationResultInfo info;
// Parse output and set info.result, info.message, etc.
return info;
}
};
Then register it in BackendRegistry::create().