API Reference

This document provides API reference for programmatically using Lotus components.

Alias Analysis APIs

DyckAA API

Include headers:

#include "Alias/DyckAA/DyckAliasAnalysis.h"

Basic usage as LLVM pass:

#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/DyckAA/DyckAliasAnalysis.h"

using namespace llvm;

int main(int argc, char **argv) {
    LLVMContext context;
    SMDiagnostic error;

    // Load module
    std::unique_ptr<Module> module = parseIRFile(argv[1], error, context);
    if (!module) {
        error.print(argv[0], errs());
        return 1;
    }

    // Run DyckAA
    legacy::PassManager PM;
    PM.add(new DyckAliasAnalysis());
    PM.run(*module);

    return 0;
}

Query alias information:

// Get alias analysis result
DyckAliasAnalysis *DAA = new DyckAliasAnalysis();

// Query if two values may alias
AliasResult result = DAA->alias(val1, val2);

switch (result) {
    case NoAlias:
        errs() << "Definitely do not alias\n";
        break;
    case MayAlias:
        errs() << "May alias\n";
        break;
    case MustAlias:
        errs() << "Must alias\n";
        break;
    case PartialAlias:
        errs() << "Partially alias\n";
        break;
}

Access Dyck graph:

#include "Alias/DyckAA/DyckGraph.h"

DyckGraph *graph = DAA->getDyckGraph();

// Get representative for a value
DyckGraphNode *node = graph->retrieveDyckVertex(value);
DyckGraphNode *rep = node->getRep();

// Iterate over alias set
for (auto *aliasNode : rep->getAliasSet()) {
    Value *aliasValue = aliasNode->getValue();
    errs() << "Alias: " << *aliasValue << "\n";
}

AserPTA API

Include headers:

#include "Alias/AserPTA/PointerAnalysis/PointerAnalysis.h"
#include "Alias/AserPTA/PointerAnalysis/Context/Context.h"
#include "Alias/AserPTA/PointerAnalysis/Solver/Solver.h"

Configure and run analysis:

#include "Alias/AserPTA/PointerAnalysis/Program/Program.h"
#include "Alias/AserPTA/PointerAnalysis/PointerAnalysis.h"

using namespace aser;

// Build program representation
Program program(module);
program.build();

// Create analysis with context sensitivity
PointerAnalysis<Context::KCallSite<1>, MemModel::FieldSensitive> pta(program);

// Run solver
pta.solve();

// Query points-to sets
for (auto *val : program.getPointers()) {
    const PointsToSet &pts = pta.getPointsToSet(val);
    errs() << "Pointer: " << *val << " points to:\n";
    for (auto objID : pts) {
        errs() << "  Object " << objID << "\n";
    }
}

Custom context sensitivity:

// Context-insensitive
PointerAnalysis<Context::NoCtx, MemModel::FieldInsensitive> pta_ci(program);

// 1-CFA (1-call-site sensitive)
PointerAnalysis<Context::KCallSite<1>, MemModel::FieldSensitive> pta_1cfa(program);

// 2-CFA
PointerAnalysis<Context::KCallSite<2>, MemModel::FieldSensitive> pta_2cfa(program);

// Origin-sensitive
PointerAnalysis<Context::KOrigin<1>, MemModel::FieldSensitive> pta_origin(program);

LotusAA API

Include headers:

#include "Alias/LotusAA/Engine/InterProceduralPass.h"

Use as LLVM pass:

legacy::PassManager PM;
PM.add(new LotusAA());
PM.run(*module);

Query results:

LotusAA *lotus = new LotusAA();
// ... run analysis ...

// Get points-to graph
PointsToGraph *ptg = lotus->getPointsToGraph();

// Query points-to set for a value
std::set<MemObject*> pts = ptg->getPointsToSet(value);

for (auto *obj : pts) {
    errs() << "Points to: " << obj->toString() << "\n";
}

Intermediate Representation APIs

Program Dependence Graph API

Include headers:

#include "IR/PDG/Core/ProgramDependencyGraph.h"
#include "IR/PDG/Core/Graph.h"

Build PDG:

using namespace pdg;

// Register required passes
legacy::PassManager PM;
PM.add(new DataDependencyGraph());
PM.add(new ControlDependencyGraph());
PM.add(new ProgramDependencyGraph());
PM.run(*module);

// Get PDG instance
ProgramGraph *pdg = &ProgramGraph::getInstance();

Traverse PDG:

