diff options
author | Anton Yartsev <anton.yartsev@gmail.com> | 2015-02-19 13:36:20 +0000 |
---|---|---|
committer | Anton Yartsev <anton.yartsev@gmail.com> | 2015-02-19 13:36:20 +0000 |
commit | 5b5c7cec081e29f8019e967333a8d893f0c83da5 (patch) | |
tree | c1519a2b71d36697921c709e8e9609bd3f996f57 /clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp | |
parent | f0f0d2739127cd8971f0c91a45596ac40effb53b (diff) | |
download | bcm5719-llvm-5b5c7cec081e29f8019e967333a8d893f0c83da5.tar.gz bcm5719-llvm-5b5c7cec081e29f8019e967333a8d893f0c83da5.zip |
[analyzer] Different handling of alloca().
+ separate bug report for "Free alloca()" error to be able to customize checkers responsible for this error.
+ Muted "Free alloca()" error for NewDelete checker that is not responsible for c-allocated memory, turned on for unix.MismatchedDeallocator checker.
+ RefState for alloca() - to be able to detect usage of zero-allocated memory by upcoming ZeroAllocDereference checker.
+ AF_Alloca family to handle alloca() consistently - keep proper family in RefState, handle 'alloca' by getCheckIfTracked() facility, etc.
+ extra tests.
llvm-svn: 229850
Diffstat (limited to 'clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp')
-rw-r--r-- | clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp | 108 |
1 files changed, 78 insertions, 30 deletions
diff --git a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp index a49247bbd22..0930808f3c2 100644 --- a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -43,7 +43,8 @@ enum AllocationFamily { AF_Malloc, AF_CXXNew, AF_CXXNewArray, - AF_IfNameIndex + AF_IfNameIndex, + AF_Alloca }; class RefState { @@ -160,10 +161,11 @@ class MallocChecker : public Checker<check::DeadSymbols, { public: MallocChecker() - : II_malloc(nullptr), II_free(nullptr), II_realloc(nullptr), - II_calloc(nullptr), II_valloc(nullptr), II_reallocf(nullptr), - II_strndup(nullptr), II_strdup(nullptr), II_kmalloc(nullptr), - II_if_nameindex(nullptr), II_if_freenameindex(nullptr) {} + : II_alloca(nullptr), II_alloca_builtin(nullptr), II_malloc(nullptr), + II_free(nullptr), II_realloc(nullptr), II_calloc(nullptr), + II_valloc(nullptr), II_reallocf(nullptr), II_strndup(nullptr), + II_strdup(nullptr), II_kmalloc(nullptr), II_if_nameindex(nullptr), + II_if_freenameindex(nullptr) {} /// In pessimistic mode, the checker assumes that it does not know which /// functions might free the memory. @@ -217,11 +219,13 @@ private: mutable std::unique_ptr<BugType> BT_Leak[CK_NumCheckKinds]; mutable std::unique_ptr<BugType> BT_UseFree[CK_NumCheckKinds]; mutable std::unique_ptr<BugType> BT_BadFree[CK_NumCheckKinds]; + mutable std::unique_ptr<BugType> BT_FreeAlloca[CK_NumCheckKinds]; mutable std::unique_ptr<BugType> BT_MismatchedDealloc; mutable std::unique_ptr<BugType> BT_OffsetFree[CK_NumCheckKinds]; - mutable IdentifierInfo *II_malloc, *II_free, *II_realloc, *II_calloc, - *II_valloc, *II_reallocf, *II_strndup, *II_strdup, - *II_kmalloc, *II_if_nameindex, *II_if_freenameindex; + mutable IdentifierInfo *II_alloca, *II_alloca_builtin, *II_malloc, *II_free, + *II_realloc, *II_calloc, *II_valloc, *II_reallocf, + *II_strndup, *II_strdup, *II_kmalloc, *II_if_nameindex, + *II_if_freenameindex; mutable Optional<uint64_t> KernelZeroFlagVal; void initIdentifierInfo(ASTContext &C) const; @@ -343,6 +347,8 @@ private: static bool SummarizeRegion(raw_ostream &os, const MemRegion *MR); void ReportBadFree(CheckerContext &C, SVal ArgVal, SourceRange Range, const Expr *DeallocExpr) const; + void ReportFreeAlloca(CheckerContext &C, SVal ArgVal, + SourceRange Range) const; void ReportMismatchedDealloc(CheckerContext &C, SourceRange Range, const Expr *DeallocExpr, const RefState *RS, SymbolRef Sym, bool OwnershipTransferred) const; @@ -501,6 +507,8 @@ public: void MallocChecker::initIdentifierInfo(ASTContext &Ctx) const { if (II_malloc) return; + II_alloca = &Ctx.Idents.get("alloca"); + II_alloca_builtin = &Ctx.Idents.get("__builtin_alloca"); II_malloc = &Ctx.Idents.get("malloc"); II_free = &Ctx.Idents.get("free"); II_realloc = &Ctx.Idents.get("realloc"); @@ -521,6 +529,9 @@ bool MallocChecker::isMemFunction(const FunctionDecl *FD, ASTContext &C) const { if (isCMemFunction(FD, C, AF_IfNameIndex, MemoryOperationKind::MOK_Any)) return true; + if (isCMemFunction(FD, C, AF_Alloca, MemoryOperationKind::MOK_Any)) + return true; + if (isStandardNewDelete(FD, C)) return true; @@ -564,6 +575,11 @@ bool MallocChecker::isCMemFunction(const FunctionDecl *FD, if (FunI == II_if_nameindex) return true; } + + if (Family == AF_Alloca && CheckAlloc) { + if (FunI == II_alloca || FunI == II_alloca_builtin) + return true; + } } if (Family != AF_Malloc) @@ -747,8 +763,10 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const { State = MallocUpdateRefState(C, CE, State); } else if (FunI == II_strndup) { State = MallocUpdateRefState(C, CE, State); - } - else if (isStandardNewDelete(FD, C.getASTContext())) { + } else if (FunI == II_alloca || FunI == II_alloca_builtin) { + State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State, + AF_Alloca); + } else if (isStandardNewDelete(FD, C.getASTContext())) { // Process direct calls to operator new/new[]/delete/delete[] functions // as distinct from new/new[]/delete/delete[] expressions that are // processed by the checkPostStmt callbacks for CXXNewExpr and @@ -1096,6 +1114,9 @@ AllocationFamily MallocChecker::getAllocationFamily(CheckerContext &C, if (isCMemFunction(FD, Ctx, AF_IfNameIndex, MemoryOperationKind::MOK_Any)) return AF_IfNameIndex; + if (isCMemFunction(FD, Ctx, AF_Alloca, MemoryOperationKind::MOK_Any)) + return AF_Alloca; + return AF_None; } @@ -1160,6 +1181,7 @@ void MallocChecker::printExpectedAllocName(raw_ostream &os, CheckerContext &C, case AF_CXXNew: os << "'new'"; return; case AF_CXXNewArray: os << "'new[]'"; return; case AF_IfNameIndex: os << "'if_nameindex()'"; return; + case AF_Alloca: case AF_None: llvm_unreachable("not a deallocation expression"); } } @@ -1171,7 +1193,8 @@ void MallocChecker::printExpectedDeallocName(raw_ostream &os, case AF_CXXNew: os << "'delete'"; return; case AF_CXXNewArray: os << "'delete[]'"; return; case AF_IfNameIndex: os << "'if_freenameindex()'"; return; - case AF_None: llvm_unreachable("suspicious AF_None argument"); + case AF_Alloca: + case AF_None: llvm_unreachable("suspicious argument"); } } @@ -1225,8 +1248,7 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C, const MemSpaceRegion *MS = R->getMemorySpace(); - // Parameters, locals, statics, globals, and memory returned by alloca() - // shouldn't be freed. + // Parameters, locals, statics and globals shouldn't be freed. if (!(isa<UnknownSpaceRegion>(MS) || isa<HeapSpaceRegion>(MS))) { // FIXME: at the time this code was written, malloc() regions were // represented by conjured symbols, which are all in UnknownSpaceRegion. @@ -1252,6 +1274,12 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C, if (RsBase) { + // Memory returned by alloca() shouldn't be freed. + if (RsBase->getAllocationFamily() == AF_Alloca) { + ReportFreeAlloca(C, ArgVal, ArgExpr->getSourceRange()); + return nullptr; + } + // Check for double free first. if ((RsBase->isReleased() || RsBase->isRelinquished()) && !didPreviousFreeFail(State, SymBase, PreviousRetStatusSymbol)) { @@ -1327,7 +1355,8 @@ MallocChecker::getCheckIfTracked(MallocChecker::CheckKind CK, switch (Family) { case AF_Malloc: - case AF_IfNameIndex: { + case AF_IfNameIndex: + case AF_Alloca: { // C checkers. if (CK == CK_MallocOptimistic || CK == CK_MallocPessimistic) { @@ -1502,23 +1531,19 @@ void MallocChecker::ReportBadFree(CheckerContext &C, SVal ArgVal, while (const ElementRegion *ER = dyn_cast_or_null<ElementRegion>(MR)) MR = ER->getSuperRegion(); - if (MR && isa<AllocaRegion>(MR)) - os << "Memory allocated by alloca() should not be deallocated"; - else { - os << "Argument to "; - if (!printAllocDeallocName(os, C, DeallocExpr)) - os << "deallocator"; - - os << " is "; - bool Summarized = MR ? SummarizeRegion(os, MR) - : SummarizeValue(os, ArgVal); - if (Summarized) - os << ", which is not memory allocated by "; - else - os << "not memory allocated by "; + os << "Argument to "; + if (!printAllocDeallocName(os, C, DeallocExpr)) + os << "deallocator"; - printExpectedAllocName(os, C, DeallocExpr); - } + os << " is "; + bool Summarized = MR ? SummarizeRegion(os, MR) + : SummarizeValue(os, ArgVal); + if (Summarized) + os << ", which is not memory allocated by "; + else + os << "not memory allocated by "; + + printExpectedAllocName(os, C, DeallocExpr); BugReport *R = new BugReport(*BT_BadFree[*CheckKind], os.str(), N); R->markInteresting(MR); @@ -1527,6 +1552,29 @@ void MallocChecker::ReportBadFree(CheckerContext &C, SVal ArgVal, } } +void MallocChecker::ReportFreeAlloca(CheckerContext &C, SVal ArgVal, + SourceRange Range) const { + + auto CheckKind = getCheckIfTracked(MakeVecFromCK(CK_MallocOptimistic, + CK_MallocPessimistic, + CK_MismatchedDeallocatorChecker), + AF_Alloca); + if (!CheckKind.hasValue()) + return; + + if (ExplodedNode *N = C.generateSink()) { + if (!BT_FreeAlloca[*CheckKind]) + BT_FreeAlloca[*CheckKind].reset( + new BugType(CheckNames[*CheckKind], "Free alloca()", "Memory Error")); + + BugReport *R = new BugReport(*BT_FreeAlloca[*CheckKind], + "Memory allocated by alloca() should not be deallocated", N); + R->markInteresting(ArgVal.getAsRegion()); + R->addRange(Range); + C.emitReport(R); + } +} + void MallocChecker::ReportMismatchedDealloc(CheckerContext &C, SourceRange Range, const Expr *DeallocExpr, |