UnixAPIChecker.cpp revision 18c66fdc3c4008d335885695fe36fb5353c5f672
1//= UnixAPIChecker.h - Checks preconditions for various Unix APIs --*- C++ -*-// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file is distributed under the University of Illinois Open Source 6// License. See LICENSE.TXT for details. 7// 8//===----------------------------------------------------------------------===// 9// 10// This defines UnixAPIChecker, which is an assortment of checks on calls 11// to various, widely used UNIX/Posix functions. 12// 13//===----------------------------------------------------------------------===// 14 15#include "ClangSACheckers.h" 16#include "clang/StaticAnalyzer/Core/Checker.h" 17#include "clang/StaticAnalyzer/Core/CheckerManager.h" 18#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 19#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 20#include "clang/Basic/TargetInfo.h" 21#include "llvm/ADT/Optional.h" 22#include "llvm/ADT/StringSwitch.h" 23#include <fcntl.h> 24 25using namespace clang; 26using namespace ento; 27using llvm::Optional; 28 29namespace { 30class UnixAPIChecker : public Checker< check::PreStmt<CallExpr> > { 31 mutable llvm::OwningPtr<BugType> BT_open, BT_pthreadOnce, BT_mallocZero; 32 mutable Optional<uint64_t> Val_O_CREAT; 33 34public: 35 void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; 36 37 void CheckOpen(CheckerContext &C, const CallExpr *CE) const; 38 void CheckPthreadOnce(CheckerContext &C, const CallExpr *CE) const; 39 void CheckMallocZero(CheckerContext &C, const CallExpr *CE) const; 40 41 typedef void (UnixAPIChecker::*SubChecker)(CheckerContext &, 42 const CallExpr *) const; 43}; 44} //end anonymous namespace 45 46//===----------------------------------------------------------------------===// 47// Utility functions. 48//===----------------------------------------------------------------------===// 49 50static inline void LazyInitialize(llvm::OwningPtr<BugType> &BT, 51 const char *name) { 52 if (BT) 53 return; 54 BT.reset(new BugType(name, "Unix API")); 55} 56 57//===----------------------------------------------------------------------===// 58// "open" (man 2 open) 59//===----------------------------------------------------------------------===// 60 61void UnixAPIChecker::CheckOpen(CheckerContext &C, const CallExpr *CE) const { 62 // The definition of O_CREAT is platform specific. We need a better way 63 // of querying this information from the checking environment. 64 if (!Val_O_CREAT.hasValue()) { 65 if (C.getASTContext().Target.getTriple().getVendor() == llvm::Triple::Apple) 66 Val_O_CREAT = 0x0200; 67 else { 68 // FIXME: We need a more general way of getting the O_CREAT value. 69 // We could possibly grovel through the preprocessor state, but 70 // that would require passing the Preprocessor object to the ExprEngine. 71 return; 72 } 73 } 74 75 // Look at the 'oflags' argument for the O_CREAT flag. 76 const ProgramState *state = C.getState(); 77 78 if (CE->getNumArgs() < 2) { 79 // The frontend should issue a warning for this case, so this is a sanity 80 // check. 81 return; 82 } 83 84 // Now check if oflags has O_CREAT set. 85 const Expr *oflagsEx = CE->getArg(1); 86 const SVal V = state->getSVal(oflagsEx); 87 if (!isa<NonLoc>(V)) { 88 // The case where 'V' can be a location can only be due to a bad header, 89 // so in this case bail out. 90 return; 91 } 92 NonLoc oflags = cast<NonLoc>(V); 93 NonLoc ocreateFlag = 94 cast<NonLoc>(C.getSValBuilder().makeIntVal(Val_O_CREAT.getValue(), 95 oflagsEx->getType())); 96 SVal maskedFlagsUC = C.getSValBuilder().evalBinOpNN(state, BO_And, 97 oflags, ocreateFlag, 98 oflagsEx->getType()); 99 if (maskedFlagsUC.isUnknownOrUndef()) 100 return; 101 DefinedSVal maskedFlags = cast<DefinedSVal>(maskedFlagsUC); 102 103 // Check if maskedFlags is non-zero. 104 const ProgramState *trueState, *falseState; 105 llvm::tie(trueState, falseState) = state->assume(maskedFlags); 106 107 // Only emit an error if the value of 'maskedFlags' is properly 108 // constrained; 109 if (!(trueState && !falseState)) 110 return; 111 112 if (CE->getNumArgs() < 3) { 113 ExplodedNode *N = C.generateSink(trueState); 114 if (!N) 115 return; 116 117 LazyInitialize(BT_open, "Improper use of 'open'"); 118 119 RangedBugReport *report = 120 new RangedBugReport(*BT_open, 121 "Call to 'open' requires a third argument when " 122 "the 'O_CREAT' flag is set", N); 123 report->addRange(oflagsEx->getSourceRange()); 124 C.EmitReport(report); 125 } 126} 127 128//===----------------------------------------------------------------------===// 129// pthread_once 130//===----------------------------------------------------------------------===// 131 132void UnixAPIChecker::CheckPthreadOnce(CheckerContext &C, 133 const CallExpr *CE) const { 134 135 // This is similar to 'CheckDispatchOnce' in the MacOSXAPIChecker. 136 // They can possibly be refactored. 137 138 if (CE->getNumArgs() < 1) 139 return; 140 141 // Check if the first argument is stack allocated. If so, issue a warning 142 // because that's likely to be bad news. 143 const ProgramState *state = C.getState(); 144 const MemRegion *R = state->getSVal(CE->getArg(0)).getAsRegion(); 145 if (!R || !isa<StackSpaceRegion>(R->getMemorySpace())) 146 return; 147 148 ExplodedNode *N = C.generateSink(state); 149 if (!N) 150 return; 151 152 llvm::SmallString<256> S; 153 llvm::raw_svector_ostream os(S); 154 os << "Call to 'pthread_once' uses"; 155 if (const VarRegion *VR = dyn_cast<VarRegion>(R)) 156 os << " the local variable '" << VR->getDecl()->getName() << '\''; 157 else 158 os << " stack allocated memory"; 159 os << " for the \"control\" value. Using such transient memory for " 160 "the control value is potentially dangerous."; 161 if (isa<VarRegion>(R) && isa<StackLocalsSpaceRegion>(R->getMemorySpace())) 162 os << " Perhaps you intended to declare the variable as 'static'?"; 163 164 LazyInitialize(BT_pthreadOnce, "Improper use of 'pthread_once'"); 165 166 RangedBugReport *report = new RangedBugReport(*BT_pthreadOnce, os.str(), N); 167 report->addRange(CE->getArg(0)->getSourceRange()); 168 C.EmitReport(report); 169} 170 171//===----------------------------------------------------------------------===// 172// "malloc" with allocation size 0 173//===----------------------------------------------------------------------===// 174 175// FIXME: Eventually this should be rolled into the MallocChecker, but this 176// check is more basic and is valuable for widespread use. 177void UnixAPIChecker::CheckMallocZero(CheckerContext &C, 178 const CallExpr *CE) const { 179 180 // Sanity check that malloc takes one argument. 181 if (CE->getNumArgs() != 1) 182 return; 183 184 // Check if the allocation size is 0. 185 const ProgramState *state = C.getState(); 186 SVal argVal = state->getSVal(CE->getArg(0)); 187 188 if (argVal.isUnknownOrUndef()) 189 return; 190 191 const ProgramState *trueState, *falseState; 192 llvm::tie(trueState, falseState) = state->assume(cast<DefinedSVal>(argVal)); 193 194 // Is the value perfectly constrained to zero? 195 if (falseState && !trueState) { 196 ExplodedNode *N = C.generateSink(falseState); 197 if (!N) 198 return; 199 200 // FIXME: Add reference to CERT advisory, and/or C99 standard in bug 201 // output. 202 203 LazyInitialize(BT_mallocZero, "Undefined allocation of 0 bytes"); 204 205 EnhancedBugReport *report = 206 new EnhancedBugReport(*BT_mallocZero, "Call to 'malloc' has an allocation" 207 " size of 0 bytes", N); 208 report->addRange(CE->getArg(0)->getSourceRange()); 209 report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, 210 CE->getArg(0)); 211 C.EmitReport(report); 212 return; 213 } 214 // Assume the the value is non-zero going forward. 215 assert(trueState); 216 if (trueState != state) { 217 C.addTransition(trueState); 218 } 219} 220 221//===----------------------------------------------------------------------===// 222// Central dispatch function. 223//===----------------------------------------------------------------------===// 224 225void UnixAPIChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const { 226 // Get the callee. All the functions we care about are C functions 227 // with simple identifiers. 228 const ProgramState *state = C.getState(); 229 const Expr *Callee = CE->getCallee(); 230 const FunctionDecl *Fn = state->getSVal(Callee).getAsFunctionDecl(); 231 232 if (!Fn) 233 return; 234 235 const IdentifierInfo *FI = Fn->getIdentifier(); 236 if (!FI) 237 return; 238 239 SubChecker SC = 240 llvm::StringSwitch<SubChecker>(FI->getName()) 241 .Case("open", &UnixAPIChecker::CheckOpen) 242 .Case("pthread_once", &UnixAPIChecker::CheckPthreadOnce) 243 .Case("malloc", &UnixAPIChecker::CheckMallocZero) 244 .Default(NULL); 245 246 if (SC) 247 (this->*SC)(C, CE); 248} 249 250//===----------------------------------------------------------------------===// 251// Registration. 252//===----------------------------------------------------------------------===// 253 254void ento::registerUnixAPIChecker(CheckerManager &mgr) { 255 mgr.registerChecker<UnixAPIChecker>(); 256} 257