API Reference
This document provides API references and usage examples for Lotus components.
Alias Analysis APIs
DyckAA API
Include headers:
#include "Alias/UnificationBased/DyckAA/DyckAliasAnalysis.h"
#include "Alias/UnificationBased/DyckAA/DyckGraph.h"
Basic usage:
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IRReader/IRReader.h"
#include "llvm/Support/SourceMgr.h"
#include "Alias/UnificationBased/DyckAA/DyckAliasAnalysis.h"
using namespace llvm;
int main(int argc, char **argv) {
LLVMContext context;
SMDiagnostic error;
std::unique_ptr<Module> module = parseIRFile(argv[1], error, context);
if (!module) {
error.print(argv[0], errs());
return 1;
}
DyckAliasAnalysis DAA;
DAA.runOnModule(*module);
return 0;
}
Query alias information:
// Query if two values may alias
bool mayAlias = DAA.mayAlias(val1, val2);
// Check if a pointer may be null
bool mayNull = DAA.mayNull(val1);
// Retrieve the alias set for a pointer
const std::set<Value *> *aliasSet = DAA.getAliasSet(val1);
if (aliasSet) {
for (Value *aliased : *aliasSet) {
errs() << "Aliased: " << *aliased << "\n";
}
}
Access Dyck graph:
#include "Alias/UnificationBased/DyckAA/DyckGraph.h"
DyckGraph *graph = DAA.getDyckGraph();
// Retrieve vertex for a value
DyckGraphNode *node = graph->retrieveDyckVertex(val1);
if (node) {
std::set<void *> *equivSet = node->getEquivalentSet();
if (equivSet) {
errs() << "Equivalence set size: " << equivSet->size() << "\n";
}
}
AserPTA API
Include headers:
#include "Alias/InclusionBased/AserPTA/PTADriver.h"
#include "Alias/InclusionBased/AserPTA/PointerAnalysis/Context/KCallSite.h"
#include "Alias/InclusionBased/AserPTA/PointerAnalysis/Context/NoCtx.h"
#include "Alias/InclusionBased/AserPTA/PointerAnalysis/Models/LanguageModel/DefaultLangModel/DefaultLangModel.h"
#include "Alias/InclusionBased/AserPTA/PointerAnalysis/Models/MemoryModel/FieldSensitive/FSMemModel.h"
#include "Alias/InclusionBased/AserPTA/PointerAnalysis/Solver/WavePropagation.h"
#include "Alias/InclusionBased/AserPTA/PointerAnalysis/Solver/PointsTo/BitVectorPTS.h"
Configure and run analysis:
using namespace aser;
// Configure language model, memory model, and points-to representation
using Model = DefaultLangModel<KCallSite<1>, FSMemModel<KCallSite<1>>, BitVectorPTS>;
using Solver = WavePropagation<Model>;
// Run analysis with preprocessing passes
runAnalysis<Solver>(*module);
LotusAA API
Include headers:
#include "Alias/InclusionBased/LotusAA/Engine/InterProceduralPass.h"
Use as LLVM pass:
legacy::PassManager PM;
PM.add(new LotusAA());
PM.run(*module);
Query results:
LotusAA *lotus = /* get pass */;
IntraLotusAA *intraPTG = lotus->getPtGraph(F);
CallTargetSet *callees = lotus->getCallees(F, callSite);
Intermediate Representation APIs
Program Dependence Graph API
Include headers:
#include "IR/PDG/Core/ProgramDependencyGraph.h"
#include "IR/PDG/Core/Graph.h"
#include "IR/PDG/Core/PDGNode.h"
#include "IR/PDG/Core/PDGEdge.h"
Build PDG:
using namespace pdg;
legacy::PassManager PM;
PM.add(new DataDependencyGraph());
PM.add(new ControlDependencyGraph());
PM.add(new ProgramDependencyGraph());
PM.run(*module);
ProgramGraph *pdg = &ProgramGraph::getInstance();
Traverse PDG:
for (auto *node : *pdg) {
for (auto *edge : node->getOutEdgeSet()) {
Node *target = edge->getDstNode();
EdgeType type = edge->getEdgeType();
}
}
Query function nodes:
Function *F = /* ... */;
FunctionWrapper *fw = pdg->getFuncWrapper(*F);
if (fw) {
Node *entryNode = fw->getEntryNode();
// Return-value trees; per-argument trees are available through
// getArgFormalInTree(Argument &) / getArgFormalOutTree(Argument &).
Tree *formalInTree = fw->getRetFormalInTree();
Tree *formalOutTree = fw->getRetFormalOutTree();
}
Check reachability:
bool hasPath = pdg->canReach(*srcNode, *dstNode);
std::set<EdgeType> exclude = {EdgeType::CONTROLDEP_CALLINV,
EdgeType::CONTROLDEP_CALLRET,
EdgeType::CONTROLDEP_ENTRY,
EdgeType::CONTROLDEP_BR,
EdgeType::CONTROLDEP_IND_BR};
bool hasDataPath = pdg->canReach(*srcNode, *dstNode, exclude);
PDG Cypher Query API
Include headers:
#include "IR/PDG/QueryLanguage/Cypher.h"
#include "IR/PDG/Core/ProgramDependencyGraph.h"
Parse and execute Cypher queries:
using namespace pdg;
ProgramGraph *pdg = &ProgramGraph::getInstance();
CypherQueryExecutor executor(*pdg);
CypherParser parser;
std::string queryStr = "MATCH (n:FUNC_ENTRY) WHERE n.name = 'main' RETURN n";
std::unique_ptr<CypherQuery> query = parser.parse(queryStr);
if (!parser.hasError()) {
std::unique_ptr<CypherResult> result = executor.execute(*query);
if (result->getType() == CypherResult::ResultType::NODES) {
for (auto *node : result->getNodes()) {
errs() << "Result node: " << node->getNodeType() << "\n";
}
}
}
Call Graph API
Include headers:
#include "IR/PDG/Core/PDGCallGraph.h"
Build and query call graph:
using namespace pdg;
PDGCallGraph &cg = PDGCallGraph::getInstance();
cg.build(*module);
// Check reachability between call-graph nodes
bool reachable = cg.canReach(*srcNode, *sinkNode);
// Compute paths between functions
auto paths = cg.computePaths(*srcNode, *sinkNode);
// Identify indirect call targets
auto candidates = cg.getIndirectCallCandidates(*callInst, *module);
Data Flow Analysis APIs
IFDS Taint Analysis API
Include headers:
#include "Dataflow/IFDS/Analyses/IFDSTaintAnalysis.h"
#include "Dataflow/IFDS/Solver/IFDSSolver.h"
Configure and run:
using namespace ifds;
TaintAnalysis analysis;
analysis.add_source_function("scanf");
analysis.add_sink_function("system");
IFDSSolver<TaintAnalysis> solver(analysis);
solver.solve(*module);
auto results = solver.get_all_results();
Bug Reporting APIs
BugReport and BugReportMgr API
Include headers:
#include "Checker/Framework/BugReport.h"
#include "Checker/Framework/BugReportMgr.h"
Report bugs:
BugReportMgr &mgr = BugReportMgr::get_instance();
int typeId = mgr.register_bug_type(
"NullDeref", BugDescription::BI_HIGH, BugDescription::BC_SECURITY,
"Null pointer dereference");
auto *report = new BugReport(typeId);
report->append_step(inst, "Dereference of null pointer");
mgr.insert_report(typeId, report, /*deduplicate_by_trace=*/true);
Example: Complete Analysis Tool
Here is an example building an analysis tool that integrates alias analysis, the PDG, and IFDS taint analysis:
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IRReader/IRReader.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/IR/LegacyPassManager.h"
#include "Alias/UnificationBased/DyckAA/DyckAliasAnalysis.h"
#include "IR/PDG/Core/ProgramDependencyGraph.h"
#include "Dataflow/IFDS/Analyses/IFDSTaintAnalysis.h"
#include "Dataflow/IFDS/Solver/IFDSSolver.h"
using namespace llvm;
int main(int argc, char **argv) {
if (argc < 2) {
errs() << "Usage: " << argv[0] << " <input.bc>\n";
return 1;
}
LLVMContext context;
SMDiagnostic error;
std::unique_ptr<Module> module = parseIRFile(argv[1], error, context);
if (!module) {
error.print(argv[0], errs());
return 1;
}
// 1. Alias analysis
DyckAliasAnalysis DAA;
DAA.runOnModule(*module);
// 2. Build PDG
legacy::PassManager PM;
PM.add(new pdg::DataDependencyGraph());
PM.add(new pdg::ControlDependencyGraph());
PM.add(new pdg::ProgramDependencyGraph());
PM.run(*module);
// 3. IFDS taint analysis
ifds::TaintAnalysis taint;
taint.add_source_function("scanf");
taint.add_sink_function("system");
ifds::IFDSSolver<ifds::TaintAnalysis> solver(taint);
solver.solve(*module);
auto results = solver.get_all_results();
errs() << "Analysis completed with " << results.size() << " result entries\n";
return 0;
}
Compile and link:
clang++ -o my_analyzer my_analyzer.cpp \
$(llvm-config --cxxflags --ldflags --libs) \
-L/path/to/lotus/build/lib \
-lCanaryDyckAA -lCanaryPDG -lIFDS
See Also
Architecture & Major Components - Understanding the framework architecture
Tutorials and Examples - Practical usage examples
Developer Guide - Extending Lotus