diff --git a/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSTaintAnalysis.cpp b/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSTaintAnalysis.cpp index a61639ee3b..e3b2a27b62 100644 --- a/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSTaintAnalysis.cpp +++ b/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSTaintAnalysis.cpp @@ -360,6 +360,30 @@ auto IFDSTaintAnalysis::getNormalFlowFunction(n_t Curr, }); } + // A select yields one of its two value operands: if either is tainted, the + // result may be tainted. (The condition is not tracked; that would be an + // implicit flow.) + if (const auto *Select = llvm::dyn_cast(Curr)) { + return lambdaFlow([Select](d_t Source) -> container_type { + if (Source == Select->getTrueValue() || + Source == Select->getFalseValue()) { + return {Source, Select}; + } + return {Source}; + }); + } + + // A phi merges values from predecessor blocks: if any incoming value is + // tainted, the merged value may be tainted. + if (const auto *Phi = llvm::dyn_cast(Curr)) { + return lambdaFlow([Phi](d_t Source) -> container_type { + if (llvm::is_contained(Phi->incoming_values(), Source)) { + return {Source, Phi}; + } + return {Source}; + }); + } + // Otherwise we do not care and leave everything as it is return identityFlow(); } diff --git a/test/llvm_test_code/taint_analysis/double_free/df_ctx_01.c b/test/llvm_test_code/taint_analysis/double_free/df_ctx_01.c index 85b45becc1..1dd5d6013a 100644 --- a/test/llvm_test_code/taint_analysis/double_free/df_ctx_01.c +++ b/test/llvm_test_code/taint_analysis/double_free/df_ctx_01.c @@ -16,13 +16,13 @@ int main() { // xtaint: /taint_analysis/double_free/df_ctx_01.c:10:3: // RUN: %phasar-cli --data-flow-analysis=ifds-taint --module %llvm_test_code/taint_analysis/double_free/df_ctx_01_c_dbg.ll --analysis-config %config/double-free-config.json | FileCheck %s -check-prefix=ifds-taint -// ifds-taint: No leaks found! +// ifds-taint: /taint_analysis/double_free/df_ctx_01.c:10:3: // RUN: %phasar-cli --data-flow-analysis=ifds-fieldsens-taint --module %llvm_test_code/taint_analysis/double_free/df_ctx_01_c_dbg.ll --analysis-config %config/double-free-config.json | FileCheck %s -check-prefix=ifds-fieldsens-taint -// ifds-fieldsens-taint: No leaks found! +// ifds-fieldsens-taint: /taint_analysis/double_free/df_ctx_01.c:10:3: // RUN: %phasar-cli --data-flow-analysis=monoifds-taint --module %llvm_test_code/taint_analysis/double_free/df_ctx_01_c_dbg.ll --analysis-config %config/double-free-config.json | FileCheck %s -check-prefix=monoifds-taint // monoifds-taint: /taint_analysis/double_free/df_ctx_01.c:10:3: // RUN: %phasar-cli --data-flow-analysis=sparse-ifds-taint --module %llvm_test_code/taint_analysis/double_free/df_ctx_01_c_dbg.ll --analysis-config %config/double-free-config.json | FileCheck %s -check-prefix=sparse-ifds-taint -// sparse-ifds-taint: No leaks found! +// sparse-ifds-taint: /taint_analysis/double_free/df_ctx_01.c:10:3: diff --git a/test/llvm_test_code/taint_analysis/dummy_source_sink/CMakeLists.txt b/test/llvm_test_code/taint_analysis/dummy_source_sink/CMakeLists.txt index 51d77070d3..e2e5576ec8 100644 --- a/test/llvm_test_code/taint_analysis/dummy_source_sink/CMakeLists.txt +++ b/test/llvm_test_code/taint_analysis/dummy_source_sink/CMakeLists.txt @@ -22,6 +22,15 @@ set(taint_tests_mem2reg taint_01.cpp taint_06.cpp taint_exception_01.cpp + taint_select_01.cpp +) + +# Compiled with -O1, so that SimplifyCFG turns branches into select +# instructions +set(taint_tests_opt + taint_select_01.cpp + taint_select_02.cpp + taint_select_03.cpp ) @@ -33,3 +42,7 @@ endforeach(TEST_SRC) foreach(TEST_SRC ${taint_tests_mem2reg}) generate_ll_file(FILE ${TEST_SRC} MEM2REG DEBUG) endforeach(TEST_SRC) + +foreach(TEST_SRC ${taint_tests_opt}) + generate_ll_file(FILE ${TEST_SRC} DEBUG O1) +endforeach(TEST_SRC) diff --git a/test/llvm_test_code/taint_analysis/dummy_source_sink/taint_select_01.cpp b/test/llvm_test_code/taint_analysis/dummy_source_sink/taint_select_01.cpp new file mode 100644 index 0000000000..9ef949b55a --- /dev/null +++ b/test/llvm_test_code/taint_analysis/dummy_source_sink/taint_select_01.cpp @@ -0,0 +1,9 @@ +extern int source(); // dummy source +extern void sink(int p); // dummy sink + +int main(int argc, char **argv) { + int a = source(); + int b = argc > 1 ? a : 0; + sink(b); + return 0; +} diff --git a/test/llvm_test_code/taint_analysis/dummy_source_sink/taint_select_02.cpp b/test/llvm_test_code/taint_analysis/dummy_source_sink/taint_select_02.cpp new file mode 100644 index 0000000000..3dc4178921 --- /dev/null +++ b/test/llvm_test_code/taint_analysis/dummy_source_sink/taint_select_02.cpp @@ -0,0 +1,12 @@ +extern int source(); // dummy source +extern void sink(int p); // dummy sink + +int main(int argc, char **argv) { + int a = source(); + int b = 0; + if (argc > 1) { + b = a; + } + sink(b); + return 0; +} diff --git a/test/llvm_test_code/taint_analysis/dummy_source_sink/taint_select_03.cpp b/test/llvm_test_code/taint_analysis/dummy_source_sink/taint_select_03.cpp new file mode 100644 index 0000000000..41fd2eee30 --- /dev/null +++ b/test/llvm_test_code/taint_analysis/dummy_source_sink/taint_select_03.cpp @@ -0,0 +1,10 @@ +extern int source(); // dummy source +extern void sink(int p); // dummy sink + +int main(int argc, char **argv) { + int a = source(); + int b = argc > 1 ? 1 : 2; + sink(b); + sink(a); + return 0; +} diff --git a/unittests/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSTaintAnalysisTest.cpp b/unittests/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSTaintAnalysisTest.cpp index 515d9c3cbc..4778743359 100644 --- a/unittests/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSTaintAnalysisTest.cpp +++ b/unittests/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSTaintAnalysisTest.cpp @@ -157,6 +157,65 @@ TEST_F(IFDSTaintAnalysisTest, TaintTest_06) { compare(TaintProblem->Leaks, GroundTruth); } +// Ternary operator, compiled with -O1: the tainted value reaches the sink +// through a select instruction +TEST_F(IFDSTaintAnalysisTest, TaintTest_Select_01) { + initialize({PathToLlFiles + "dummy_source_sink/taint_select_01_cpp_dbg.ll"}); + IFDSSolver TaintSolver(&*TaintProblem, &HA->getICFG()); + TaintSolver.solve(); + + auto Entry = LineColFun{7, 3, "main"}; + auto EntryTwo = LineColFunOp{6, 11, "main", llvm::Instruction::Select}; + GroundTruthTy GroundTruth{{Entry, {EntryTwo}}}; + + compare(TaintProblem->Leaks, GroundTruth); +} + +// Same ternary operator after mem2reg: the tainted value reaches the sink +// through a phi instruction +TEST_F(IFDSTaintAnalysisTest, TaintTest_Select_01_m2r) { + initialize( + {PathToLlFiles + "dummy_source_sink/taint_select_01_cpp_m2r_dbg.ll"}); + IFDSSolver TaintSolver(&*TaintProblem, &HA->getICFG()); + TaintSolver.solve(); + + auto Entry = LineColFun{7, 3, "main"}; + auto EntryTwo = LineColFunOp{6, 11, "main", llvm::Instruction::PHI}; + GroundTruthTy GroundTruth{{Entry, {EntryTwo}}}; + + compare(TaintProblem->Leaks, GroundTruth); +} + +// Conditional assignment, compiled with -O1: SimplifyCFG turns the if into a +// select instruction +TEST_F(IFDSTaintAnalysisTest, TaintTest_Select_02) { + initialize({PathToLlFiles + "dummy_source_sink/taint_select_02_cpp_dbg.ll"}); + IFDSSolver TaintSolver(&*TaintProblem, &HA->getICFG()); + TaintSolver.solve(); + + auto Entry = LineColFun{10, 3, "main"}; + // Column 0 matches any column: LLVM versions attach the select to either + // the `if` or the condition on line 7 + auto EntryTwo = LineColFunOp{7, 0, "main", llvm::Instruction::Select}; + GroundTruthTy GroundTruth{{Entry, {EntryTwo}}}; + + compare(TaintProblem->Leaks, GroundTruth); +} + +// A select between two constants must not be tainted, even if a tainted value +// exists. Only the direct flow into the second sink is a leak. +TEST_F(IFDSTaintAnalysisTest, TaintTest_Select_03) { + initialize({PathToLlFiles + "dummy_source_sink/taint_select_03_cpp_dbg.ll"}); + IFDSSolver TaintSolver(&*TaintProblem, &HA->getICFG()); + TaintSolver.solve(); + + auto Entry = LineColFun{8, 3, "main"}; + auto EntryTwo = LineColFunOp{5, 11, "main", llvm::Instruction::Call}; + GroundTruthTy GroundTruth{{Entry, {EntryTwo}}}; + + compare(TaintProblem->Leaks, GroundTruth); +} + TEST_F(IFDSTaintAnalysisTest, SRetTest_01) { initialize({PathToLlFiles + "dummy_source_sink/sret_c_dbg.ll"}); IFDSSolver TaintSolver(&*TaintProblem, &HA->getICFG());