// Iterate over all nodes
for (auto *node : pdg->getNodes()) {
    errs() << "Node type: " << node->getNodeTypeStr() << "\n";

    // Get outgoing edges
    for (auto *edge : node->getOutEdges()) {
        Node *target = edge->getTarget();
        EdgeType type = edge->getEdgeType();
        errs() << "  -> " << target->getNodeTypeStr()
               << " (edge: " << edge->getEdgeTypeStr() << ")\n";
    }
}

Query node types:

// Get function wrapper
Function *F = /* ... */;
FunctionWrapper *fw = pdg->getFuncWrapperMap()[F];

// Get entry node
Node *entryNode = fw->getEntryNode();

// Get formal input parameters
Tree *formalInTree = fw->getFormalInTree();

// Get formal output (return values)
Tree *formalOutTree = fw->getFormalOutTree();

Check dependencies:

// Check if there's a path from src to dst
bool hasPath = pdg->canReach(*srcNode, *dstNode);

// Check reachability excluding certain edge types
std::set<EdgeType> exclude = {EdgeType::CONTROL_DEP};
bool hasDataPath = pdg->canReach(*srcNode, *dstNode, exclude);

PDG Cypher Query API

Include headers:

#include "IR/PDG/Analysis/CypherQuery.h"
#include "IR/PDG/Core/ProgramDependencyGraph.h"

Parse and execute Cypher queries:

using namespace pdg;

// Build PDG first
ProgramDependencyGraph *pdgPass = /* get pass */;
ProgramGraph &pdg = pdgPass->getPDG();

// Create Cypher query executor
CypherQueryExecutor executor(pdg);

// Parse Cypher query
CypherParser parser;
std::string queryStr = "MATCH (n:FUNC_ENTRY) WHERE n.name = 'main' RETURN n";
std::unique_ptr<CypherQuery> query = parser.parse(queryStr);

// Execute query
std::unique_ptr<CypherResult> result = executor.execute(*query);

// Process results
if (result->getType() == CypherResult::ResultType::NODES) {
    for (auto *node : result->getNodes()) {
        errs() << "Result node: " << *node << "\n";
    }
}

Execute queries with error handling:

CypherParser parser;
std::string queryStr = "MATCH (n) RETURN n";
std::unique_ptr<CypherQuery> query = parser.parse(queryStr);

if (parser.hasError()) {
    errs() << "Parse error: " << parser.getLastError().toString() << "\n";
    return;
}

CypherQueryExecutor executor(pdg);
std::unique_ptr<CypherResult> result = executor.execute(*query);

if (!executor.getLastError().empty()) {
    errs() << "Execution error: " << executor.getLastError() << "\n";
    return;
}

Query with parameters:

CypherQueryParameters params;
params["functionName"] = "main";

std::string queryStr = "MATCH (n:FUNC_ENTRY) WHERE n.name = $functionName RETURN n";
std::unique_ptr<CypherQuery> query = parser.parse(queryStr, params);

Data Flow Analysis APIs

IFDS/IDE Framework API

Include headers:

#include "Dataflow/IFDS/IFDSSolver.h"
#include "Dataflow/IFDS/FlowFunction.h"

Define flow functions:

#include "Dataflow/IFDS/IFDSProblem.h"

class MyTaintAnalysis : public IFDSProblem<Value*, Function*> {
public:
    // Define initial seeds
    std::set<Value*> initialSeeds() override {
        std::set<Value*> seeds;
        // Add taint sources
        for (auto &F : module) {
            if (F.getName() == "getInput") {
                // Return value is tainted
                seeds.insert(&F);
            }
        }
        return seeds;
    }

    // Normal flow function
    FlowFunctionPtr getNormalFlowFunction(Instruction *curr,
                                          Instruction *succ) override {
        // Define how facts flow through instructions
        if (auto *call = dyn_cast<CallInst>(curr)) {
            if (isSanitizer(call)) {
                // Kill taint at sanitizer
                return std::make_shared<KillAllFlowFunction>();
            }
        }
        // Identity by default
        return std::make_shared<IdentityFlowFunction>();
    }

    // Call flow function
    FlowFunctionPtr getCallFlowFunction(Instruction *callSite,
                                        Function *destFun) override {
        // Map actual parameters to formal parameters
        return std::make_shared<ParameterMapFlowFunction>(callSite, destFun);
    }

