Skip to content

Commit 0ad3d3e

Browse files
fabianbs96Fabian Schiebel
andauthored
MonoIFDS (#837)
* Start adding MonoIFDS to phasar * Port rest of monoifds-solver * Add rest of monoifds to phasar * Integrating MonoIFDSTaintAnalysis into phasar-cli + make double_free01_c_dbg.ll pass (2nd test does not pass, though!!) * Fix ieration order + make tests pass * Add initializer to MonoIFDSSolver * Add some documentation + add summaryFlow() to MonoIFDS * Add unittest for MonoIFDS and make it pass * Add some more comments * Add shouldBeInSummary * minor * Make ICFG independent from analysis domain for MonoIFDSSolver * Add functionCompressor, usedGlobals, and CGSCCs to helperAnalyses to improve usability * minor * Some somments + renaming * domain * Some nullable-correctness in analyzeBlockImpl * Integrate MonoIFDS with HelperAnalyses * Use llvm::SmallBitVector in DataFlowEnvironment to match behavior presented in paper * monir stuff + some general changes that should go into their own PR (aliasinfo -> aliasiterator conversion) * As discussed, remove Eric from file headers * Add example, how to use MonoIFDS * pre-commit + readme * modules * llvm 22 * Fix dependency of UsedGlobals to ProjectIRDB * Fix MonoIFDSSolver typo * minor * Fix module compilation. Also use DummyFn in concept instead of lambda to prevent a clang-22 crash --------- Co-authored-by: Fabian Schiebel <fabian.schiebel@uni-paderborn.de>
1 parent ab4b7ff commit 0ad3d3e

69 files changed

Lines changed: 3590 additions & 108 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.clang-tidy

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ Checks: '-*,
1919
-readability-identifier-length,
2020
-readability-redundant-member-init,
2121
-readability-use-anyofallof,
22+
-readability-avoid-return-with-void-value,
23+
-readability-use-std-min-max,
2224
cppcoreguidelines-*,
2325
-cppcoreguidelines-avoid-non-const-global-variables,
2426
-cppcoreguidelines-pro-bounds-array-to-pointer-decay,
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
cmake_minimum_required(VERSION 3.14...3.28)
2+
3+
project(run-monoifds-analysis)
4+
5+
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
6+
7+
find_package(phasar REQUIRED CONFIG)
8+
9+
10+
add_executable(run-monoifds-analysis-helper-analyses helper-analyses.cpp)
11+
target_link_libraries(run-monoifds-analysis-helper-analyses PRIVATE phasar::phasar)
12+
13+
add_executable(run-monoifds-analysis-manual manual.cpp)
14+
target_link_libraries(run-monoifds-analysis-manual PRIVATE phasar::phasar)
15+
16+
17+
if (TARGET run_sample_programs)
18+
add_custom_target(run_run_monoifds_analysis
19+
DEPENDS run-monoifds-analysis-helper-analyses run-monoifds-analysis-manual LLFileGeneration
20+
COMMAND $<TARGET_FILE:run-monoifds-analysis-helper-analyses> "${CMAKE_CURRENT_BINARY_DIR}/../llvm-hello-world/target/taint_cpp_dbg.ll"
21+
COMMAND $<TARGET_FILE:run-monoifds-analysis-manual> "${CMAKE_CURRENT_BINARY_DIR}/../llvm-hello-world/target/taint_cpp_dbg.ll"
22+
)
23+
24+
add_dependencies(run_sample_programs run_run_monoifds_analysis)
25+
endif()
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Run a MonoIFDS Analysis
2+
3+
Shows some way, how you can use PhASAR to run an already existing MonoIFDS analysis on a LLVM IR module.
4+
For this example, we selected the `MonoIFDSTaintAnalysis`.
5+
6+
You may look at the different C++ source files to see, how you can run an MonoIFDS taint analysis using PhASAR.
7+
We suggest to start with the simplest example [helper-analyses.cpp](./helper-analyses.cpp).
8+
9+
## Build
10+
11+
This example program can be built using cmake.
12+
It assumes, that you have installed PhASAR on your system. If you did not install PhASAR to a default location, you can specify `-Dphasar_ROOT=your/path/to/phasar` when invoking `cmake`, replacing "your/path/to/phasar" by the actual path where you have installed PhASAR.
13+
14+
```bash
15+
# Invoked from the 06-run-monoifds-analysis root folder:
16+
$ mkdir -p build && cd build
17+
$ cmake ..
18+
$ cmake --build .
19+
```
20+
21+
## Test
22+
23+
You can test the example program on the target programs from [llvm-hello-world/target](../../llvm-hello-world/target/).
24+
25+
```bash
26+
# Invoked from the 06-run-monoifds-analysis/build folder:
27+
./run-monoifds-analysis-helper-analyses ../../../llvm-hello-world/target/taint.ll
28+
29+
./run-monoifds-analysis-manual ../../../llvm-hello-world/target/taint.ll
30+
```
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#include "phasar/DataFlow.h" // For MonoIFDSSolver
2+
#include "phasar/PhasarLLVM.h" // For the HelperAnalyses
3+
#include "phasar/PhasarLLVM/DataFlow.h" // For the MonoIFDSTaintAnalysis
4+
#include "phasar/PhasarLLVM/Pointer/FilteredLLVMAliasIterator.h"
5+
#include "phasar/PhasarLLVM/TaintConfig.h" // For the LLVMTaintConfig
6+
7+
int main(int Argc, char *Argv[]) {
8+
if (Argc < 2) {
9+
llvm::errs()
10+
<< "USAGE: run-monoifds-analysis-helper-analyses <LLVM-IR file>\n";
11+
return 1;
12+
}
13+
14+
using namespace std::string_literals;
15+
std::vector EntryPoints = {"main"s};
16+
17+
// Instead of creating all the helper analyses ourselves, we can just use the
18+
// HelperAnalyses class. It will create the necessary information on-demand.
19+
//
20+
// You can customize the underlying algorithms by passing a
21+
// HelperAnalysisConfig as third parameter
22+
psr::HelperAnalyses HA(Argv[1], EntryPoints);
23+
if (!HA.getProjectIRDB()) {
24+
return 1;
25+
}
26+
27+
// Create the taint configuration
28+
psr::LLVMTaintConfig TC(HA.getProjectIRDB());
29+
TC.print();
30+
llvm::outs() << "------------------------\n";
31+
32+
// More precise alias-information; techically, this is not required, but it
33+
// helps a lot
34+
psr::FilteredLLVMAliasIterator FAI(HA.getAliasInfo());
35+
36+
// Create the taint analysis problem:
37+
psr::monoifds::TaintAnalysis TaintProblem(&TC, &HA.getUsedGlobals(), &FAI);
38+
39+
// To solve the taint problem, we now create an instance of the
40+
// MonoIFDSSolver. Passing the HelperAnalyses here, lets the solver
41+
// automatically grab the needed information
42+
psr::monoifds::MonoIFDSSolver Solver(&TaintProblem, HA);
43+
44+
// Solves the taint problem. This may take some time.
45+
Solver.solve();
46+
47+
// The monoifds::TaintAnalysis is set-up to use the analysis-printer (see
48+
// ../04-run-ifds-analysis/otf-reporter.cpp). By default, it prints the
49+
// detected leaks into the given llvm::raw_ostream
50+
TaintProblem.emitTextReport(llvm::outs());
51+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
#include "phasar/DataFlow.h" // For MonoIFDSSolver
2+
#include "phasar/PhasarLLVM/ControlFlow.h" // For FunctionCompressor & getEntryFunctions
3+
#include "phasar/PhasarLLVM/DataFlow.h" // For the MonoIFDSTaintAnalysis
4+
#include "phasar/PhasarLLVM/Pointer.h" // For the LLVMAliasSet
5+
#include "phasar/PhasarLLVM/TaintConfig.h" // For the LLVMTaintConfig
6+
#include "phasar/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchy.h"
7+
#include "phasar/PhasarLLVM/Utils/UsedGlobals.h"
8+
9+
int main(int Argc, char *Argv[]) {
10+
if (Argc < 2) {
11+
llvm::errs() << "USAGE: run-monoifds-analysis-manual <LLVM-IR file>\n";
12+
return 1;
13+
}
14+
15+
using namespace std::string_literals;
16+
std::vector EntryPoints = {"main"s};
17+
18+
// Load the IR
19+
auto IRDB = psr::LLVMProjectIRDB::loadOrExit(Argv[1]);
20+
21+
// The MonoIFDSTaintAnalysis requires alias information, so create it here
22+
psr::LLVMAliasSet AS(&IRDB);
23+
24+
// We use a type-hierarchy to build the call-graph (LLVMBasedICFG below)
25+
psr::DIBasedTypeHierarchy TH(IRDB);
26+
27+
// Create the ICFG
28+
psr::LLVMBasedICFG ICFG(&IRDB, psr::CallGraphAnalysisType::VTA, {"main"}, &TH,
29+
&AS);
30+
31+
// Assign each reachable llvm::Function in the call-graph a sequential ID.
32+
// This is needed for SCC computation and for the solver
33+
auto Funs = psr::compressFunctions(ICFG.getCallGraph(),
34+
psr::getEntryFunctions(IRDB, EntryPoints));
35+
36+
// Compute the call-graph SCCs.
37+
auto CGSCCs = computeCGSCCs(ICFG, Funs);
38+
39+
// Build a dependency-graph induced by the call-graph, collapsing each SCC to
40+
// a single node
41+
auto SCCC = computeCGSCCCallers(ICFG, Funs, CGSCCs);
42+
43+
// For each CGSCC, compute which global variables are (transitively) used by
44+
// any function in that SCC
45+
auto UG = psr::computeUsedGlobals(IRDB, Funs, CGSCCs, SCCC);
46+
47+
// Create the taint configuration
48+
psr::LLVMTaintConfig TC(IRDB);
49+
TC.print();
50+
llvm::outs() << "------------------------\n";
51+
52+
// More precise alias-information; techically, this is not required, but it
53+
// helps a lot
54+
psr::FilteredLLVMAliasIterator FAI(&AS);
55+
56+
// Create the taint analysis problem:
57+
psr::monoifds::TaintAnalysis TaintProblem(&TC, &UG, &FAI);
58+
59+
// To solve the taint problem, we now create an instance of the
60+
// MonoIFDSSolver. Passing the HelperAnalyses here, lets the solver
61+
// automatically grab the needed information
62+
psr::monoifds::MonoIFDSSolver Solver(&TaintProblem, &ICFG);
63+
64+
// Supply the solver with the previously computed helper information. If we
65+
// don't provide this, the solver would compute them on its own once solve()
66+
// is called.
67+
Solver.setCGSCCs(&CGSCCs).setFunctionCompressor(&Funs);
68+
69+
// Solves the taint problem. This may take some time.
70+
Solver.solve();
71+
72+
// The monoifds::TaintAnalysis is set-up to use the analysis-printer (see
73+
// ../04-run-ifds-analysis/otf-reporter.cpp). By default, it prints the
74+
// detected leaks into the given llvm::raw_ostream
75+
TaintProblem.emitTextReport(llvm::outs());
76+
}

include/phasar/ControlFlow/CFG.h

Lines changed: 54 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,21 @@
88
*****************************************************************************/
99
#pragma once
1010

11+
#include "phasar/Utils/Nullable.h"
1112
#include "phasar/Utils/TypeTraits.h"
1213

1314
#include "llvm/Support/raw_ostream.h"
1415

1516
#include <concepts>
17+
#include <type_traits>
1618
#include <utility>
1719

1820
namespace psr {
1921

2022
template <typename T>
2123
concept InstructionClassifier =
22-
requires(const T &IC, typename T::n_t Inst, typename T::n_t Succ) {
24+
requires(const T &IC, typename std::remove_cvref_t<T>::n_t Inst,
25+
typename std::remove_cvref_t<T>::n_t Succ) {
2326
{ IC.isCallSite(Inst) } -> std::convertible_to<bool>;
2427
{ IC.isFieldLoad(Inst) } -> std::convertible_to<bool>;
2528
{ IC.isFieldStore(Inst) } -> std::convertible_to<bool>;
@@ -28,27 +31,36 @@ concept InstructionClassifier =
2831
};
2932

3033
template <typename T>
31-
concept CFG = requires(const T &CF, typename T::n_t Inst, typename T::f_t Fun) {
32-
typename T::n_t;
33-
typename T::f_t;
34+
concept CFG = requires(const T &CF, typename std::remove_cvref_t<T>::n_t Inst,
35+
typename std::remove_cvref_t<T>::f_t Fun) {
36+
typename std::remove_cvref_t<T>::n_t;
37+
typename std::remove_cvref_t<T>::f_t;
3438

3539
/// Returns the function that contains the given instruction Inst.
3640
// TODO: Actually belongs into ProjectIRDB!
37-
{ CF.getFunctionOf(Inst) } -> std::convertible_to<typename T::f_t>;
41+
{
42+
CF.getFunctionOf(Inst)
43+
} -> std::convertible_to<typename std::remove_cvref_t<T>::f_t>;
3844
/// Returns an iterable range of all instructions of the given function that
3945
/// are part of the control-flow graph.
4046
// TODO: We should have sth like this in the ProjectIRDB as well!
41-
{ CF.getAllInstructionsOf(Fun) } -> psr::is_iterable_over_v<typename T::n_t>;
47+
{
48+
CF.getAllInstructionsOf(Fun)
49+
} -> psr::is_iterable_over_v<typename std::remove_cvref_t<T>::n_t>;
4250

4351
/// Returns an iterable range of all successor instructions of Inst in the
4452
/// CFG.
4553
/// NOTE: This function is typically being called in a hot part of the
4654
/// analysis and should therefore be highly optimized for performance.
47-
{ CF.getSuccsOf(Inst) } -> psr::is_iterable_over_v<typename T::n_t>;
55+
{
56+
CF.getSuccsOf(Inst)
57+
} -> psr::is_iterable_over_v<typename std::remove_cvref_t<T>::n_t>;
4858

4959
/// Returns an iterable range of all starting instructions of the given
5060
/// function. For a forward-CFG, this is typically a singleton range.
51-
{ CF.getStartPointsOf(Fun) } -> psr::is_iterable_over_v<typename T::n_t>;
61+
{
62+
CF.getStartPointsOf(Fun)
63+
} -> psr::is_iterable_over_v<typename std::remove_cvref_t<T>::n_t>;
5264

5365
/// Returns whether the given Inst is a root node of the CFG
5466
{ CF.isStartPoint(Inst) } -> std::convertible_to<bool>;
@@ -59,32 +71,54 @@ concept CFG = requires(const T &CF, typename T::n_t Inst, typename T::f_t Fun) {
5971
requires InstructionClassifier<T>;
6072
};
6173

74+
template <typename T, typename N, typename F>
75+
concept CFGOf =
76+
CFG<T> && std::same_as<N, typename std::remove_cvref_t<T>::n_t> &&
77+
std::same_as<F, typename std::remove_cvref_t<T>::f_t>;
78+
6279
template <typename T>
6380
concept BidiCFG =
64-
CFG<T> && requires(const T &CF, typename T::n_t Inst, typename T::f_t Fun) {
81+
CFG<T> && requires(const T &CF, typename std::remove_cvref_t<T>::n_t Inst,
82+
typename std::remove_cvref_t<T>::f_t Fun) {
6583
/// Returns an iterable range of all predecessor instructions of Inst in
6684
/// the CFG
67-
{ CF.getPredsOf(Inst) } -> psr::is_iterable_over_v<typename T::n_t>;
85+
{
86+
CF.getPredsOf(Inst)
87+
} -> psr::is_iterable_over_v<typename std::remove_cvref_t<T>::n_t>;
6888

6989
/// Returns an iterable range of all exit instructions (often return
7090
/// instructions) of the given function. For a backward-CFG, this is
7191
/// typically a singleton range
72-
{ CF.getExitPointsOf(Fun) } -> psr::is_iterable_over_v<typename T::n_t>;
92+
{
93+
CF.getExitPointsOf(Fun)
94+
} -> psr::is_iterable_over_v<typename std::remove_cvref_t<T>::n_t>;
7395
};
7496

7597
template <typename T>
76-
concept CFGDump = requires(const T &CF, typename T::n_t Inst,
77-
typename T::f_t Fun, llvm::raw_ostream &OS) {
78-
{ CF.getStatementId(Inst) } -> psr::is_string_like_v;
79-
{ CF.getFunctionName(Fun) } -> psr::is_string_like_v;
80-
{ CF.getDemangledFunctionName(Fun) } -> psr::is_string_like_v;
81-
CF.print(Fun, OS);
82-
};
98+
concept CFGDump =
99+
requires(const T &CF, typename std::remove_cvref_t<T>::n_t Inst,
100+
typename std::remove_cvref_t<T>::f_t Fun, llvm::raw_ostream &OS) {
101+
{ CF.getStatementId(Inst) } -> psr::is_string_like_v;
102+
{ CF.getFunctionName(Fun) } -> psr::is_string_like_v;
103+
{ CF.getDemangledFunctionName(Fun) } -> psr::is_string_like_v;
104+
CF.print(Fun, OS);
105+
};
83106

84107
template <typename T>
85-
concept CFGEdgesProvider = requires(const T &CF, typename T::f_t Fun) {
108+
concept CFGEdgesProvider = requires(const T &CF,
109+
typename std::remove_cvref_t<T>::f_t Fun) {
86110
{
87111
CF.getAllControlFlowEdges(Fun)
88-
} -> psr::is_iterable_over_v<std::pair<typename T::n_t, typename T::n_t>>;
112+
} -> psr::is_iterable_over_v<std::pair<typename std::remove_cvref_t<T>::n_t,
113+
typename std::remove_cvref_t<T>::n_t>>;
89114
};
115+
116+
template <typename T>
117+
concept IsBlockAwareControlFlow =
118+
requires(const T &CF, typename std::remove_cvref_t<T>::n_t Inst) {
119+
{
120+
CF.getUniqueSuccessor(Inst)
121+
} -> std::convertible_to<Nullable<typename std::remove_cvref_t<T>::n_t>>;
122+
{ CF.hasUniquePredecessor(Inst) } -> std::convertible_to<bool>;
123+
};
90124
} // namespace psr

0 commit comments

Comments
 (0)