-
-
Save zeroomega/0a2abb371d85ff0444a8a9562d119b80 to your computer and use it in GitHub Desktop.
This file contains 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
diff --git a/include/clang/StaticAnalyzer/Checkers/Checkers.td b/include/clang/StaticAnalyzer/Checkers/Checkers.td | |
index 4171c685cb..40eff81e12 100644 | |
--- a/include/clang/StaticAnalyzer/Checkers/Checkers.td | |
+++ b/include/clang/StaticAnalyzer/Checkers/Checkers.td | |
@@ -64,6 +64,7 @@ def CStringAlpha : Package<"cstring">, InPackage<UnixAlpha>, Hidden; | |
def OSX : Package<"osx">; | |
def OSXAlpha : Package<"osx">, InPackage<Alpha>, Hidden; | |
def OSXOptIn : Package<"osx">, InPackage<OptIn>; | |
+def Magenta : Package<"magenta">, InPackage<Alpha>; | |
def Cocoa : Package<"cocoa">, InPackage<OSX>; | |
def CocoaAlpha : Package<"cocoa">, InPackage<OSXAlpha>, Hidden; | |
@@ -754,3 +755,14 @@ def CloneChecker : Checker<"CloneChecker">, | |
} // end "clone" | |
+//===----------------------------------------------------------------------===// | |
+// Magenta Specified Checkers | |
+//===----------------------------------------------------------------------===// | |
+ | |
+let ParentPackage = Magenta in { | |
+ | |
+def MagentaHandleChecker : Checker<"MagentaHandleChecker">, | |
+ HelpText<"A Checker that detect leaks related to Magenta handles">, | |
+ DescFile<"MagentaHandleChecker.cpp">; | |
+ | |
+} // end "magenta" | |
diff --git a/lib/StaticAnalyzer/Checkers/CMakeLists.txt b/lib/StaticAnalyzer/Checkers/CMakeLists.txt | |
index 2759240dd2..28c5fc7fda 100644 | |
--- a/lib/StaticAnalyzer/Checkers/CMakeLists.txt | |
+++ b/lib/StaticAnalyzer/Checkers/CMakeLists.txt | |
@@ -45,6 +45,7 @@ add_clang_library(clangStaticAnalyzerCheckers | |
LocalizationChecker.cpp | |
MacOSKeychainAPIChecker.cpp | |
MacOSXAPIChecker.cpp | |
+ MagentaHandleChecker.cpp | |
MallocChecker.cpp | |
MallocOverflowSecurityChecker.cpp | |
MallocSizeofChecker.cpp | |
diff --git a/lib/StaticAnalyzer/Checkers/MagentaHandleChecker.cpp b/lib/StaticAnalyzer/Checkers/MagentaHandleChecker.cpp | |
new file mode 100644 | |
index 0000000000..349be7e70b | |
--- /dev/null | |
+++ b/lib/StaticAnalyzer/Checkers/MagentaHandleChecker.cpp | |
@@ -0,0 +1,1030 @@ | |
+//== MagentaHandleChecker.cpp - Magenta Handle Checker------------*- C++-*--==// | |
+// | |
+// The LLVM Compiler Infrastructure | |
+// | |
+// This file is distributed under the University of Illinois Open Source | |
+// License. See LICENSE.TXT for details. | |
+// | |
+//===----------------------------------------------------------------------===// | |
+// | |
+// This checker checks if the handle of magenta kernel is properly used. | |
+// - If a handle is allocated, it should be closed/released before execution | |
+// ends. | |
+// - If a handle is closed/released, it should not be closed/released again. | |
+// - If a handle is closed/released, it should not be used for other purposes | |
+// such as I/O. | |
+// | |
+//===----------------------------------------------------------------------===// | |
+ | |
+#define DEBUG_FLAG_CLEAN | |
+#include "ClangSACheckers.h" | |
+#include "clang/AST/Attr.h" | |
+#include "clang/AST/ParentMap.h" | |
+#include "clang/Basic/SourceManager.h" | |
+#include "clang/Basic/TargetInfo.h" | |
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" | |
+#include "clang/StaticAnalyzer/Core/Checker.h" | |
+#include "clang/StaticAnalyzer/Core/CheckerManager.h" | |
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" | |
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" | |
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" | |
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" | |
+#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" | |
+#include "llvm/ADT/STLExtras.h" | |
+#include "llvm/ADT/SmallString.h" | |
+#include "llvm/ADT/StringExtras.h" | |
+#include <climits> | |
+#include <stdio.h> | |
+#include <string> | |
+#include <utility> | |
+ | |
+using namespace clang; | |
+using namespace ento; | |
+ | |
+namespace { | |
+ | |
+enum CallKind { | |
+ // Will add more calls in the future. For now only worried about the ones | |
+ // related to channel creation. | |
+ MX_CHANNEL_CREATE, | |
+ MX_CHANNEL_READ, | |
+ MX_CHANNEL_WRITE, | |
+ MX_HANDLE_CLOSE, | |
+ UNRELATED_CALL | |
+}; | |
+ | |
+struct HandleState { | |
+private: | |
+ enum Kind { | |
+ // Handle is allocated | |
+ Allocated, | |
+ // Handle is released | |
+ Released, | |
+ // Handle is no longer trackable | |
+ Escaped | |
+ } K; | |
+ | |
+ HandleState(Kind k) : K(k) {} | |
+ | |
+public: | |
+ bool operator==(const HandleState &X) const { return K == X.K; } | |
+ bool isAllocated() const { return K == Allocated; } | |
+ bool isReleased() const { return K == Released; } | |
+ bool isEscaped() const { return K == Escaped; } | |
+ | |
+ static HandleState getAllocated() { return HandleState(Allocated); } | |
+ | |
+ static HandleState getReleased() { return HandleState(Released); } | |
+ | |
+ static HandleState getEscaped() { return HandleState(Escaped); } | |
+ | |
+ void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(K); } | |
+ | |
+ void dump(raw_ostream &OS) const { | |
+ switch (static_cast<Kind>(K)) { | |
+#define CASE(ID) case ID: OS << #ID; break; | |
+ CASE(Allocated) | |
+ CASE(Released) | |
+ CASE(Escaped) | |
+ } | |
+ } | |
+ LLVM_DUMP_METHOD void dump() const { dump(llvm::errs()); } | |
+}; | |
+ | |
+class MagentaHandleChecker : public Checker<check::DeadSymbols, check::PreCall, | |
+ check::PreStmt<ReturnStmt>, | |
+ check::PointerEscape, eval::Call> { | |
+ std::unique_ptr<BugType> LeakBugType; | |
+ std::unique_ptr<BugType> DoubleReleaseBugType; | |
+ std::unique_ptr<BugType> UseAfterFreeBugType; | |
+ mutable llvm::StringMap<StringRef> FUNC_SIGNATURE_MAP; | |
+ | |
+public: | |
+ MagentaHandleChecker(); | |
+ // Checker callbacks | |
+ void checkPreCall(const CallEvent &Call, CheckerContext &Ctx) const; | |
+ | |
+ void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &Ctx) const; | |
+ | |
+ void checkPreStmt(const ReturnStmt *RS, CheckerContext &Ctx) const; | |
+ | |
+ bool evalCall(const CallExpr *CE, CheckerContext &Ctx) const; | |
+ | |
+ ProgramStateRef checkPointerEscape(ProgramStateRef State, | |
+ const InvalidatedSymbols &Escaped, | |
+ const CallEvent *Call, | |
+ PointerEscapeKind Kind) const; | |
+ | |
+private: | |
+ bool processMxChannelRead(const CallExpr *CE, CheckerContext &C) const; | |
+ | |
+ bool processMxChannelCreate(const CallExpr *CE, CheckerContext &C) const; | |
+ | |
+ bool processMxChannelWrite(const CallExpr *CE, CheckerContext &C) const; | |
+ | |
+ void processMxHandleClose(ProgramStateRef State, const CallEvent &Call, | |
+ CheckerContext &Ctx) const; | |
+ | |
+ void processOtherCalls(ProgramStateRef State, const CallEvent &Call, | |
+ CheckerContext &Ctx) const; | |
+ | |
+ // Bug report functions | |
+ void reportLeaks(ArrayRef<SymbolRef> LeakedHandles, CheckerContext &Ctx, | |
+ ExplodedNode *ErrorNode) const; | |
+ | |
+ void reportDoubleRelease(SymbolRef HandleSym, const SourceRange &Range, | |
+ CheckerContext &Ctx) const; | |
+ | |
+ void reportUseAfterFree(SymbolRef HandleSym, const SourceRange &Range, | |
+ CheckerContext &Ctx) const; | |
+ // Utility functions | |
+ CallKind CheckCallSignature(const CallEvent &Call) const; | |
+ | |
+ CallKind CheckCallSignature(const FunctionDecl *FuncDecl) const; | |
+ | |
+ bool CheckTypeSignature(StringRef &TypeSig, QualType &RetType, | |
+ ArrayRef<ParmVarDecl *> &ParmDeclArray) const; | |
+ | |
+ bool CheckExprIsZero(const Expr *ArgExpr, CheckerContext &Ctx) const; | |
+ | |
+ // Return true only if it is certain that Sym will be Zero | |
+ static bool CheckSymbolConstraintToZero(SymbolRef Sym, ProgramStateRef State); | |
+ | |
+ // Return true only if it is certain that Sym will not be Zero | |
+ static bool CheckSymbolConstraintToNotZero(SymbolRef Sym, | |
+ ProgramStateRef State); | |
+ | |
+ bool isLeaked(SymbolRef SymHandle, const HandleState &CurHandleState, | |
+ bool isSymDead, ProgramStateRef State) const; | |
+ #ifdef DEBUG_FLAG_CLEAN | |
+ static void dumpSymbol(SymbolRef symRef) { | |
+ if (symRef) { | |
+ char buf[256]; | |
+ sprintf(buf, "%p", (void *)symRef); | |
+ symRef->dumpToStream(llvm::errs()); | |
+ llvm::errs()<<" Addr:"<<buf<<'\n'; | |
+ } | |
+ else { | |
+ llvm::errs()<<"SymbolRef is null\n"; | |
+ } | |
+ } | |
+ #endif | |
+}; | |
+ | |
+class MagentaBugVisitor : public BugReporterVisitorImpl<MagentaBugVisitor> { | |
+private: | |
+ SymbolRef Sym; | |
+ | |
+public: | |
+ MagentaBugVisitor(SymbolRef sym): Sym(sym){} | |
+ void Profile(llvm::FoldingSetNodeID &ID) const { | |
+ ID.AddPointer(Sym); | |
+ } | |
+ std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, | |
+ const ExplodedNode *PrevN, | |
+ BugReporterContext &BRC, | |
+ BugReport &BR) { | |
+ const Stmt *S = PathDiagnosticLocation::getStmt(N); | |
+ if (!S) | |
+ return nullptr; | |
+ PathDiagnosticLocation Pos(S, BRC.getSourceManager(), | |
+ N->getLocationContext()); | |
+ return std::make_shared<PathDiagnosticEventPiece>(Pos, "Here"); | |
+ } | |
+}; | |
+ | |
+} // end anonymous namespace | |
+ | |
+// Handle -> HandleState map | |
+REGISTER_MAP_WITH_PROGRAMSTATE(HStateMap, SymbolRef, HandleState) | |
+ | |
+MagentaHandleChecker::MagentaHandleChecker() { | |
+ // Initialize the bug types. | |
+ LeakBugType.reset( | |
+ new BugType(this, "MX handle leak", "Magenta Handle Error")); | |
+ DoubleReleaseBugType.reset( | |
+ new BugType(this, "MX handle double release", "Magenta Handle Error")); | |
+ UseAfterFreeBugType.reset( | |
+ new BugType(this, "MX handle use after free", "Magenta Handle Error")); | |
+ | |
+ FUNC_SIGNATURE_MAP = { | |
+ {"mx_channel_create", | |
+ "mx_status_t,uint32_t,mx_handle_t *,mx_handle_t *,"}, | |
+ {"mx_channel_read", | |
+ "mx_status_t,mx_handle_t,uint32_t,void *,mx_handle_t *," | |
+ "uint32_t,uint32_t,uint32_t *,uint32_t *,"}, | |
+ {"mx_handle_close", "mx_status_t,mx_handle_t,"}, | |
+ {"mx_channel_write", "mx_status_t,mx_handle_t,uint32_t,const void *,uint32_t," | |
+ "const mx_handle_t *,uint32_t,"}}; | |
+} | |
+ | |
+CallKind | |
+MagentaHandleChecker::CheckCallSignature(const FunctionDecl *FuncDecl) const { | |
+ if (!FuncDecl) | |
+ return UNRELATED_CALL; | |
+ | |
+ DeclarationName DeclName = FuncDecl->getDeclName(); | |
+ if (!DeclName.isIdentifier()) | |
+ return UNRELATED_CALL; | |
+ | |
+ // Use function name and type signatures to match target function calls | |
+ StringRef FuncName = FuncDecl->getName(); | |
+ if (FUNC_SIGNATURE_MAP.count(FuncName) != 1) | |
+ // The signature of this function is not available in the | |
+ // map, unrelated. | |
+ return UNRELATED_CALL; | |
+ | |
+ // Get function type signature from map | |
+ StringRef TypeSig = FUNC_SIGNATURE_MAP[FuncName]; | |
+ // Get function type info from FuncDecl | |
+ QualType RetType = FuncDecl->getReturnType(); | |
+ ArrayRef<ParmVarDecl *> ParmDeclArray = FuncDecl->parameters(); | |
+ if (!CheckTypeSignature(TypeSig, RetType, ParmDeclArray)) | |
+ // Type signature not match | |
+ return UNRELATED_CALL; | |
+ | |
+ // Type signature check passed | |
+ // Check function name | |
+ // May changed to map instead of if stmt in the future. | |
+ if (FuncName.equals("mx_channel_create")) | |
+ return MX_CHANNEL_CREATE; | |
+ if (FuncName.equals("mx_channel_read")) | |
+ return MX_CHANNEL_READ; | |
+ if (FuncName.equals("mx_channel_write")) | |
+ return MX_CHANNEL_WRITE; | |
+ if (FuncName.equals("mx_handle_close")) | |
+ return MX_HANDLE_CLOSE; | |
+ | |
+ return UNRELATED_CALL; | |
+} | |
+ | |
+CallKind MagentaHandleChecker::CheckCallSignature(const CallEvent &Call) const { | |
+ if (const AnyFunctionCall *FuncCall = dyn_cast<AnyFunctionCall>(&Call)) { | |
+ const FunctionDecl *FuncDecl = FuncCall->getDecl(); | |
+ if (!FuncDecl) | |
+ return UNRELATED_CALL; | |
+ | |
+ return CheckCallSignature(FuncDecl); | |
+ } | |
+ return UNRELATED_CALL; | |
+} | |
+ | |
+bool MagentaHandleChecker::isLeaked(SymbolRef SymHandle, | |
+ const HandleState &CurHandleState, | |
+ bool isSymDead, | |
+ ProgramStateRef State) const { | |
+ if (isSymDead && CurHandleState.isAllocated()) { | |
+ // Check if handle value is MX_HANDLE_INVALID. If so, not a leak. | |
+ if (CheckSymbolConstraintToZero(SymHandle, State)) | |
+ return false; | |
+ | |
+ // Handle is allocated and dead. Leaked | |
+ return true; | |
+ } | |
+ // Handle not dead. Not a leak. | |
+ return false; | |
+} | |
+ | |
+bool MagentaHandleChecker::CheckSymbolConstraintToZero(SymbolRef Sym, | |
+ ProgramStateRef State) { | |
+ ConstraintManager &CMgr = State->getConstraintManager(); | |
+ ConditionTruthVal IsZero = CMgr.isNull(State, Sym); | |
+ if (IsZero.isConstrained()) { | |
+ // The value can be compared to zero | |
+ if (IsZero.isConstrainedTrue()) | |
+ return true; | |
+ } | |
+ return false; | |
+} | |
+ | |
+bool MagentaHandleChecker::CheckSymbolConstraintToNotZero( | |
+ SymbolRef Sym, ProgramStateRef State) { | |
+ ConstraintManager &CMgr = State->getConstraintManager(); | |
+ ConditionTruthVal IsZero = CMgr.isNull(State, Sym); | |
+ if (IsZero.isConstrained()) { | |
+ // The value can be compared to zero | |
+ if (IsZero.isConstrainedFalse()) | |
+ return true; | |
+ } | |
+ return false; | |
+} | |
+ | |
+bool MagentaHandleChecker::CheckTypeSignature( | |
+ StringRef &TypeSig, QualType &RetType, | |
+ ArrayRef<ParmVarDecl *> &ParmDeclArray) const { | |
+ size_t SigStrId = 0; | |
+ size_t SigStrIdEnd = TypeSig.size() - 1; | |
+ // Check return type | |
+ StringRef RetTypeStr = RetType.getAsString(); | |
+ // Check if signature will overflow for this comparison. | |
+ if (SigStrId + RetTypeStr.size() - 1 > SigStrIdEnd) | |
+ return false; | |
+ | |
+ if (TypeSig.find(RetTypeStr, SigStrId) != SigStrId) | |
+ return false; | |
+ | |
+ SigStrId += RetTypeStr.size() + 1; | |
+ // Check Param Types | |
+ for (ParmVarDecl *ParmDecl : ParmDeclArray) { | |
+ QualType ParmType = ParmDecl->getOriginalType(); | |
+ StringRef ParmTypeString = ParmType.getAsString(); | |
+ // Check if signature will overflow for this comparison. | |
+ if (SigStrId + ParmTypeString.size() - 1 > SigStrIdEnd) | |
+ return false; | |
+ | |
+ if (TypeSig.find(ParmTypeString, SigStrId) != SigStrId) | |
+ return false; | |
+ | |
+ SigStrId += ParmTypeString.size() + 1; | |
+ } | |
+ if (SigStrId == SigStrIdEnd + 1) | |
+ return true; | |
+ | |
+ return false; | |
+} | |
+ | |
+ProgramStateRef MagentaHandleChecker::checkPointerEscape( | |
+ ProgramStateRef State, const InvalidatedSymbols &Escaped, | |
+ const CallEvent *Call, PointerEscapeKind Kind) const { | |
+ | |
+ HStateMapTy TrackedHandle = State->get<HStateMap>(); | |
+ SmallVector<SymbolRef, 2> EscapedSymbolRef; | |
+ for (auto &CurItem : TrackedHandle) { | |
+ SymbolRef Sym = CurItem.first; | |
+ if (Escaped.count(Sym) == 1) { | |
+ EscapedSymbolRef.push_back(Sym); | |
+ } | |
+ const SymbolDerived *ElementSym = dyn_cast<SymbolDerived>(Sym); | |
+ if (!ElementSym) { | |
+ continue; | |
+ } | |
+ const SymbolRef ParentSymbol = ElementSym->getParentSymbol(); | |
+ if (Escaped.count(ParentSymbol) == 1) { | |
+ EscapedSymbolRef.push_back(Sym); | |
+ } | |
+ } | |
+ | |
+ for (SymbolRef EscapedHandle : EscapedSymbolRef) { | |
+ State = State->remove<HStateMap>(EscapedHandle); | |
+ State = State->set<HStateMap>(EscapedHandle, HandleState::getEscaped()); | |
+ } | |
+ | |
+ // Currently only handles passed through pointer/arrays are considered in | |
+ // this callback. There are some cases that the values of handles are wrapped | |
+ // in multiple layers of struct. Current implementation may failed to treat | |
+ // these handles as escaped, which causes false positives. | |
+ // TODO: Improve handle escape detection in the future. | |
+ | |
+ return State; | |
+} | |
+ | |
+bool MagentaHandleChecker::CheckExprIsZero(const Expr *ArgExpr, | |
+ CheckerContext &Ctx) const { | |
+ ProgramStateRef State = Ctx.getState(); | |
+ const LocationContext *LCtx = Ctx.getLocationContext(); | |
+ SVal ExprSVal = State->getSVal(ArgExpr, LCtx); | |
+ int64_t ExtVal; | |
+ if (ExprSVal.getBaseKind() == SVal::NonLocKind && | |
+ ExprSVal.getSubKind() == nonloc::ConcreteIntKind) { | |
+ ExtVal = ExprSVal.castAs<nonloc::ConcreteInt>().getValue().getExtValue(); | |
+ } else if (ExprSVal.getBaseKind() == SVal::LocKind && | |
+ ExprSVal.getSubKind() == loc::ConcreteIntKind) { | |
+ ExtVal = ExprSVal.castAs<loc::ConcreteInt>().getValue().getExtValue(); | |
+ } else { | |
+ return false; | |
+ } | |
+ if (ExtVal == 0) | |
+ return true; | |
+ | |
+ return false; | |
+} | |
+ | |
+void MagentaHandleChecker::checkPreCall(const CallEvent &Call, | |
+ CheckerContext &Ctx) const { | |
+ ProgramStateRef State = Ctx.getState(); | |
+ | |
+ switch (CheckCallSignature(Call)) { | |
+ case MX_HANDLE_CLOSE: | |
+ processMxHandleClose(State, Call, Ctx); | |
+ break; | |
+ case UNRELATED_CALL: | |
+ processOtherCalls(State, Call, Ctx); | |
+ break; | |
+ default: | |
+ // Ignore other handle release call for now | |
+ break; | |
+ } | |
+} | |
+ | |
+void MagentaHandleChecker::checkDeadSymbols(SymbolReaper &SymReaper, | |
+ CheckerContext &Ctx) const { | |
+ ProgramStateRef State = Ctx.getState(); | |
+ SmallVector<SymbolRef, 2> LeakVector; | |
+ HStateMapTy TrackedHandle = State->get<HStateMap>(); | |
+ for (auto &CurItem : TrackedHandle) { | |
+ SymbolRef Sym = CurItem.first; | |
+ // Workaround for zombie symbol issue. | |
+ bool IsSymDead = SymReaper.maybeDead(Sym); | |
+ const HandleState &CurHandleState = CurItem.second; | |
+ if (isLeaked(Sym, CurHandleState, IsSymDead, State)) { | |
+ LeakVector.push_back(Sym); | |
+ #ifdef DEBUG_FLAG_CLEAN | |
+ llvm::errs()<<"Leaked Symbol: "; | |
+ dumpSymbol(Sym); | |
+ #endif | |
+ } | |
+ if (IsSymDead) { | |
+ State = State->remove<HStateMap>(Sym); | |
+ } | |
+ #ifdef DEBUG_FLAG_CLEAN | |
+ if (CurHandleState.isReleased() && IsSymDead) { | |
+ llvm::errs()<<"Symbol properly released: "; | |
+ dumpSymbol(Sym); | |
+ } | |
+ #endif | |
+ } | |
+ | |
+ if (!LeakVector.empty()) { | |
+ ExplodedNode *N = Ctx.generateNonFatalErrorNode(State); | |
+ if (!N) { | |
+ return; | |
+ } | |
+ reportLeaks(LeakVector, Ctx, N); | |
+ } else { | |
+ Ctx.addTransition(State); | |
+ } | |
+} | |
+ | |
+void MagentaHandleChecker::checkPreStmt(const ReturnStmt *RS, | |
+ CheckerContext &Ctx) const { | |
+ ProgramStateRef State = Ctx.getState(); | |
+ const auto *LCtx = Ctx.getLocationContext(); | |
+ if (!LCtx) | |
+ return; | |
+ | |
+ const auto *SFCtx = LCtx->getCurrentStackFrame(); | |
+ if (!SFCtx) | |
+ return; | |
+ | |
+ const Expr *RetE = RS->getRetValue(); | |
+ if (!RetE) | |
+ return; | |
+ | |
+ SVal SValRet = State->getSVal(RetE, LCtx); | |
+ SymbolRef SymRet = SValRet.getAsSymbol(); | |
+ if (!SymRet) | |
+ return; | |
+ | |
+ const HandleState *SymState = State->get<HStateMap>(SymRet); | |
+ // Check if value returned in this ReturnStmt is tracked by analyzer | |
+ if (!SymState) | |
+ return; | |
+ | |
+ if (SymState->isReleased()) { | |
+ // Use after release bug. | |
+ ExplodedNode *N = Ctx.generateErrorNode(State); | |
+ if (!N) | |
+ return; | |
+ | |
+ reportUseAfterFree(SymRet, RS->getSourceRange(), Ctx); | |
+ return; | |
+ } | |
+ State = State->set<HStateMap>(SymRet, HandleState::getEscaped()); | |
+ Ctx.addTransition(State); | |
+} | |
+ | |
+bool MagentaHandleChecker::evalCall(const CallExpr *CE, | |
+ CheckerContext &C) const { | |
+ const FunctionDecl *FuncDecl = C.getCalleeDecl(CE); | |
+ switch (CheckCallSignature(FuncDecl)) { | |
+ case MX_CHANNEL_READ: | |
+ return processMxChannelRead(CE, C); | |
+ case MX_CHANNEL_CREATE: | |
+ return processMxChannelCreate(CE, C); | |
+ case MX_CHANNEL_WRITE: | |
+ return processMxChannelWrite(CE, C); | |
+ default: | |
+ break; | |
+ } | |
+ return false; | |
+} | |
+ | |
+void MagentaHandleChecker::processOtherCalls(ProgramStateRef State, | |
+ const CallEvent &Call, | |
+ CheckerContext &Ctx) const { | |
+ // Process the handle escaped situation. | |
+ // When a function is outside of current translation unit, if one of its | |
+ // arguments is an acquired handle, treat it as an escaped handle. | |
+ // This is just a naive approach to reduce the false positive rate, should | |
+ // be refined later, e.g. through annotation | |
+ // TODO: Use annotation to make it more accurate. | |
+ const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl()); | |
+ if (!FD) | |
+ return; | |
+ | |
+ // Check if function has a body. If not, assume the handle passed in is | |
+ // escaped | |
+ if (FD->isDefined()) | |
+ return; | |
+ | |
+ unsigned ArgsCount = Call.getNumArgs(); | |
+ bool StateChanged = false; | |
+ if (!ArgsCount) | |
+ // No argument passed to this call, ignore it | |
+ return; | |
+ #ifdef DEBUG_FLAG_CLEAN | |
+ // DeclarationName DeclName = FD->getDeclName(); | |
+ // if (DeclName.isIdentifier()) { | |
+ // StringRef FuncName = FD->getName(); | |
+ // llvm::errs()<<"Escape check on function: "<<FuncName<<"\n"; | |
+ // } | |
+ SourceManager & SMgr = Ctx.getSourceManager(); | |
+ const Expr * E = Call.getOriginExpr(); | |
+ if (isa<CallExpr>(E)) { | |
+ const CallExpr * CE = dyn_cast<CallExpr>(E); | |
+ SourceLocation SL = CE->getLocStart(); | |
+ SL.print(llvm::errs(), SMgr); | |
+ } | |
+ #endif | |
+ for (unsigned i = 0; i < ArgsCount; i++) { | |
+ SVal ArgSVal = Call.getArgSVal(i); | |
+ SymbolRef ArgSym = ArgSVal.getAsSymbol(); | |
+ // Check if the argument is a pointer. If so, it should have been handled | |
+ // by checkPointerEscape, can be ignored here. | |
+ if (!ArgSym) | |
+ continue; | |
+ | |
+ const HandleState *CurHandleState = State->get<HStateMap>(ArgSym); | |
+ // Check if the handle is tracked by analyzer. If not, we ignore it here. | |
+ if (!CurHandleState) | |
+ | |
+ continue; | |
+ | |
+ if (CurHandleState->isReleased()) | |
+ // Use after free | |
+ reportUseAfterFree(ArgSym, Call.getSourceRange(), Ctx); | |
+ #ifdef DEBUG_FLAG_CLEAN | |
+ llvm::errs()<<" handle escaped "; | |
+ dumpSymbol(ArgSym); | |
+ #endif | |
+ State = State->set<HStateMap>(ArgSym, HandleState::getEscaped()); | |
+ StateChanged = true; | |
+ } | |
+ if (StateChanged) | |
+ Ctx.addTransition(State); | |
+} | |
+ | |
+bool MagentaHandleChecker::processMxChannelRead(const CallExpr *CE, | |
+ CheckerContext &Ctx) const { | |
+ #ifdef DEBUG_FLAG_CLEAN | |
+ SourceLocation SL = CE->getLocStart(); | |
+ SourceManager & SMgr = Ctx.getSourceManager(); | |
+ SL.print(llvm::errs(), SMgr); | |
+ llvm::errs()<<" mx_channel_read encountered "; | |
+ #endif | |
+ ProgramStateRef State = Ctx.getState(); | |
+ const LocationContext *LCtx = Ctx.getLocationContext(); | |
+ ProgramStateManager &Mgr = State->getStateManager(); | |
+ ASTContext &ASTCtx = Mgr.getContext(); | |
+ SValBuilder &svalBuilder = Ctx.getSValBuilder(); | |
+ MemRegionManager &memRegionMgr = svalBuilder.getRegionManager(); | |
+ | |
+ const Expr *HandleBufExpr = CE->getArg(3); | |
+ const Expr *InputSizeExpr = CE->getArg(5); | |
+ const Expr *OutputSizeExpr = CE->getArg(7); | |
+ if (CheckExprIsZero(HandleBufExpr, Ctx) || | |
+ CheckExprIsZero(InputSizeExpr, Ctx) || | |
+ CheckExprIsZero(OutputSizeExpr, Ctx)) | |
+ return false; | |
+ | |
+ // Conjure a failed state which does not allocate any handle | |
+ DefinedOrUnknownSVal FailedStatusSVal = | |
+ svalBuilder.conjureSymbolVal(nullptr, CE, LCtx, Ctx.blockCount()); | |
+ ProgramStateRef FailedState = State->BindExpr(CE, LCtx, FailedStatusSVal); | |
+ FailedState = FailedState->assumeInclusiveRange( | |
+ FailedStatusSVal, llvm::APSInt::get(-2147483647), llvm::APSInt::get(-1), | |
+ true); | |
+ | |
+ // Conjure a successful state | |
+ SmallVector<SVal, 2> InvalidateSVal; | |
+ InvalidateSVal.push_back(State->getSVal(HandleBufExpr, LCtx)); | |
+ InvalidateSVal.push_back(State->getSVal(OutputSizeExpr, LCtx)); | |
+ State = State->invalidateRegions(InvalidateSVal, CE, Ctx.blockCount(), LCtx, | |
+ false, nullptr, nullptr, nullptr); | |
+ | |
+ SVal StatusSVal = Ctx.getSValBuilder().makeIntVal(llvm::APSInt::get(0)); | |
+ State = State->BindExpr(CE, LCtx, StatusSVal); | |
+ | |
+ const MemRegion *MemHandleBuf = | |
+ State->getSVal(HandleBufExpr, LCtx).getAsRegion(); | |
+ if (!MemHandleBuf) | |
+ return false; | |
+ | |
+ // The handle buffer is not an array or a pointer to a memory space | |
+ // It might be a pointer to a local variable. Currently ignore it | |
+ // TODO: Handle the case when the handle buffer is a single mx_handle_t | |
+ // variable | |
+ if (!isa<ElementRegion>(MemHandleBuf)) | |
+ return false; | |
+ | |
+ const SubRegion *SuperRegionSub = dyn_cast<SubRegion>( | |
+ dyn_cast<ElementRegion>(MemHandleBuf)->getSuperRegion()); | |
+ if (!SuperRegionSub) | |
+ return false; | |
+ | |
+ SVal HandleBufSVal = State->getSVal(MemHandleBuf); | |
+ SymbolRef SymHandleBuf = HandleBufSVal.getAsSymbol(); | |
+ if (!SymHandleBuf) | |
+ return false; | |
+ | |
+ SymbolRef ParentSymbol = | |
+ dyn_cast<SymbolDerived>(SymHandleBuf)->getParentSymbol(); | |
+ if (!ParentSymbol) | |
+ return false; | |
+ | |
+ // The handle buffer is a typedValueRegion | |
+ const TypedValueRegion *TR = cast<TypedValueRegion>(MemHandleBuf); | |
+ QualType HandleType = TR->getValueType(); | |
+ | |
+ // Construct derived symbol associated with handle buffer | |
+ // 1. When the supplied handle count can be determined at call time | |
+ // (e.g. const/known variable), conjure exactly the number of handle symbols | |
+ // 2. if 1. is not satisfied, conjure exactly 4 handles | |
+ SVal InputSizeSVal = State->getSVal(InputSizeExpr, LCtx); | |
+ int64_t ExtVal = 4; | |
+ if (InputSizeSVal.getBaseKind() == SVal::NonLocKind && | |
+ InputSizeSVal.getSubKind() == nonloc::ConcreteIntKind) { | |
+ ExtVal = | |
+ InputSizeSVal.castAs<nonloc::ConcreteInt>().getValue().getExtValue(); | |
+ } else if (InputSizeSVal.getBaseKind() == SVal::LocKind && | |
+ InputSizeSVal.getSubKind() == loc::ConcreteIntKind) { | |
+ ExtVal = InputSizeSVal.castAs<loc::ConcreteInt>().getValue().getExtValue(); | |
+ } | |
+ | |
+ if (ExtVal < 0) | |
+ return false; | |
+ | |
+ for (int i = 0; i < ExtVal; ++i) { | |
+ NonLoc AryIndex = svalBuilder.makeArrayIndex(i); | |
+ const ElementRegion *CurElementRegion = memRegionMgr.getElementRegion( | |
+ HandleType, AryIndex, SuperRegionSub, ASTCtx); | |
+ if (!CurElementRegion) | |
+ return false; | |
+ | |
+ // Build derived SVal for elements in the handle buffer | |
+ DefinedOrUnknownSVal CurSVal = svalBuilder.getDerivedRegionValueSymbolVal( | |
+ ParentSymbol, CurElementRegion); | |
+ SymbolRef CurSymbol = CurSVal.getAsSymbol(); | |
+ if (!CurSymbol) | |
+ return false; | |
+ if (CurSVal.getBaseKind() == SVal::NonLocKind) | |
+ State->assumeInclusiveRange(CurSVal, llvm::APSInt::get(1), llvm::APSInt::get(2147483647), true); | |
+ #ifdef DEBUG_FLAG_CLEAN | |
+ llvm::errs()<<"Allocated handle symbol: "; | |
+ dumpSymbol(CurSymbol); | |
+ #endif | |
+ | |
+ // Set state for each constructed symbol | |
+ State = State->set<HStateMap>(CurSymbol, HandleState::getAllocated()); | |
+ } | |
+ // Process the actual_handle returned | |
+ SVal ActHandleSVal = State->getSVal(OutputSizeExpr, LCtx); | |
+ SVal ConcreteInvSVal = svalBuilder.makeIntVal(llvm::APSInt::get(ExtVal)); | |
+ State = State->bindLoc(ActHandleSVal, ConcreteInvSVal, LCtx); | |
+ | |
+ Ctx.addTransition(State); | |
+ Ctx.addTransition(FailedState); | |
+ | |
+ return true; | |
+} | |
+ | |
+bool MagentaHandleChecker::processMxChannelCreate(const CallExpr *CE, | |
+ CheckerContext &Ctx) const { | |
+ #ifdef DEBUG_FLAG_CLEAN | |
+ SourceLocation SL = CE->getLocStart(); | |
+ SourceManager & SMgr = Ctx.getSourceManager(); | |
+ SL.print(llvm::errs(), SMgr); | |
+ llvm::errs()<<" mx_channel_create encountered "; | |
+ #endif | |
+ ProgramStateRef State = Ctx.getState(); | |
+ const LocationContext *LCtx = Ctx.getLocationContext(); | |
+ const Expr *Handle1Expr = CE->getArg(1); | |
+ const Expr *Handle2Expr = CE->getArg(2); | |
+ if (!(Handle1Expr && Handle2Expr)) | |
+ return false; | |
+ | |
+ // Conjure a failed state | |
+ DefinedOrUnknownSVal FailedSVal = Ctx.getSValBuilder().conjureSymbolVal( | |
+ nullptr, CE, LCtx, Ctx.blockCount()); | |
+ ProgramStateRef FailedState = State->BindExpr(CE, LCtx, FailedSVal); | |
+ FailedState = FailedState->assumeInclusiveRange( | |
+ FailedSVal, llvm::APSInt::get(-2147483647), llvm::APSInt::get(-1), true); | |
+ | |
+ SmallVector<SVal, 2> invalidateSVal; | |
+ | |
+ // Conjure a successful state | |
+ invalidateSVal.push_back(State->getSVal(Handle1Expr, LCtx)); | |
+ invalidateSVal.push_back(State->getSVal(Handle2Expr, LCtx)); | |
+ | |
+ State = State->invalidateRegions(invalidateSVal, CE, Ctx.blockCount(), LCtx, | |
+ false, nullptr, nullptr, nullptr); | |
+ SVal StatusSVal = Ctx.getSValBuilder().makeIntVal(llvm::APSInt::get(0)); | |
+ State = State->BindExpr(CE, LCtx, StatusSVal); | |
+ | |
+ const MemRegion *MemHandle1 = State->getSVal(Handle1Expr, LCtx).getAsRegion(); | |
+ const MemRegion *MemHandle2 = State->getSVal(Handle2Expr, LCtx).getAsRegion(); | |
+ if (!MemHandle1 || !MemHandle2) | |
+ return false; | |
+ | |
+ SVal SValHandle1 = State->getSVal(MemHandle1); | |
+ SVal SValHandle2 = State->getSVal(MemHandle2); | |
+ SymbolRef SymHandle1 = SValHandle1.getAsSymbol(); | |
+ SymbolRef SymHandle2 = SValHandle2.getAsSymbol(); | |
+ if (!SymHandle1 || !SymHandle2) | |
+ return false; | |
+ DefinedOrUnknownSVal DSValHandle1 = SValHandle1.castAs<DefinedOrUnknownSVal>(); | |
+ DefinedOrUnknownSVal DSValHandle2 = SValHandle2.castAs<DefinedOrUnknownSVal>(); | |
+ if (DSValHandle1.getBaseKind() == SVal::NonLocKind && DSValHandle2.getBaseKind() == SVal::NonLocKind) { | |
+ State->assumeInclusiveRange(DSValHandle1, llvm::APSInt::get(1), llvm::APSInt::get(2147483647), true); | |
+ State->assumeInclusiveRange(DSValHandle2, llvm::APSInt::get(1), llvm::APSInt::get(2147483647), true); | |
+ } | |
+ #ifdef DEBUG_FLAG_CLEAN | |
+ llvm::errs()<<" handle allocated: "; | |
+ dumpSymbol(SymHandle1); | |
+ dumpSymbol(SymHandle2); | |
+ llvm::errs()<<"\n"; | |
+ #endif | |
+ State = State->set<HStateMap>(SymHandle1, HandleState::getAllocated()); | |
+ State = State->set<HStateMap>(SymHandle2, HandleState::getAllocated()); | |
+ Ctx.addTransition(State); | |
+ // Ctx.addTransition(FailedState); | |
+ | |
+ return true; | |
+} | |
+ | |
+bool MagentaHandleChecker::processMxChannelWrite(const CallExpr *CE, | |
+ CheckerContext &Ctx) const { | |
+ #ifdef DEBUG_FLAG_CLEAN | |
+ SourceLocation SL = CE->getLocStart(); | |
+ SourceManager & SMgr = Ctx.getSourceManager(); | |
+ SL.print(llvm::errs(), SMgr); | |
+ llvm::errs()<<" mx_channel_create encountered "; | |
+ #endif | |
+ ProgramStateRef State = Ctx.getState(); | |
+ const LocationContext *LCtx = Ctx.getLocationContext(); | |
+ ProgramStateManager &Mgr = State->getStateManager(); | |
+ ASTContext &ASTCtx = Mgr.getContext(); | |
+ SValBuilder &svalBuilder = Ctx.getSValBuilder(); | |
+ MemRegionManager &memRegionMgr = svalBuilder.getRegionManager(); | |
+ | |
+ // Determine if this callsite is related to transfer of handles | |
+ const Expr *HandleBufExpr = CE->getArg(4); | |
+ const Expr *InputSizeExpr = CE->getArg(5); | |
+ if (CheckExprIsZero(HandleBufExpr, Ctx) || | |
+ CheckExprIsZero(InputSizeExpr, Ctx)) | |
+ return false; | |
+ | |
+ // Conjure a failed state which does not release any handle | |
+ DefinedOrUnknownSVal FailedStatusSVal = | |
+ svalBuilder.conjureSymbolVal(nullptr, CE, LCtx, Ctx.blockCount()); | |
+ ProgramStateRef FailedState = State->BindExpr(CE, LCtx, FailedStatusSVal); | |
+ FailedState = FailedState->assumeInclusiveRange( | |
+ FailedStatusSVal, llvm::APSInt::get(-2147483647), llvm::APSInt::get(-1), | |
+ true); | |
+ | |
+ // Conjure a successful state | |
+ SVal StatusSVal = Ctx.getSValBuilder().makeIntVal(llvm::APSInt::get(0)); | |
+ State = State->BindExpr(CE, LCtx, StatusSVal); | |
+ | |
+ // Determine the number of handles | |
+ SVal InputSizeSVal = State->getSVal(InputSizeExpr, LCtx); | |
+ int64_t ExtVal = -1; | |
+ if (InputSizeSVal.getBaseKind() == SVal::NonLocKind && | |
+ InputSizeSVal.getSubKind() == nonloc::ConcreteIntKind) { | |
+ ExtVal = | |
+ InputSizeSVal.castAs<nonloc::ConcreteInt>().getValue().getExtValue(); | |
+ } else if (InputSizeSVal.getBaseKind() == SVal::LocKind && | |
+ InputSizeSVal.getSubKind() == loc::ConcreteIntKind) { | |
+ ExtVal = InputSizeSVal.castAs<loc::ConcreteInt>().getValue().getExtValue(); | |
+ } | |
+#ifdef DEBUG_FLAG_CLEAN | |
+ llvm::errs()<<"Channel Write, handle count: "<<ExtVal<<"\n"; | |
+#endif | |
+ // Retrive MemRegion of the handle buffer | |
+ SVal HandleBufSValOrig = State->getSVal(HandleBufExpr, LCtx); | |
+ const MemRegion *MemHandleBuf = | |
+ HandleBufSValOrig.getAsRegion(); | |
+ | |
+ if (isa<VarRegion>(MemHandleBuf)) { | |
+ // Handle buffer is a pointer to a local mx_handle_t variable | |
+ // Only 1 handle will be transmitted | |
+ ExtVal = 1; | |
+ SVal HandleBufSVal = State->getSVal(MemHandleBuf); | |
+ SymbolRef SymHandleBuf = HandleBufSVal.getAsSymbol(); | |
+ #ifdef DEBUG_FLAG_CLEAN | |
+ llvm::errs()<<"MemHandleBuf is a VarRegion type\n"; | |
+ llvm::errs()<<"Releasing SVal:"; | |
+ HandleBufSVal.dumpToStream(llvm::errs()); | |
+ llvm::errs()<<" SymbolRef "; | |
+ dumpSymbol(SymHandleBuf); | |
+ #endif | |
+ State = State->set<HStateMap>(SymHandleBuf, HandleState::getReleased()); | |
+ Ctx.addTransition(FailedState); | |
+ Ctx.addTransition(State); | |
+ return true; | |
+ } | |
+ | |
+ const SubRegion *SuperRegionSub = isa<ElementRegion>(MemHandleBuf)? dyn_cast<SubRegion>(dyn_cast<ElementRegion>(MemHandleBuf)->getSuperRegion()) : NULL; | |
+ #ifdef DEBUG_FLAG_CLEAN | |
+ llvm::errs()<<"SVal: "; | |
+ HandleBufSValOrig.dumpToStream(llvm::errs()); | |
+ llvm::errs()<<"MemRegion: "; | |
+ MemHandleBuf->dumpToStream(llvm::errs()); | |
+ SVal RegionSVal = State->getSVal(MemHandleBuf); | |
+ llvm::errs()<<" SVal: "; | |
+ RegionSVal.dumpToStream(llvm::errs()); | |
+ llvm::errs()<<"\n"; | |
+ #endif | |
+ if (!SuperRegionSub) { | |
+ // The handle buffer passed in is a pointer to local mx_handle_t variable | |
+ // instead of an array or a pointer to a memory region. | |
+ // Rare to see in practice. | |
+ // TODO: Handle this situation in the future. | |
+ llvm::errs()<<"SuperRegionSub is null\n"; | |
+ return false; | |
+ } | |
+ | |
+ SVal HandleBufSVal = State->getSVal(MemHandleBuf); | |
+ SymbolRef SymHandleBuf = HandleBufSVal.getAsSymbol(); | |
+ | |
+ if (!SymHandleBuf) | |
+ // The handle buffer is not initialized or the handles are not allocated. | |
+ // Rare to see in practice. | |
+ // TODO: Rethink this situation in the future. | |
+ return false; | |
+ | |
+ SymbolRef ParentSymbol = | |
+ dyn_cast<SymbolDerived>(SymHandleBuf)->getParentSymbol(); | |
+ if (!ParentSymbol) | |
+ return false; | |
+ | |
+ const TypedValueRegion *TR = cast<TypedValueRegion>(MemHandleBuf); | |
+ QualType HandleType = TR->getValueType(); | |
+ | |
+ bool TransitionHappened = false; | |
+ if (ExtVal < 0 || ExtVal > 5) { | |
+ // We don't know the exact number of handles that should be transferred or | |
+ // it is too large. Query the map and release all handles that may related. | |
+ HStateMapTy TrackedHandle = State->get<HStateMap>(); | |
+ for (auto &CurItem : TrackedHandle) { | |
+ SymbolRef Sym = CurItem.first; | |
+ HandleState CurHandleState = CurItem.second; | |
+ const SymbolDerived *ElementSym = dyn_cast<SymbolDerived>(Sym); | |
+ if (!ElementSym) | |
+ continue; | |
+ | |
+ if (ElementSym->getParentSymbol() == ParentSymbol) { | |
+ if (CurHandleState.isReleased()) { | |
+ // Double release | |
+ reportDoubleRelease(Sym, CE->getSourceRange(), Ctx); | |
+ } else { | |
+ #ifdef DEBUG_FLAG_CLEAN | |
+ llvm::errs()<<"Releasing handle symbol: "; | |
+ dumpSymbol(Sym); | |
+ #endif | |
+ State = State->set<HStateMap>(Sym, HandleState::getReleased()); | |
+ TransitionHappened = true; | |
+ } | |
+ } | |
+ } | |
+ } else { | |
+ for (int i = 0; i < ExtVal; ++i) { | |
+ NonLoc AryIndex = svalBuilder.makeArrayIndex(i); | |
+ const ElementRegion *CurElementRegion = memRegionMgr.getElementRegion( | |
+ HandleType, AryIndex, SuperRegionSub, ASTCtx); | |
+ if (!CurElementRegion) | |
+ return false; | |
+ | |
+ // Build derived SVal for elements in the handle buffer | |
+ DefinedOrUnknownSVal CurSVal = svalBuilder.getDerivedRegionValueSymbolVal( | |
+ ParentSymbol, CurElementRegion); | |
+ SymbolRef CurSymbol = CurSVal.getAsSymbol(); | |
+ if (!CurSymbol) | |
+ return false; | |
+ | |
+ const HandleState *SymState = State->get<HStateMap>(CurSymbol); | |
+ if (!SymState) | |
+ continue; | |
+ | |
+ if (SymState->isReleased()) { | |
+ reportDoubleRelease(CurSymbol, CE->getSourceRange(), Ctx); | |
+ } else { | |
+ #ifdef DEBUG_FLAG_CLEAN | |
+ llvm::errs()<<"Releasing handle symbol: "; | |
+ dumpSymbol(CurSymbol); | |
+ #endif | |
+ State = State->set<HStateMap>(CurSymbol, HandleState::getReleased()); | |
+ TransitionHappened = true; | |
+ } | |
+ } | |
+ } | |
+ if (TransitionHappened) | |
+ Ctx.addTransition(State); | |
+ | |
+ Ctx.addTransition(FailedState); | |
+ return true; | |
+} | |
+ | |
+void MagentaHandleChecker::processMxHandleClose(ProgramStateRef State, | |
+ const CallEvent &Call, | |
+ CheckerContext &Ctx) const { | |
+ #ifdef DEBUG_FLAG_CLEAN | |
+ const CallExpr * CE = dyn_cast<CallExpr>(Call.getOriginExpr()); | |
+ if (!CE) | |
+ return; | |
+ SourceLocation SL = CE->getLocStart(); | |
+ SourceManager & SMgr = Ctx.getSourceManager(); | |
+ SL.print(llvm::errs(), SMgr); | |
+ llvm::errs()<<" mx_handle_close encountered "; | |
+ #endif | |
+ // Retrive symbolic value for passed in handle | |
+ SymbolRef SymHandle = Call.getArgSVal(0).getAsSymbol(); | |
+ if (!SymHandle) { | |
+ #ifdef DEBUG_FLAG_CLEAN | |
+ llvm::errs()<<" Symbol is null, SVal is "; | |
+ Call.getArgSVal(0).dumpToStream(llvm::errs()); | |
+ llvm::errs()<<"\n"; | |
+ #endif | |
+ return; | |
+ } | |
+ | |
+ const HandleState *SymState = State->get<HStateMap>(SymHandle); | |
+ if (!SymState) { | |
+ #ifdef DEBUG_FLAG_CLEAN | |
+ llvm::errs()<<" HandleState is not found for symbol "; | |
+ dumpSymbol(SymHandle); | |
+ #endif | |
+ return; | |
+ } | |
+ #ifdef DEBUG_FLAG_CLEAN | |
+ SL.print(llvm::errs(), SMgr); | |
+ llvm::errs()<<"Handle Close on SVal: "; | |
+ Call.getArgSVal(0).dumpToStream(llvm::errs()); | |
+ llvm::errs()<<" SymbolRef: "; | |
+ dumpSymbol(SymHandle); | |
+ #endif | |
+ if (SymState->isReleased()) { | |
+ // Double release | |
+ reportDoubleRelease(SymHandle, Call.getSourceRange(), Ctx); | |
+ return; | |
+ } | |
+ State = State->set<HStateMap>(SymHandle, HandleState::getReleased()); | |
+ Ctx.addTransition(State); | |
+} | |
+ | |
+void MagentaHandleChecker::reportLeaks(ArrayRef<SymbolRef> LeakedHandles, | |
+ CheckerContext &C, | |
+ ExplodedNode *ErrorNode) const { | |
+ for (SymbolRef LeakedHandle : LeakedHandles) { | |
+ auto R = llvm::make_unique<BugReport>( | |
+ *LeakBugType, | |
+ "Allocated handle is never released; potential resource leak", | |
+ ErrorNode); | |
+ R->markInteresting(LeakedHandle); | |
+ R->disablePathPruning(); | |
+// R->addVisitor(llvm::make_unique<MagentaBugVisitor>(LeakedHandle)); | |
+ C.emitReport(std::move(R)); | |
+ } | |
+} | |
+ | |
+void MagentaHandleChecker::reportDoubleRelease(SymbolRef HandleSym, | |
+ const SourceRange &Range, | |
+ CheckerContext &C) const { | |
+ ExplodedNode *ErrNode = C.generateErrorNode(); | |
+ if (!ErrNode) | |
+ return; | |
+ | |
+ auto R = llvm::make_unique<BugReport>( | |
+ *DoubleReleaseBugType, "Releasing a previously released handle", ErrNode); | |
+ R->addRange(Range); | |
+ R->markInteresting(HandleSym); | |
+ C.emitReport(std::move(R)); | |
+} | |
+ | |
+void MagentaHandleChecker::reportUseAfterFree(SymbolRef HandleSym, | |
+ const SourceRange &Range, | |
+ CheckerContext &Ctx) const { | |
+ ExplodedNode *ErrNode = Ctx.generateErrorNode(); | |
+ if (!ErrNode) | |
+ return; | |
+ | |
+ auto R = llvm::make_unique<BugReport>( | |
+ *UseAfterFreeBugType, "Using a previously released handle", ErrNode); | |
+ R->addRange(Range); | |
+ R->markInteresting(HandleSym); | |
+ Ctx.emitReport(std::move(R)); | |
+} | |
+ | |
+void ento::registerMagentaHandleChecker(CheckerManager &mgr) { | |
+ mgr.registerChecker<MagentaHandleChecker>(); | |
+} | |
diff --git a/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp | |
index f84c0ee800..5545719a74 100644 | |
--- a/lib/StaticAnalyzer/Core/ExprEngine.cpp | |
+++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp | |
@@ -1501,6 +1501,7 @@ void ExprEngine::processCFGBlockEntrance(const BlockEdge &L, | |
unsigned int BlockCount = nodeBuilder.getContext().blockCount(); | |
if (BlockCount == AMgr.options.maxBlockVisitOnPath - 1 && | |
AMgr.options.shouldWidenLoops()) { | |
+ llvm::errs()<<"Widen Loop\n"; | |
const Stmt *Term = nodeBuilder.getContext().getBlock()->getTerminator(); | |
if (!(Term && | |
(isa<ForStmt>(Term) || isa<WhileStmt>(Term) || isa<DoStmt>(Term)))) | |
@@ -1516,6 +1517,11 @@ void ExprEngine::processCFGBlockEntrance(const BlockEdge &L, | |
// FIXME: Refactor this into a checker. | |
if (BlockCount >= AMgr.options.maxBlockVisitOnPath) { | |
static SimpleProgramPointTag tag(TagProviderName, "Block count exceeded"); | |
+ llvm::errs()<<"Block Count exceeded maxBlockVisitOnPath "<<AMgr.options.maxBlockVisitOnPath<<" Dump SRC\n"; | |
+ L.getSrc()->dump(); | |
+ llvm::errs()<<"\nDump Dst:\n"; | |
+ L.getDst()->dump(); | |
+ llvm::errs()<<"\n"; | |
const ExplodedNode *Sink = | |
nodeBuilder.generateSink(Pred->getState(), Pred, &tag); | |
@@ -1906,7 +1912,7 @@ void ExprEngine::processSwitch(SwitchNodeBuilder& builder) { | |
// Evaluate the LHS of the case value. | |
llvm::APSInt V1 = Case->getLHS()->EvaluateKnownConstInt(getContext()); | |
assert(V1.getBitWidth() == getContext().getIntWidth(CondE->getType())); | |
- | |
+ | |
// Get the RHS of the case, if it exists. | |
llvm::APSInt V2; | |
if (const Expr *E = Case->getRHS()) | |
diff --git a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp | |
index caf86b26b6..03d929470c 100644 | |
--- a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp | |
+++ b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp | |
@@ -902,37 +902,52 @@ void ExprEngine::defaultEvalCall(NodeBuilder &Bldr, ExplodedNode *Pred, | |
// The origin expression here is just used as a kind of checksum; | |
// this should still be safe even for CallEvents that don't come from exprs. | |
const Expr *E = Call->getOriginExpr(); | |
- | |
+ SourceLocation SL = E->getExprLoc(); | |
+ SourceManager &SMgr = getBugReporter().getSourceManager(); | |
+ SL.print(llvm::errs(), SMgr); | |
+ const FunctionDecl * FD = dyn_cast<CallExpr>(E)->getDirectCallee(); | |
+ if (FD) { | |
+ StringRef Name = FD->getName(); | |
+ llvm::errs()<<" "<<Name<<" "; | |
+ } | |
ProgramStateRef InlinedFailedState = getInlineFailedState(State, E); | |
if (InlinedFailedState) { | |
// If we already tried once and failed, make sure we don't retry later. | |
+ llvm::errs()<<" InlinedFailedState is not null "; | |
State = InlinedFailedState; | |
} else { | |
RuntimeDefinition RD = Call->getRuntimeDefinition(); | |
const Decl *D = RD.getDecl(); | |
if (shouldInlineCall(*Call, D, Pred)) { | |
+ llvm::errs()<<" Should inline: "; | |
if (RD.mayHaveOtherDefinitions()) { | |
AnalyzerOptions &Options = getAnalysisManager().options; | |
// Explore with and without inlining the call. | |
if (Options.getIPAMode() == IPAK_DynamicDispatchBifurcate) { | |
+ llvm::errs()<<"Bifurcate \n"; | |
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) { | |
+ llvm::errs()<<"Dynamic \n"; | |
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)) | |
+ if (inlineCall(*Call, D, Bldr, Pred, State)) { | |
+ llvm::errs()<<"Direct inlined\n"; | |
return; | |
+ } else { | |
+ llvm::errs()<<"Direct inline failed\n"; | |
+ } | |
} | |
} | |
- | |
+ llvm::errs()<<" Should not inline \n"; | |
// If we can't inline it, handle the return value and invalidate the regions. | |
conservativeEvalCall(*Call, Bldr, Pred, State); | |
} | |
diff --git a/test/Analysis/mxhandle.c b/test/Analysis/mxhandle.c | |
new file mode 100644 | |
index 0000000000..f241045d57 | |
--- /dev/null | |
+++ b/test/Analysis/mxhandle.c | |
@@ -0,0 +1,217 @@ | |
+// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.magenta.MagentaHandleChecker -analyzer-store=region -verify %s | |
+ | |
+typedef __typeof__(sizeof(int)) size_t; | |
+typedef int mx_status_t; | |
+typedef __typeof__(sizeof(int)) mx_handle_t; | |
+typedef unsigned int uint32_t; | |
+#define NULL ((void *)0) | |
+ | |
+mx_status_t mx_channel_create( | |
+ uint32_t options, | |
+ mx_handle_t* out0, | |
+ mx_handle_t* out1); | |
+ | |
+mx_status_t mx_handle_close(mx_handle_t handle); | |
+ | |
+mx_status_t mx_channel_read(mx_handle_t handle, uint32_t options, | |
+ void* bytes, mx_handle_t* handles, | |
+ uint32_t num_bytes, uint32_t num_handles, | |
+ uint32_t* actual_bytes, uint32_t* actual_handles); | |
+ | |
+mx_status_t mx_channel_write(mx_handle_t handle, uint32_t options, | |
+ void* bytes, uint32_t num_bytes, | |
+ mx_handle_t* handles, uint32_t num_handles); | |
+ | |
+void escapeMethod(mx_handle_t *in); | |
+void useHandle(mx_handle_t handle); | |
+ | |
+// End of declaration | |
+ | |
+void checkNoLeak01() { | |
+ mx_handle_t sa, sb; | |
+ mx_channel_create(0, &sa, &sb); | |
+ mx_handle_close(sa); | |
+ mx_handle_close(sb); | |
+} | |
+ | |
+void checkNoLeak02() { | |
+ mx_handle_t ay[2]; | |
+ mx_channel_create(0, &ay[0], &ay[1]); | |
+ mx_handle_close(ay[0]); | |
+ mx_handle_close(ay[1]); | |
+} | |
+ | |
+void checkNoLeak03() { | |
+ mx_handle_t ay[2]; | |
+ mx_channel_create(0, &ay[0], &ay[1]); | |
+ for (int i = 0; i < 2; i++) { | |
+ mx_handle_close(ay[i]); | |
+ } | |
+} | |
+ | |
+mx_handle_t checkNoLeak04() { | |
+ mx_handle_t sa, sb; | |
+ mx_channel_create(0, &sa, &sb); | |
+ mx_handle_close(sa); | |
+ return sb; // no warning | |
+} | |
+ | |
+mx_handle_t checkNoLeak05(mx_handle_t *out1) { | |
+ mx_handle_t sa, sb; | |
+ mx_channel_create(0, &sa, &sb); | |
+ *out1 = sa; | |
+ return sb; // no warning | |
+} | |
+ | |
+void checkNoLeak06(mx_handle_t handle) { | |
+ mx_handle_t handlebuf[4]; | |
+ uint32_t hcount; | |
+ mx_channel_read(handle, 0, NULL, handlebuf, 0, 4, 0, &hcount); | |
+ for (int i = 0; i < hcount; i++) { | |
+ mx_handle_close(handlebuf[i]); | |
+ } | |
+} | |
+ | |
+void checkNoLeak07(mx_handle_t handle, uint32_t hcount) { | |
+ mx_handle_t handlebuf[6]; | |
+ mx_channel_read(handle, 0, NULL, handlebuf, 0, hcount, 0, &hcount); | |
+ for (int i = 0; i < hcount; i++) { | |
+ mx_handle_close(handlebuf[i]); | |
+ } | |
+} | |
+ | |
+void checkNoLeak08(mx_handle_t handle) { | |
+ mx_handle_t handlebuf[4]; | |
+ uint32_t hcount; | |
+ mx_channel_read(handle, 0, NULL, handlebuf, 0, 4, 0, &hcount); | |
+ if (mx_channel_write(handle, 0, NULL, 0, handlebuf, hcount) < 0) { | |
+ for (int i = 0; i < hcount; i++) { | |
+ mx_handle_close(handlebuf[i]); | |
+ } | |
+ } | |
+} | |
+ | |
+void checkNoLeak09(mx_handle_t handle, uint32_t hcount) { | |
+ mx_handle_t handlebuf[6]; | |
+ mx_channel_read(handle, 0, NULL, handlebuf, 0, hcount, 0, &hcount); | |
+ if (mx_channel_write(handle, 0, NULL, 0, handlebuf, hcount) < 0) { | |
+ for (int i = 0; i < hcount; i++) { | |
+ mx_handle_close(handlebuf[i]); | |
+ } | |
+ } | |
+} | |
+ | |
+void checkNoLeak10() { | |
+ mx_handle_t sa, sb; | |
+ if (mx_channel_create(0, &sa, &sb) < 0) { | |
+ return; | |
+ } | |
+ mx_handle_close(sa); | |
+ mx_handle_close(sb); | |
+} | |
+ | |
+void checkNoLeak11(mx_handle_t handle, uint32_t hcount) { | |
+ mx_handle_t handlebuf[6]; | |
+ mx_status_t r = mx_channel_read(handle, 0, NULL, | |
+ handlebuf, 0, hcount, 0, &hcount); | |
+ if (r < 0) { | |
+ return; | |
+ } | |
+ for (int i = 0; i < hcount; i++) { | |
+ mx_handle_close(handlebuf[i]); | |
+ } | |
+} | |
+ | |
+void checkNoLeak12(int tag) { | |
+ mx_handle_t sa, sb; | |
+ if (mx_channel_create(0, &sa, &sb) < 0) { | |
+ return; | |
+ } | |
+ if (tag) { | |
+ escapeMethod(&sa); | |
+ escapeMethod(&sb); | |
+ } | |
+ mx_handle_close(sa); | |
+ mx_handle_close(sb); | |
+} | |
+ | |
+void checkLeak01() { | |
+ mx_handle_t sa, sb; | |
+ mx_channel_create(0, &sa, &sb); | |
+} // expected-warning {{Allocated handle is never released; potential resource leak}} | |
+ | |
+void checkLeak02(int tag) { | |
+ mx_handle_t sa, sb; | |
+ mx_channel_create(0, &sa, &sb); | |
+ if (tag) { | |
+ mx_handle_close(sa); | |
+ } | |
+ mx_handle_close(sb); // expected-warning {{Allocated handle is never released; potential resource leak}} | |
+} | |
+ | |
+void checkLeak03(mx_handle_t handle) { | |
+ mx_handle_t handlebuf[4]; | |
+ uint32_t hcount; | |
+ mx_status_t r = mx_channel_read(handle, 0, NULL, handlebuf, 0, 4, 0, &hcount); | |
+ if (r < 0) { | |
+ return; | |
+ } | |
+ for (int i = 0; i < hcount -1; i++) { | |
+ mx_handle_close(handlebuf[i]); | |
+ } | |
+} // expected-warning {{Allocated handle is never released; potential resource leak}} | |
+ | |
+void checkLeak04(mx_handle_t handle) { | |
+ mx_handle_t handlebuf[3]; | |
+ uint32_t hcount; | |
+ mx_status_t r = mx_channel_read(handle, 0, NULL, handlebuf, 0, 3, 0, &hcount); | |
+ if (r < 0) { | |
+ return; | |
+ } | |
+ if (mx_channel_write(handle, 0, NULL, 0, handlebuf, hcount - 1) < 0) { | |
+ for (int i = 0; i < hcount; i++) { | |
+ mx_handle_close(handlebuf[i]); | |
+ } | |
+ } | |
+} // expected-warning {{Allocated handle is never released; potential resource leak}} | |
+ | |
+void checkLeak05(mx_handle_t handle, uint32_t hcount) { | |
+ mx_handle_t handlebuf[6]; | |
+ mx_status_t r = mx_channel_read( | |
+ handle, 0, NULL, handlebuf, 0, hcount, 0, &hcount); | |
+ if (r < 0) { | |
+ return; | |
+ } | |
+ if (mx_channel_write(handle, 0, NULL, 0, handlebuf, hcount - 1) < 0) { | |
+ for (int i = 0; i < hcount; i++) { | |
+ mx_handle_close(handlebuf[i]); | |
+ } | |
+ } | |
+} // expected-warning {{Allocated handle is never released; potential resource leak}} | |
+ | |
+void checkLeak06(mx_handle_t handle, uint32_t hcount) { | |
+ mx_handle_t handlebuf[6]; | |
+ mx_channel_read(handle, 0, NULL, handlebuf, 0, hcount, 0, &hcount); | |
+ mx_channel_write(handle, 0, NULL, 0, handlebuf, hcount); // It may fail and handles are not released. | |
+} // expected-warning {{Allocated handle is never released; potential resource leak}} | |
+ | |
+void checkDoubleRelease01(int tag) { | |
+ mx_handle_t sa, sb; | |
+ mx_channel_create(0, &sa, &sb); | |
+ if (tag) { | |
+ mx_handle_close(sa); | |
+ } | |
+ mx_handle_close(sa); // expected-warning {{Releasing a previously released handle}} | |
+ mx_handle_close(sb); | |
+} | |
+ | |
+void checkUseAfterFree01(int tag) { | |
+ mx_handle_t sa, sb; | |
+ mx_channel_create(0, &sa, &sb); | |
+ if (tag) { | |
+ mx_handle_close(sa); | |
+ } | |
+ useHandle(sa); // expected-warning {{Using a previously released handle}} | |
+ mx_handle_close(sa); | |
+ mx_handle_close(sb); | |
+} | |
diff --git a/tools/scan-build-py/libscanbuild/analyze.py b/tools/scan-build-py/libscanbuild/analyze.py | |
index a09c72389d..b5614b5b6d 100644 | |
--- a/tools/scan-build-py/libscanbuild/analyze.py | |
+++ b/tools/scan-build-py/libscanbuild/analyze.py | |
@@ -249,7 +249,7 @@ def analyzer_params(args): | |
if args.output_format: | |
result.append('-analyzer-output={0}'.format(args.output_format)) | |
if args.analyzer_config: | |
- result.append(args.analyzer_config) | |
+ result.extend(['-analyzer-config', args.analyzer_config]) | |
if args.verbose >= 4: | |
result.append('-analyzer-display-progress') | |
if args.plugins: |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment