UnixAPIChecker.cpp revision 224145
1218887Sdim//= UnixAPIChecker.h - Checks preconditions for various Unix APIs --*- C++ -*-// 2218887Sdim// 3218887Sdim// The LLVM Compiler Infrastructure 4218887Sdim// 5218887Sdim// This file is distributed under the University of Illinois Open Source 6218887Sdim// License. See LICENSE.TXT for details. 7218887Sdim// 8218887Sdim//===----------------------------------------------------------------------===// 9218887Sdim// 10218887Sdim// This defines UnixAPIChecker, which is an assortment of checks on calls 11218887Sdim// to various, widely used UNIX/Posix functions. 12218887Sdim// 13218887Sdim//===----------------------------------------------------------------------===// 14218887Sdim 15218887Sdim#include "ClangSACheckers.h" 16221345Sdim#include "clang/StaticAnalyzer/Core/Checker.h" 17218887Sdim#include "clang/StaticAnalyzer/Core/CheckerManager.h" 18219077Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 19218887Sdim#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 20219077Sdim#include "clang/Basic/TargetInfo.h" 21218887Sdim#include "llvm/ADT/Optional.h" 22218887Sdim#include "llvm/ADT/StringSwitch.h" 23218887Sdim#include <fcntl.h> 24218887Sdim 25218887Sdimusing namespace clang; 26218887Sdimusing namespace ento; 27218887Sdimusing llvm::Optional; 28218887Sdim 29218887Sdimnamespace { 30221345Sdimclass UnixAPIChecker : public Checker< check::PreStmt<CallExpr> > { 31224145Sdim mutable llvm::OwningPtr<BugType> BT_open, BT_pthreadOnce, BT_mallocZero; 32219077Sdim mutable Optional<uint64_t> Val_O_CREAT; 33218887Sdim 34218887Sdimpublic: 35224145Sdim void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; 36218887Sdim 37224145Sdim void CheckOpen(CheckerContext &C, const CallExpr *CE) const; 38224145Sdim void CheckPthreadOnce(CheckerContext &C, const CallExpr *CE) const; 39224145Sdim void CheckMallocZero(CheckerContext &C, const CallExpr *CE) const; 40224145Sdim 41224145Sdim typedef void (UnixAPIChecker::*SubChecker)(CheckerContext &, 42224145Sdim const CallExpr *) const; 43218887Sdim}; 44218887Sdim} //end anonymous namespace 45218887Sdim 46218887Sdim//===----------------------------------------------------------------------===// 47218887Sdim// Utility functions. 48218887Sdim//===----------------------------------------------------------------------===// 49218887Sdim 50224145Sdimstatic inline void LazyInitialize(llvm::OwningPtr<BugType> &BT, 51224145Sdim const char *name) { 52218887Sdim if (BT) 53218887Sdim return; 54224145Sdim BT.reset(new BugType(name, "Unix API")); 55218887Sdim} 56218887Sdim 57218887Sdim//===----------------------------------------------------------------------===// 58218887Sdim// "open" (man 2 open) 59218887Sdim//===----------------------------------------------------------------------===// 60218887Sdim 61224145Sdimvoid UnixAPIChecker::CheckOpen(CheckerContext &C, const CallExpr *CE) const { 62218887Sdim // The definition of O_CREAT is platform specific. We need a better way 63218887Sdim // of querying this information from the checking environment. 64224145Sdim if (!Val_O_CREAT.hasValue()) { 65218887Sdim if (C.getASTContext().Target.getTriple().getVendor() == llvm::Triple::Apple) 66224145Sdim Val_O_CREAT = 0x0200; 67218887Sdim else { 68218887Sdim // FIXME: We need a more general way of getting the O_CREAT value. 69218887Sdim // We could possibly grovel through the preprocessor state, but 70218887Sdim // that would require passing the Preprocessor object to the ExprEngine. 71218887Sdim return; 72218887Sdim } 73218887Sdim } 74218887Sdim 75218887Sdim // Look at the 'oflags' argument for the O_CREAT flag. 76218887Sdim const GRState *state = C.getState(); 77218887Sdim 78218887Sdim if (CE->getNumArgs() < 2) { 79218887Sdim // The frontend should issue a warning for this case, so this is a sanity 80218887Sdim // check. 81218887Sdim return; 82218887Sdim } 83218887Sdim 84218887Sdim // Now check if oflags has O_CREAT set. 85218887Sdim const Expr *oflagsEx = CE->getArg(1); 86218887Sdim const SVal V = state->getSVal(oflagsEx); 87218887Sdim if (!isa<NonLoc>(V)) { 88218887Sdim // The case where 'V' can be a location can only be due to a bad header, 89218887Sdim // so in this case bail out. 90218887Sdim return; 91218887Sdim } 92218887Sdim NonLoc oflags = cast<NonLoc>(V); 93218887Sdim NonLoc ocreateFlag = 94224145Sdim cast<NonLoc>(C.getSValBuilder().makeIntVal(Val_O_CREAT.getValue(), 95218887Sdim oflagsEx->getType())); 96218887Sdim SVal maskedFlagsUC = C.getSValBuilder().evalBinOpNN(state, BO_And, 97218887Sdim oflags, ocreateFlag, 98218887Sdim oflagsEx->getType()); 99218887Sdim if (maskedFlagsUC.isUnknownOrUndef()) 100218887Sdim return; 101218887Sdim DefinedSVal maskedFlags = cast<DefinedSVal>(maskedFlagsUC); 102218887Sdim 103218887Sdim // Check if maskedFlags is non-zero. 104218887Sdim const GRState *trueState, *falseState; 105218887Sdim llvm::tie(trueState, falseState) = state->assume(maskedFlags); 106218887Sdim 107218887Sdim // Only emit an error if the value of 'maskedFlags' is properly 108218887Sdim // constrained; 109218887Sdim if (!(trueState && !falseState)) 110218887Sdim return; 111218887Sdim 112218887Sdim if (CE->getNumArgs() < 3) { 113218887Sdim ExplodedNode *N = C.generateSink(trueState); 114218887Sdim if (!N) 115218887Sdim return; 116218887Sdim 117224145Sdim LazyInitialize(BT_open, "Improper use of 'open'"); 118224145Sdim 119224145Sdim RangedBugReport *report = 120224145Sdim new RangedBugReport(*BT_open, 121218887Sdim "Call to 'open' requires a third argument when " 122218887Sdim "the 'O_CREAT' flag is set", N); 123218887Sdim report->addRange(oflagsEx->getSourceRange()); 124218887Sdim C.EmitReport(report); 125218887Sdim } 126218887Sdim} 127218887Sdim 128218887Sdim//===----------------------------------------------------------------------===// 129218887Sdim// pthread_once 130218887Sdim//===----------------------------------------------------------------------===// 131218887Sdim 132224145Sdimvoid UnixAPIChecker::CheckPthreadOnce(CheckerContext &C, 133224145Sdim const CallExpr *CE) const { 134218887Sdim 135218887Sdim // This is similar to 'CheckDispatchOnce' in the MacOSXAPIChecker. 136218887Sdim // They can possibly be refactored. 137218887Sdim 138218887Sdim if (CE->getNumArgs() < 1) 139218887Sdim return; 140218887Sdim 141218887Sdim // Check if the first argument is stack allocated. If so, issue a warning 142218887Sdim // because that's likely to be bad news. 143218887Sdim const GRState *state = C.getState(); 144218887Sdim const MemRegion *R = state->getSVal(CE->getArg(0)).getAsRegion(); 145218887Sdim if (!R || !isa<StackSpaceRegion>(R->getMemorySpace())) 146218887Sdim return; 147218887Sdim 148218887Sdim ExplodedNode *N = C.generateSink(state); 149218887Sdim if (!N) 150218887Sdim return; 151218887Sdim 152218887Sdim llvm::SmallString<256> S; 153218887Sdim llvm::raw_svector_ostream os(S); 154218887Sdim os << "Call to 'pthread_once' uses"; 155218887Sdim if (const VarRegion *VR = dyn_cast<VarRegion>(R)) 156218887Sdim os << " the local variable '" << VR->getDecl()->getName() << '\''; 157218887Sdim else 158218887Sdim os << " stack allocated memory"; 159218887Sdim os << " for the \"control\" value. Using such transient memory for " 160218887Sdim "the control value is potentially dangerous."; 161218887Sdim if (isa<VarRegion>(R) && isa<StackLocalsSpaceRegion>(R->getMemorySpace())) 162218887Sdim os << " Perhaps you intended to declare the variable as 'static'?"; 163218887Sdim 164224145Sdim LazyInitialize(BT_pthreadOnce, "Improper use of 'pthread_once'"); 165224145Sdim 166224145Sdim RangedBugReport *report = new RangedBugReport(*BT_pthreadOnce, os.str(), N); 167218887Sdim report->addRange(CE->getArg(0)->getSourceRange()); 168218887Sdim C.EmitReport(report); 169218887Sdim} 170218887Sdim 171218887Sdim//===----------------------------------------------------------------------===// 172218887Sdim// "malloc" with allocation size 0 173218887Sdim//===----------------------------------------------------------------------===// 174218887Sdim 175218887Sdim// FIXME: Eventually this should be rolled into the MallocChecker, but this 176218887Sdim// check is more basic and is valuable for widespread use. 177224145Sdimvoid UnixAPIChecker::CheckMallocZero(CheckerContext &C, 178224145Sdim const CallExpr *CE) const { 179218887Sdim 180218887Sdim // Sanity check that malloc takes one argument. 181218887Sdim if (CE->getNumArgs() != 1) 182218887Sdim return; 183218887Sdim 184218887Sdim // Check if the allocation size is 0. 185218887Sdim const GRState *state = C.getState(); 186218887Sdim SVal argVal = state->getSVal(CE->getArg(0)); 187218887Sdim 188218887Sdim if (argVal.isUnknownOrUndef()) 189218887Sdim return; 190218887Sdim 191218887Sdim const GRState *trueState, *falseState; 192218887Sdim llvm::tie(trueState, falseState) = state->assume(cast<DefinedSVal>(argVal)); 193218887Sdim 194218887Sdim // Is the value perfectly constrained to zero? 195218887Sdim if (falseState && !trueState) { 196218887Sdim ExplodedNode *N = C.generateSink(falseState); 197218887Sdim if (!N) 198218887Sdim return; 199218887Sdim 200218887Sdim // FIXME: Add reference to CERT advisory, and/or C99 standard in bug 201218887Sdim // output. 202218887Sdim 203224145Sdim LazyInitialize(BT_mallocZero, "Undefined allocation of 0 bytes"); 204218887Sdim 205218887Sdim EnhancedBugReport *report = 206224145Sdim new EnhancedBugReport(*BT_mallocZero, "Call to 'malloc' has an allocation" 207224145Sdim " size of 0 bytes", N); 208218887Sdim report->addRange(CE->getArg(0)->getSourceRange()); 209218887Sdim report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, 210218887Sdim CE->getArg(0)); 211218887Sdim C.EmitReport(report); 212218887Sdim return; 213218887Sdim } 214218887Sdim // Assume the the value is non-zero going forward. 215218887Sdim assert(trueState); 216218887Sdim if (trueState != state) { 217218887Sdim C.addTransition(trueState); 218218887Sdim } 219218887Sdim} 220218887Sdim 221218887Sdim//===----------------------------------------------------------------------===// 222218887Sdim// Central dispatch function. 223218887Sdim//===----------------------------------------------------------------------===// 224218887Sdim 225219077Sdimvoid UnixAPIChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const { 226218887Sdim // Get the callee. All the functions we care about are C functions 227218887Sdim // with simple identifiers. 228218887Sdim const GRState *state = C.getState(); 229218887Sdim const Expr *Callee = CE->getCallee(); 230224145Sdim const FunctionDecl *Fn = state->getSVal(Callee).getAsFunctionDecl(); 231218887Sdim 232218887Sdim if (!Fn) 233218887Sdim return; 234218887Sdim 235224145Sdim const IdentifierInfo *FI = Fn->getIdentifier(); 236218887Sdim if (!FI) 237218887Sdim return; 238218887Sdim 239224145Sdim SubChecker SC = 240224145Sdim llvm::StringSwitch<SubChecker>(FI->getName()) 241224145Sdim .Case("open", &UnixAPIChecker::CheckOpen) 242224145Sdim .Case("pthread_once", &UnixAPIChecker::CheckPthreadOnce) 243224145Sdim .Case("malloc", &UnixAPIChecker::CheckMallocZero) 244224145Sdim .Default(NULL); 245218887Sdim 246224145Sdim if (SC) 247224145Sdim (this->*SC)(C, CE); 248218887Sdim} 249219077Sdim 250219077Sdim//===----------------------------------------------------------------------===// 251219077Sdim// Registration. 252219077Sdim//===----------------------------------------------------------------------===// 253219077Sdim 254219077Sdimvoid ento::registerUnixAPIChecker(CheckerManager &mgr) { 255219077Sdim mgr.registerChecker<UnixAPIChecker>(); 256219077Sdim} 257