Skip to content

Instantly share code, notes, and snippets.

@zeroomega
Created June 26, 2017 23:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zeroomega/0a2abb371d85ff0444a8a9562d119b80 to your computer and use it in GitHub Desktop.
Save zeroomega/0a2abb371d85ff0444a8a9562d119b80 to your computer and use it in GitHub Desktop.
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