UnixAPIChecker.cpp revision 219077
1199482Srdivacky//= UnixAPIChecker.h - Checks preconditions for various Unix APIs --*- C++ -*-// 2199482Srdivacky// 3199482Srdivacky// The LLVM Compiler Infrastructure 4199482Srdivacky// 5199482Srdivacky// This file is distributed under the University of Illinois Open Source 6199482Srdivacky// License. See LICENSE.TXT for details. 7199482Srdivacky// 8199482Srdivacky//===----------------------------------------------------------------------===// 9199482Srdivacky// 10199482Srdivacky// This defines UnixAPIChecker, which is an assortment of checks on calls 11212904Sdim// to various, widely used UNIX/Posix functions. 12199482Srdivacky// 13218893Sdim//===----------------------------------------------------------------------===// 14199482Srdivacky 15199482Srdivacky#include "ClangSACheckers.h" 16199482Srdivacky#include "clang/StaticAnalyzer/Core/CheckerV2.h" 17199482Srdivacky#include "clang/StaticAnalyzer/Core/CheckerManager.h" 18199482Srdivacky#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 19218893Sdim#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 20218893Sdim#include "clang/Basic/TargetInfo.h" 21212904Sdim#include "llvm/ADT/Optional.h" 22218893Sdim#include "llvm/ADT/StringSwitch.h" 23221345Sdim#include <fcntl.h> 24199482Srdivacky 25199482Srdivackyusing namespace clang; 26199482Srdivackyusing namespace ento; 27199482Srdivackyusing llvm::Optional; 28199482Srdivacky 29199482Srdivackynamespace { 30218893Sdimclass UnixAPIChecker : public CheckerV2< check::PreStmt<CallExpr> > { 31218893Sdim enum SubChecks { 32218893Sdim OpenFn = 0, 33218893Sdim PthreadOnceFn = 1, 34218893Sdim MallocZero = 2, 35218893Sdim NumChecks 36218893Sdim }; 37218893Sdim 38218893Sdim mutable BugType *BTypes[NumChecks]; 39218893Sdim 40218893Sdimpublic: 41218893Sdim mutable Optional<uint64_t> Val_O_CREAT; 42218893Sdim 43218893Sdimpublic: 44218893Sdim UnixAPIChecker() { memset(BTypes, 0, sizeof(*BTypes) * NumChecks); } 45218893Sdim ~UnixAPIChecker() { 46218893Sdim for (unsigned i=0; i != NumChecks; ++i) 47218893Sdim delete BTypes[i]; 48218893Sdim } 49218893Sdim 50218893Sdim void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; 51218893Sdim}; 52218893Sdim} //end anonymous namespace 53218893Sdim 54218893Sdim//===----------------------------------------------------------------------===// 55218893Sdim// Utility functions. 56218893Sdim//===----------------------------------------------------------------------===// 57218893Sdim 58218893Sdimstatic inline void LazyInitialize(BugType *&BT, const char *name) { 59218893Sdim if (BT) 60218893Sdim return; 61218893Sdim BT = new BugType(name, "Unix API"); 62218893Sdim} 63218893Sdim 64218893Sdim//===----------------------------------------------------------------------===// 65218893Sdim// "open" (man 2 open) 66218893Sdim//===----------------------------------------------------------------------===// 67218893Sdim 68218893Sdimstatic void CheckOpen(CheckerContext &C, const UnixAPIChecker &UC, 69218893Sdim const CallExpr *CE, BugType *&BT) { 70218893Sdim // The definition of O_CREAT is platform specific. We need a better way 71218893Sdim // of querying this information from the checking environment. 72218893Sdim if (!UC.Val_O_CREAT.hasValue()) { 73218893Sdim if (C.getASTContext().Target.getTriple().getVendor() == llvm::Triple::Apple) 74218893Sdim UC.Val_O_CREAT = 0x0200; 75218893Sdim else { 76218893Sdim // FIXME: We need a more general way of getting the O_CREAT value. 77218893Sdim // We could possibly grovel through the preprocessor state, but 78218893Sdim // that would require passing the Preprocessor object to the ExprEngine. 79218893Sdim return; 80218893Sdim } 81199990Srdivacky } 82199482Srdivacky 83199482Srdivacky LazyInitialize(BT, "Improper use of 'open'"); 84199482Srdivacky 85210299Sed // Look at the 'oflags' argument for the O_CREAT flag. 86210299Sed const GRState *state = C.getState(); 87199482Srdivacky 88210299Sed if (CE->getNumArgs() < 2) { 89199482Srdivacky // The frontend should issue a warning for this case, so this is a sanity 90199482Srdivacky // check. 91199482Srdivacky return; 92218893Sdim } 93218893Sdim 94218893Sdim // Now check if oflags has O_CREAT set. 95218893Sdim const Expr *oflagsEx = CE->getArg(1); 96218893Sdim const SVal V = state->getSVal(oflagsEx); 97218893Sdim if (!isa<NonLoc>(V)) { 98218893Sdim // The case where 'V' can be a location can only be due to a bad header, 99218893Sdim // so in this case bail out. 100218893Sdim return; 101218893Sdim } 102218893Sdim NonLoc oflags = cast<NonLoc>(V); 103218893Sdim NonLoc ocreateFlag = 104218893Sdim cast<NonLoc>(C.getSValBuilder().makeIntVal(UC.Val_O_CREAT.getValue(), 105218893Sdim oflagsEx->getType())); 106218893Sdim SVal maskedFlagsUC = C.getSValBuilder().evalBinOpNN(state, BO_And, 107218893Sdim oflags, ocreateFlag, 108218893Sdim oflagsEx->getType()); 109218893Sdim if (maskedFlagsUC.isUnknownOrUndef()) 110218893Sdim return; 111218893Sdim DefinedSVal maskedFlags = cast<DefinedSVal>(maskedFlagsUC); 112218893Sdim 113218893Sdim // Check if maskedFlags is non-zero. 114218893Sdim const GRState *trueState, *falseState; 115218893Sdim llvm::tie(trueState, falseState) = state->assume(maskedFlags); 116218893Sdim 117218893Sdim // Only emit an error if the value of 'maskedFlags' is properly 118218893Sdim // constrained; 119218893Sdim if (!(trueState && !falseState)) 120218893Sdim return; 121218893Sdim 122218893Sdim if (CE->getNumArgs() < 3) { 123218893Sdim ExplodedNode *N = C.generateSink(trueState); 124218893Sdim if (!N) 125199482Srdivacky return; 126199482Srdivacky 127210299Sed EnhancedBugReport *report = 128199482Srdivacky new EnhancedBugReport(*BT, 129199482Srdivacky "Call to 'open' requires a third argument when " 130210299Sed "the 'O_CREAT' flag is set", N); 131199482Srdivacky report->addRange(oflagsEx->getSourceRange()); 132199482Srdivacky C.EmitReport(report); 133199482Srdivacky } 134199482Srdivacky} 135210299Sed 136199482Srdivacky//===----------------------------------------------------------------------===// 137199482Srdivacky// pthread_once 138210299Sed//===----------------------------------------------------------------------===// 139210299Sed 140199482Srdivackystatic void CheckPthreadOnce(CheckerContext &C, const UnixAPIChecker &, 141206275Srdivacky const CallExpr *CE, BugType *&BT) { 142199482Srdivacky 143218893Sdim // This is similar to 'CheckDispatchOnce' in the MacOSXAPIChecker. 144218893Sdim // They can possibly be refactored. 145200583Srdivacky 146199482Srdivacky LazyInitialize(BT, "Improper use of 'pthread_once'"); 147199482Srdivacky 148210299Sed if (CE->getNumArgs() < 1) 149199482Srdivacky return; 150199482Srdivacky 151199482Srdivacky // Check if the first argument is stack allocated. If so, issue a warning 152199482Srdivacky // because that's likely to be bad news. 153199482Srdivacky const GRState *state = C.getState(); 154199482Srdivacky const MemRegion *R = state->getSVal(CE->getArg(0)).getAsRegion(); 155199482Srdivacky if (!R || !isa<StackSpaceRegion>(R->getMemorySpace())) 156199482Srdivacky return; 157199482Srdivacky 158199482Srdivacky ExplodedNode *N = C.generateSink(state); 159199482Srdivacky if (!N) 160199482Srdivacky return; 161199482Srdivacky 162218893Sdim llvm::SmallString<256> S; 163199482Srdivacky llvm::raw_svector_ostream os(S); 164199482Srdivacky os << "Call to 'pthread_once' uses"; 165199482Srdivacky if (const VarRegion *VR = dyn_cast<VarRegion>(R)) 166199482Srdivacky os << " the local variable '" << VR->getDecl()->getName() << '\''; 167199482Srdivacky else 168199482Srdivacky os << " stack allocated memory"; 169210299Sed os << " for the \"control\" value. Using such transient memory for " 170210299Sed "the control value is potentially dangerous."; 171210299Sed if (isa<VarRegion>(R) && isa<StackLocalsSpaceRegion>(R->getMemorySpace())) 172210299Sed os << " Perhaps you intended to declare the variable as 'static'?"; 173218893Sdim 174210299Sed EnhancedBugReport *report = new EnhancedBugReport(*BT, os.str(), N); 175210299Sed report->addRange(CE->getArg(0)->getSourceRange()); 176210299Sed C.EmitReport(report); 177210299Sed} 178210299Sed 179210299Sed//===----------------------------------------------------------------------===// 180210299Sed// "malloc" with allocation size 0 181210299Sed//===----------------------------------------------------------------------===// 182210299Sed 183210299Sed// FIXME: Eventually this should be rolled into the MallocChecker, but this 184210299Sed// check is more basic and is valuable for widespread use. 185210299Sedstatic void CheckMallocZero(CheckerContext &C, const UnixAPIChecker &UC, 186210299Sed const CallExpr *CE, BugType *&BT) { 187210299Sed 188210299Sed // Sanity check that malloc takes one argument. 189210299Sed if (CE->getNumArgs() != 1) 190210299Sed return; 191210299Sed 192210299Sed // Check if the allocation size is 0. 193199482Srdivacky const GRState *state = C.getState(); 194199482Srdivacky SVal argVal = state->getSVal(CE->getArg(0)); 195199482Srdivacky 196199482Srdivacky if (argVal.isUnknownOrUndef()) 197199482Srdivacky return; 198199482Srdivacky 199199482Srdivacky const GRState *trueState, *falseState; 200199482Srdivacky llvm::tie(trueState, falseState) = state->assume(cast<DefinedSVal>(argVal)); 201199482Srdivacky 202199482Srdivacky // Is the value perfectly constrained to zero? 203199482Srdivacky if (falseState && !trueState) { 204199482Srdivacky ExplodedNode *N = C.generateSink(falseState); 205199482Srdivacky if (!N) 206218893Sdim return; 207218893Sdim 208218893Sdim // FIXME: Add reference to CERT advisory, and/or C99 standard in bug 209218893Sdim // output. 210212904Sdim 211218893Sdim LazyInitialize(BT, "Undefined allocation of 0 bytes"); 212218893Sdim 213221345Sdim EnhancedBugReport *report = 214221345Sdim new EnhancedBugReport(*BT, "Call to 'malloc' has an allocation size" 215221345Sdim " of 0 bytes", N); 216221345Sdim report->addRange(CE->getArg(0)->getSourceRange()); 217221345Sdim report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, 218221345Sdim CE->getArg(0)); 219221345Sdim C.EmitReport(report); 220221345Sdim return; 221221345Sdim } 222221345Sdim // Assume the the value is non-zero going forward. 223199482Srdivacky assert(trueState); 224218893Sdim if (trueState != state) { 225218893Sdim C.addTransition(trueState); 226218893Sdim } 227218893Sdim} 228218893Sdim 229218893Sdim//===----------------------------------------------------------------------===// 230218893Sdim// Central dispatch function. 231218893Sdim//===----------------------------------------------------------------------===// 232218893Sdim 233199482Srdivackytypedef void (*SubChecker)(CheckerContext &C, const UnixAPIChecker &UC, 234212904Sdim const CallExpr *CE, BugType *&BT); 235212904Sdimnamespace { 236218893Sdim class SubCheck { 237218893Sdim SubChecker SC; 238199482Srdivacky const UnixAPIChecker *UC; 239199482Srdivacky BugType **BT; 240199482Srdivacky public: 241210299Sed SubCheck(SubChecker sc, const UnixAPIChecker *uc, BugType *& bt) 242212904Sdim : SC(sc), UC(uc), BT(&bt) {} 243210299Sed SubCheck() : SC(NULL), UC(NULL), BT(NULL) {} 244210299Sed 245199482Srdivacky void run(CheckerContext &C, const CallExpr *CE) const { 246199482Srdivacky if (SC) 247199482Srdivacky SC(C, *UC, CE, *BT); 248199482Srdivacky } 249199482Srdivacky }; 250199482Srdivacky} // end anonymous namespace 251199482Srdivacky 252218893Sdimvoid UnixAPIChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const { 253199482Srdivacky // Get the callee. All the functions we care about are C functions 254199482Srdivacky // with simple identifiers. 255199482Srdivacky const GRState *state = C.getState(); 256199482Srdivacky const Expr *Callee = CE->getCallee(); 257199482Srdivacky const FunctionTextRegion *Fn = 258199482Srdivacky dyn_cast_or_null<FunctionTextRegion>(state->getSVal(Callee).getAsRegion()); 259199482Srdivacky 260199482Srdivacky if (!Fn) 261221345Sdim return; 262221345Sdim 263221345Sdim const IdentifierInfo *FI = Fn->getDecl()->getIdentifier(); 264221345Sdim if (!FI) 265199482Srdivacky return; 266199482Srdivacky 267199482Srdivacky const SubCheck &SC = 268210299Sed llvm::StringSwitch<SubCheck>(FI->getName()) 269199482Srdivacky .Case("open", 270199482Srdivacky SubCheck(CheckOpen, this, BTypes[OpenFn])) 271199482Srdivacky .Case("pthread_once", 272199482Srdivacky SubCheck(CheckPthreadOnce, this, BTypes[PthreadOnceFn])) 273199482Srdivacky .Case("malloc", 274199482Srdivacky SubCheck(CheckMallocZero, this, BTypes[MallocZero])) 275199482Srdivacky .Default(SubCheck()); 276199482Srdivacky 277199482Srdivacky SC.run(C, CE); 278199482Srdivacky} 279199482Srdivacky 280199482Srdivacky//===----------------------------------------------------------------------===// 281199482Srdivacky// Registration. 282199482Srdivacky//===----------------------------------------------------------------------===// 283199482Srdivacky 284199482Srdivackyvoid ento::registerUnixAPIChecker(CheckerManager &mgr) { 285206275Srdivacky mgr.registerChecker<UnixAPIChecker>(); 286199482Srdivacky} 287199482Srdivacky