Skip to content

Instantly share code, notes, and snippets.

@astarasikov
Created October 4, 2017 10:38
Show Gist options
  • Save astarasikov/15e34d032ba68a9f9c8ef88f3702f34a to your computer and use it in GitHub Desktop.
Save astarasikov/15e34d032ba68a9f9c8ef88f3702f34a to your computer and use it in GitHub Desktop.
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