    // Return flow function
    FlowFunctionPtr getReturnFlowFunction(Instruction *callSite,
                                          Function *calleeFun,
                                          Instruction *exitInst) override {
        // Map return value to call site
        return std::make_shared<ReturnMapFlowFunction>(callSite, exitInst);
    }
};

Run IFDS solver:

MyTaintAnalysis problem(module);
IFDSSolver<Value*, Function*> solver(problem);
solver.solve();

// Query results
for (auto &F : module) {
    for (auto &BB : F) {
        for (auto &I : BB) {
            auto facts = solver.ifdsResultsAt(&I);
            if (!facts.empty()) {
                errs() << "Tainted at: " << I << "\n";
                for (auto *fact : facts) {
                    errs() << "  Fact: " << *fact << "\n";
                }
            }
        }
    }
}

Taint Analysis API

Include headers:

#include "Dataflow/TaintAnalysis/TaintAnalysis.h"

Configure and run:

// Create taint analysis
TaintAnalysis taint(module);

// Configure sources
taint.addSourceFunction("scanf");
taint.addSourceFunction("gets");
taint.addSourceFunction("read");

// Configure sinks
taint.addSinkFunction("system");
taint.addSinkFunction("exec");
taint.addSinkFunction("popen");

// Run analysis
taint.analyze();

// Get results
std::vector<TaintFlow> flows = taint.getTaintFlows();
for (auto &flow : flows) {
    errs() << "Taint flow:\n";
    errs() << "  Source: " << *flow.source << "\n";
    errs() << "  Sink: " << *flow.sink << "\n";
    errs() << "  Path length: " << flow.pathLength << "\n";
}

Abstract Interpretation APIs

CLAM API

Include headers:

#include "Apps/clam/Clam.hh"
#include "Apps/clam/ClamAnalysisParams.hh"

Configure analysis:

using namespace clam;

// Set analysis parameters
ClamAnalysisParams params;
params.dom = "zones";  // Abstract domain
params.run_inter = true;  // Interprocedural
params.check_nulls = true;  // Check null pointers
params.check_bounds = true;  // Check array bounds

// Create analyzer
ClamGlobalAnalysis analyzer(module, params);

// Run analysis
analyzer.analyze();

Query invariants:

// Get invariants at a program point
for (auto &F : module) {
    for (auto &BB : F) {
        clam_abstract_domain inv = analyzer.get_pre(&BB);

        // Check if a variable is in range
        if (inv.is_bottom()) {
            errs() << "Unreachable basic block\n";
        } else {
            errs() << "Invariant at " << BB.getName() << ": ";
            inv.write(errs());
            errs() << "\n";
        }
    }
}

Check properties:

#include "crab/checkers/base_property.hpp"

// Get check results
auto checks = analyzer.get_all_checks();

for (auto &check : checks) {
    switch (check.result) {
        case crab::checker::check_kind::SAFE:
            errs() << "SAFE: " << check.description << "\n";
            break;
        case crab::checker::check_kind::WARNING:
            errs() << "WARNING: " << check.description << "\n";
            break;
        case crab::checker::check_kind::ERROR:
            errs() << "ERROR: " << check.description << "\n";
            break;
    }
}

Constraint Solving APIs

SMT Solver API

Include headers:

#include "Solvers/SMT/Z3Solver.h"

Basic usage:

using namespace lotus::smt;

// Create Z3 context and solver
Z3Solver solver;

// Create variables
z3::expr x = solver.makeIntVar("x");
z3::expr y = solver.makeIntVar("y");

// Add constraints
solver.addConstraint(x >= 0);
solver.addConstraint(x <= 10);
solver.addConstraint(y == x + 5);

// Check satisfiability
if (solver.check() == z3::sat) {
    z3::model model = solver.getModel();
    int x_val = model.eval(x).get_numeral_int();
    int y_val = model.eval(y).get_numeral_int();
    errs() << "Solution: x = " << x_val << ", y = " << y_val << "\n";
} else {
    errs() << "Unsatisfiable\n";
}

Build complex formulas:

// Logical operations
z3::expr formula = (x > 0) && (y < 10) || (x == y);

// Arrays
z3::expr arr = solver.makeArrayVar("arr", solver.intSort());
z3::expr arrSelect = z3::select(arr, x);
solver.addConstraint(arrSelect == 42);

// Quantifiers
z3::expr i = solver.makeIntVar("i");
z3::expr forall = z3::forall(i, z3::implies(i >= 0 && i < 10,
                                             z3::select(arr, i) >= 0));

BDD Solver API

Include headers:

#include "Solvers/BDD/BDDSolver.h"

Basic operations:

using namespace lotus::bdd;

// Initialize BDD manager
BDDManager mgr;

// Create variables
BDD x = mgr.createVar("x");
BDD y = mgr.createVar("y");
BDD z = mgr.createVar("z");

// Boolean operations
BDD formula = (x | y) & (~z);

// Check satisfiability
if (!formula.isZero()) {
    errs() << "Formula is satisfiable\n";

    // Get one satisfying assignment
    std::map<std::string, bool> assignment = formula.oneSat();
    for (auto &[var, val] : assignment) {
        errs() << var << " = " << (val ? "true" : "false") << "\n";
    }
}

Bug Detection APIs

Kint API

Include headers:

#include "Checker/KINT/MKintPass.h"

Use programmatically:

// Create pass
MKintPass kintPass;

// Configure checkers
kintPass.enableChecker(CheckerType::IntOverflow);
kintPass.enableChecker(CheckerType::ArrayOutOfBounds);

// Run on module
kintPass.runOnModule(module);

// Get bug reports
std::vector<BugReport> bugs = kintPass.getBugReports();

for (auto &bug : bugs) {
    errs() << "Bug Type: " << bug.type << "\n";
    errs() << "Location: " << bug.location << "\n";
    errs() << "Description: " << bug.description << "\n";
}

Utility APIs

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);

// Get callers of a function
Function *F = /* ... */;
std::set<CallInst*> callers = cg.getCallers(F);

// Get callees of a call site
CallInst *call = /* ... */;
std::set<Function*> callees = cg.getCallees(call);

// Check if function is reachable
bool reachable = cg.isReachable(mainFunc, targetFunc);

Memory Model API

Include headers:

#include "Alias/AserPTA/PointerAnalysis/Model/MemModels.h"

Query memory objects:

// Get object ID for a value
ObjID obj = memModel.getObject(value);

// Get field offset
FieldOffset offset = memModel.getFieldOffset(gepInst);

// Check if field-sensitive
if (memModel.isFieldSensitive()) {
    errs() << "Using field-sensitive model\n";
}

Configuration API

Include headers:

#include "Utils/Config/LotusConfig.h"

Load and use configuration:

// Load YAML configuration
LotusConfig config;
config.loadFromFile("config.yaml");

// Query configuration values
int timeout = config.getInt("analysis.timeout", 300);
bool verbose = config.getBool("logging.verbose", false);
std::string domain = config.getString("clam.domain", "zones");

// Set configuration programmatically
config.set("analysis.max_iterations", 100);
config.set("checker.enabled", true);

Example: Complete Analysis Tool

Here’s a complete example of building a custom analysis tool:

#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/DyckAA/DyckAliasAnalysis.h"
#include "IR/PDG/Core/ProgramDependencyGraph.h"
#include "Dataflow/TaintAnalysis/TaintAnalysis.h"
#include "Checker/KINT/MKintPass.h"

using namespace llvm;

int main(int argc, char **argv) {
    if (argc < 2) {
        errs() << "Usage: " << argv[0] << " <input.bc>\n";
        return 1;
    }

    // Load module
    LLVMContext context;
    SMDiagnostic error;
    std::unique_ptr<Module> module = parseIRFile(argv[1], error, context);

    if (!module) {
        error.print(argv[0], errs());
        return 1;
    }

    // Run analyses
    legacy::PassManager PM;

    // 1. Alias analysis
    PM.add(new DyckAliasAnalysis());

    // 2. Build PDG
    PM.add(new pdg::DataDependencyGraph());
    PM.add(new pdg::ControlDependencyGraph());
    PM.add(new pdg::ProgramDependencyGraph());

    // 3. Bug detection
    PM.add(new MKintPass());

    PM.run(*module);

    // 4. Taint analysis
    TaintAnalysis taint(*module);
    taint.addSourceFunction("scanf");
    taint.addSinkFunction("system");
    taint.analyze();

    // Print results
    auto flows = taint.getTaintFlows();
    errs() << "Found " << flows.size() << " taint flows\n";

    return 0;
}

Compile and link:

clang++ -o my_analyzer my_analyzer.cpp \
    $(llvm-config --cxxflags --ldflags --libs) \
    -L/path/to/lotus/build/lib \
    -lLotusAlias -lLotusIR -lLotusDataflow -lLotusChecker

See Also