diff options
author | Jordan Rose <jordan_rose@apple.com> | 2014-03-26 17:05:46 +0000 |
---|---|---|
committer | Jordan Rose <jordan_rose@apple.com> | 2014-03-26 17:05:46 +0000 |
commit | 6b33c6f2349f74f0a70d5447e2c3eb0913aba902 (patch) | |
tree | c07ff98962c8fc619c7d6ac5ee0b40eec0871894 /clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp | |
parent | 5df175cec8ed9ae694fd75ec39c25e9d2eb693f1 (diff) | |
download | bcm5719-llvm-6b33c6f2349f74f0a70d5447e2c3eb0913aba902.tar.gz bcm5719-llvm-6b33c6f2349f74f0a70d5447e2c3eb0913aba902.zip |
[analyzer] Handle the M_ZERO and __GFP_ZERO flags in kernel mallocs.
Add M_ZERO awareness to malloc() static analysis in Clang for FreeBSD,
NetBSD, and OpenBSD in a similar fashion to O_CREAT for open(2).
These systems have a three-argument malloc() in the kernel where the
third argument contains flags; the M_ZERO flag will zero-initialize the
allocated buffer.
This should reduce the number of false positives when running static
analysis on BSD kernels.
Additionally, add kmalloc() (Linux kernel malloc()) and treat __GFP_ZERO
like M_ZERO on Linux.
Future work involves a better method of checking for named flags without
hardcoding values.
Patch by Conrad Meyer, with minor modifications by me.
llvm-svn: 204832
Diffstat (limited to 'clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp')
-rw-r--r-- | clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp | 119 |
1 files changed, 114 insertions, 5 deletions
diff --git a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp index f64c3925a71..f0d1924df59 100644 --- a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -16,6 +16,7 @@ #include "InterCheckerAPI.h" #include "clang/AST/Attr.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" @@ -157,7 +158,8 @@ class MallocChecker : public Checker<check::DeadSymbols, { public: MallocChecker() : II_malloc(0), II_free(0), II_realloc(0), II_calloc(0), - II_valloc(0), II_reallocf(0), II_strndup(0), II_strdup(0) {} + II_valloc(0), II_reallocf(0), II_strndup(0), II_strdup(0), + II_kmalloc(0) {} /// In pessimistic mode, the checker assumes that it does not know which /// functions might free the memory. @@ -207,7 +209,9 @@ private: 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_valloc, *II_reallocf, *II_strndup, *II_strdup, + *II_kmalloc; + mutable Optional<uint64_t> KernelZeroFlagVal; void initIdentifierInfo(ASTContext &C) const; @@ -253,6 +257,12 @@ private: ProgramStateRef State, AllocationFamily Family = AF_Malloc); + // Check if this malloc() for special flags. At present that means M_ZERO or + // __GFP_ZERO (in which case, treat it like calloc). + llvm::Optional<ProgramStateRef> + performKernelMalloc(const CallExpr *CE, CheckerContext &C, + const ProgramStateRef &State) const; + /// Update the RefState to reflect the new memory allocation. static ProgramStateRef MallocUpdateRefState(CheckerContext &C, const Expr *E, ProgramStateRef State, @@ -482,6 +492,7 @@ void MallocChecker::initIdentifierInfo(ASTContext &Ctx) const { II_valloc = &Ctx.Idents.get("valloc"); II_strdup = &Ctx.Idents.get("strdup"); II_strndup = &Ctx.Idents.get("strndup"); + II_kmalloc = &Ctx.Idents.get("kmalloc"); } bool MallocChecker::isMemFunction(const FunctionDecl *FD, ASTContext &C) const { @@ -508,7 +519,7 @@ bool MallocChecker::isAllocationFunction(const FunctionDecl *FD, if (FunI == II_malloc || FunI == II_realloc || FunI == II_reallocf || FunI == II_calloc || FunI == II_valloc || - FunI == II_strdup || FunI == II_strndup) + FunI == II_strdup || FunI == II_strndup || FunI == II_kmalloc) return true; } @@ -572,10 +583,88 @@ bool MallocChecker::isStandardNewDelete(const FunctionDecl *FD, return true; } +llvm::Optional<ProgramStateRef> MallocChecker::performKernelMalloc( + const CallExpr *CE, CheckerContext &C, const ProgramStateRef &State) const { + // 3-argument malloc(), as commonly used in {Free,Net,Open}BSD Kernels: + // + // void *malloc(unsigned long size, struct malloc_type *mtp, int flags); + // + // One of the possible flags is M_ZERO, which means 'give me back an + // allocation which is already zeroed', like calloc. + + // 2-argument kmalloc(), as used in the Linux kernel: + // + // void *kmalloc(size_t size, gfp_t flags); + // + // Has the similar flag value __GFP_ZERO. + + // This logic is largely cloned from O_CREAT in UnixAPIChecker, maybe some + // code could be shared. + + ASTContext &Ctx = C.getASTContext(); + llvm::Triple::OSType OS = Ctx.getTargetInfo().getTriple().getOS(); + + if (!KernelZeroFlagVal.hasValue()) { + if (OS == llvm::Triple::FreeBSD) + KernelZeroFlagVal = 0x0100; + else if (OS == llvm::Triple::NetBSD) + KernelZeroFlagVal = 0x0002; + else if (OS == llvm::Triple::OpenBSD) + KernelZeroFlagVal = 0x0008; + else if (OS == llvm::Triple::Linux) + // __GFP_ZERO + KernelZeroFlagVal = 0x8000; + else + // FIXME: We need a more general way of getting the M_ZERO value. + // See also: O_CREAT in UnixAPIChecker.cpp. + + // Fall back to normal malloc behavior on platforms where we don't + // know M_ZERO. + return None; + } + + // We treat the last argument as the flags argument, and callers fall-back to + // normal malloc on a None return. This works for the FreeBSD kernel malloc + // as well as Linux kmalloc. + if (CE->getNumArgs() < 2) + return None; + + const Expr *FlagsEx = CE->getArg(CE->getNumArgs() - 1); + const SVal V = State->getSVal(FlagsEx, C.getLocationContext()); + if (!V.getAs<NonLoc>()) { + // The case where 'V' can be a location can only be due to a bad header, + // so in this case bail out. + return None; + } + + NonLoc Flags = V.castAs<NonLoc>(); + NonLoc ZeroFlag = C.getSValBuilder() + .makeIntVal(KernelZeroFlagVal.getValue(), FlagsEx->getType()) + .castAs<NonLoc>(); + SVal MaskedFlagsUC = C.getSValBuilder().evalBinOpNN(State, BO_And, + Flags, ZeroFlag, + FlagsEx->getType()); + if (MaskedFlagsUC.isUnknownOrUndef()) + return None; + DefinedSVal MaskedFlags = MaskedFlagsUC.castAs<DefinedSVal>(); + + // Check if maskedFlags is non-zero. + ProgramStateRef TrueState, FalseState; + std::tie(TrueState, FalseState) = State->assume(MaskedFlags); + + // If M_ZERO is set, treat this like calloc (initialized). + if (TrueState && !FalseState) { + SVal ZeroVal = C.getSValBuilder().makeZeroVal(Ctx.CharTy); + return MallocMemAux(C, CE, CE->getArg(0), ZeroVal, TrueState); + } + + return None; +} + void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const { if (C.wasInlined) return; - + const FunctionDecl *FD = C.getCalleeDecl(CE); if (!FD) return; @@ -587,7 +676,27 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const { initIdentifierInfo(C.getASTContext()); IdentifierInfo *FunI = FD->getIdentifier(); - if (FunI == II_malloc || FunI == II_valloc) { + if (FunI == II_malloc) { + if (CE->getNumArgs() < 1) + return; + if (CE->getNumArgs() < 3) { + State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State); + } else if (CE->getNumArgs() == 3) { + llvm::Optional<ProgramStateRef> MaybeState = + performKernelMalloc(CE, C, State); + if (MaybeState.hasValue()) + State = MaybeState.getValue(); + else + State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State); + } + } else if (FunI == II_kmalloc) { + llvm::Optional<ProgramStateRef> MaybeState = + performKernelMalloc(CE, C, State); + if (MaybeState.hasValue()) + State = MaybeState.getValue(); + else + State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State); + } else if (FunI == II_valloc) { if (CE->getNumArgs() < 1) return; State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State); |