Created
October 4, 2017 10:38
-
-
Save astarasikov/15e34d032ba68a9f9c8ef88f3702f34a to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
commit 6887d0432b148b85592538bbff8941d6d5a9ab16 | |
Author: Alexander Tarasikov <[email protected]> | |
Date: Tue Oct 3 14:08:10 2017 +0300 | |
XXX: try assigning func pointers to struct member | |
diff --git a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp | |
index caf86b26b6..32587624bf 100644 | |
--- a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp | |
+++ b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp | |
@@ -23,9 +23,14 @@ | |
#include "llvm/ADT/Statistic.h" | |
#include "llvm/Support/SaveAndRestore.h" | |
+#include <iostream> | |
+ | |
using namespace clang; | |
using namespace ento; | |
+extern Decl *gDeclFunc1; | |
+extern Decl *gDeclFunc2; | |
+ | |
#define DEBUG_TYPE "ExprEngine" | |
STATISTIC(NumOfDynamicDispatchPathSplits, | |
@@ -909,7 +914,55 @@ void ExprEngine::defaultEvalCall(NodeBuilder &Bldr, ExplodedNode *Pred, | |
State = InlinedFailedState; | |
} else { | |
RuntimeDefinition RD = Call->getRuntimeDefinition(); | |
- const Decl *D = RD.getDecl(); | |
+ | |
+ std::cerr << __func__ << ": evaluating call for: "; | |
+ Call->dump(llvm::errs()); | |
+ | |
+ //Here's what I'm trying to do here | |
+ //when performing a call via a function pointer | |
+ //that is a member of a struct, I look up a compatible function | |
+ //declaration and pretend that the pointer was initialized to this value | |
+ //in my toy example, "func1" and "func2" are such functions. | |
+ //that's why we check that it's at index 5 in expression ("foo->func1") | |
+ // | |
+ //What I really want to do is to take the CTU/XTU patch | |
+ //and then perform analysis of Linux Kernel while assigning | |
+ //various structs (file operations, platform device) members to | |
+ //whatever compatible definitions are available. | |
+ //this way, in theory, when analyzing a particular ioctl in a driver | |
+ //we will be able to follow indirect calls via function pointers | |
+ // | |
+ | |
+ std::string S; | |
+ llvm::raw_string_ostream OS(S); | |
+ Call->dump(OS); | |
+ std::string &dumpS = OS.str(); | |
+ std::cerr << __func__ << ": expr: " << dumpS; | |
+ | |
+ std::size_t idx_func1 = dumpS.find("func1"); | |
+ std::size_t idx_func2 = dumpS.find("func2"); | |
+ std::cerr << __func__ << ": idx_func: (" << idx_func1 << "," << idx_func2 << ")" << std::endl; | |
+ | |
+ const Decl *D = RD.getDecl(); | |
+ | |
+ if (idx_func1 == 5) { | |
+ D = gDeclFunc1; | |
+ } | |
+ if (idx_func2 == 5) { | |
+ D = gDeclFunc2; | |
+ } | |
+ | |
+ if (D) { | |
+ std::cerr << __func__ << ": decl is |||"; | |
+ D->dump(llvm::errs()); | |
+ } | |
+ else { | |
+ // if it's a struct member call, we shall look up compatible | |
+ // definitions (possibly in external TUs) | |
+ // and fork the analyzer state for each of them | |
+ std::cerr << "declaration not found" << std::endl; | |
+ } | |
+ | |
if (shouldInlineCall(*Call, D, Pred)) { | |
if (RD.mayHaveOtherDefinitions()) { | |
AnalyzerOptions &Options = getAnalysisManager().options; | |
@@ -917,20 +970,56 @@ void ExprEngine::defaultEvalCall(NodeBuilder &Bldr, ExplodedNode *Pred, | |
// Explore with and without inlining the call. | |
if (Options.getIPAMode() == IPAK_DynamicDispatchBifurcate) { | |
BifurcateCall(RD.getDispatchRegion(), *Call, D, Bldr, Pred); | |
- return; | |
+ goto try_second; | |
+ //return; | |
} | |
// Don't inline if we're not in any dynamic dispatch mode. | |
if (Options.getIPAMode() != IPAK_DynamicDispatch) { | |
conservativeEvalCall(*Call, Bldr, Pred, State); | |
- return; | |
+ goto try_second; | |
+ //return; | |
} | |
} | |
// We are not bifurcating and we do have a Decl, so just inline. | |
if (inlineCall(*Call, D, Bldr, Pred, State)) | |
- return; | |
+ goto try_second; | |
+ //return; | |
} | |
+ | |
+ try_second: | |
+ if (idx_func1 == 5 && gDeclFunc2) { | |
+ //evaluate the same but for a different function | |
+ //if we're analyzing "func1", also check as if | |
+ //struct member was assigned to "func2" | |
+ | |
+ D = gDeclFunc2; | |
+ | |
+ if (shouldInlineCall(*Call, D, Pred)) { | |
+ if (RD.mayHaveOtherDefinitions()) { | |
+ AnalyzerOptions &Options = getAnalysisManager().options; | |
+ | |
+ // Explore with and without inlining the call. | |
+ if (Options.getIPAMode() == IPAK_DynamicDispatchBifurcate) { | |
+ BifurcateCall(RD.getDispatchRegion(), *Call, D, Bldr, Pred); | |
+ return; | |
+ } | |
+ | |
+ // Don't inline if we're not in any dynamic dispatch mode. | |
+ if (Options.getIPAMode() != IPAK_DynamicDispatch) { | |
+ conservativeEvalCall(*Call, Bldr, Pred, State); | |
+ return; | |
+ } | |
+ } | |
+ | |
+ // We are not bifurcating and we do have a Decl, so just inline. | |
+ if (inlineCall(*Call, D, Bldr, Pred, State)) | |
+ return; | |
+ } | |
+ } | |
+ return; | |
+ | |
} | |
// If we can't inline it, handle the return value and invalidate the regions. | |
diff --git a/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp b/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp | |
index c47edc7d21..0bb767418a 100644 | |
--- a/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp | |
+++ b/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp | |
@@ -44,6 +44,8 @@ | |
#include <queue> | |
#include <utility> | |
+#include <iostream> | |
+ | |
using namespace clang; | |
using namespace ento; | |
@@ -60,6 +62,9 @@ STATISTIC(NumBlocksInAnalyzedFunctions, | |
STATISTIC(PercentReachableBlocks, "The % of reachable basic blocks."); | |
STATISTIC(MaxCFGSize, "The maximum number of basic blocks in a function."); | |
+Decl *gDeclFunc1; | |
+Decl *gDeclFunc2; | |
+ | |
//===----------------------------------------------------------------------===// | |
// Special PathDiagnosticConsumers. | |
//===----------------------------------------------------------------------===// | |
@@ -464,6 +469,20 @@ void AnalysisConsumer::HandleDeclsCallGraph(const unsigned LocalTUDeclsSize) { | |
// We rely on random access to add the initially processed Decls to CG. | |
CallGraph CG; | |
for (unsigned i = 0 ; i < LocalTUDeclsSize ; ++i) { | |
+ std::cerr << __func__ | |
+ << ": local decl <" | |
+ << getFunctionName(LocalTUDecls[i]) | |
+ << ">" | |
+ << std::endl; | |
+ Decl *D = LocalTUDecls[i]; | |
+ | |
+ if ("func2" == getFunctionName(D)) { | |
+ gDeclFunc2 = D; | |
+ } | |
+ if ("func1" == getFunctionName(D)) { | |
+ gDeclFunc1 = D; | |
+ } | |
+ | |
CG.addToCallGraph(LocalTUDecls[i]); | |
} | |
@@ -482,15 +501,24 @@ void AnalysisConsumer::HandleDeclsCallGraph(const unsigned LocalTUDeclsSize) { | |
CallGraphNode *N = *I; | |
Decl *D = N->getDecl(); | |
- | |
+ | |
// Skip the abstract root node. | |
if (!D) | |
continue; | |
+ | |
// Skip the functions which have been processed already or previously | |
// inlined. | |
- if (shouldSkipFunction(D, Visited, VisitedAsTopLevel)) | |
- continue; | |
+ bool skip = shouldSkipFunction(D, Visited, VisitedAsTopLevel); | |
+ std::cerr << __func__ | |
+ << ": " << (skip ? "skipping" : "adding") | |
+ << " decl <" | |
+ << getFunctionName(D) | |
+ << ">" | |
+ << std::endl; | |
+ | |
+ if (skip) | |
+ continue; | |
// Analyze the function. | |
SetOfConstDecls VisitedCallees; | |
diff --git a/lib/StaticAnalyzer/test_clang_fptr.c b/lib/StaticAnalyzer/test_clang_fptr.c | |
new file mode 100644 | |
index 0000000000..fba7d8bebf | |
--- /dev/null | |
+++ b/lib/StaticAnalyzer/test_clang_fptr.c | |
@@ -0,0 +1,69 @@ | |
+//scan-build clang -c -o /tmp/foo.o test_clang_fptr.c | |
+ | |
+#include <stdio.h> | |
+#include <stdlib.h> | |
+#include <string.h> | |
+ | |
+typedef int (ftype)(void *arg); | |
+ | |
+struct foo { | |
+ ftype *func1; | |
+ ftype *func2; | |
+}; | |
+ | |
+int func1(void *arg) | |
+{ | |
+ printf("%s: arg=%p\n", __func__, arg); | |
+ *(int*)arg = 0; | |
+ return 1; | |
+} | |
+ | |
+int func2(void *arg) | |
+{ | |
+ printf("%s: arg=%p\n", __func__, arg); | |
+ //if (arg) { | |
+ *(int*)arg = 42; | |
+ //} | |
+ return 2; | |
+} | |
+ | |
+struct foo *mkFoo(void) | |
+{ | |
+ struct foo *foo = (struct foo*)malloc(sizeof(struct foo)); | |
+ memset(foo, 0, sizeof(*foo)); | |
+ | |
+ if (rand() % 2) { | |
+ foo->func1 = func1; | |
+ } | |
+ else { | |
+ foo->func2 = func2; | |
+ } | |
+ return foo; | |
+} | |
+ | |
+void testFoo(struct foo *foo) { | |
+ if (foo->func1) { | |
+ foo->func1(NULL); | |
+ } | |
+ if (foo->func2) { | |
+ foo->func2(NULL); | |
+ } | |
+} | |
+ | |
+extern struct foo *mkFooExt(); | |
+ | |
+void foofinate(void) | |
+{ | |
+ //the definition of mkFooExt is not available! | |
+ //but due to our hack, the analyzer will assume | |
+ //that in "testFoo" the pointer "foo->func1" can point to | |
+ //either "func1" or "func2" | |
+ struct foo *foo = mkFooExt(); | |
+ testFoo(foo); | |
+} | |
+ | |
+int main() | |
+{ | |
+ foofinate(); | |
+ return 0; | |
+} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment