UnixAPIChecker.cpp revision 5eca482fe895ea57bc82410222e6426c09e63284
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 CheckCallocZero(CheckerContext &C, const CallExpr *CE) const; 40 void CheckMallocZero(CheckerContext &C, const CallExpr *CE) const; 41 void CheckReallocZero(CheckerContext &C, const CallExpr *CE) const; 42 43 typedef void (UnixAPIChecker::*SubChecker)(CheckerContext &, 44 const CallExpr *) const; 45private: 46 bool ReportZeroByteAllocation(CheckerContext &C, 47 const ProgramState *falseState, 48 const Expr *arg, 49 const char *fn_name) const; 50}; 51} //end anonymous namespace 52 53//===----------------------------------------------------------------------===// 54// Utility functions. 55//===----------------------------------------------------------------------===// 56 57static inline void LazyInitialize(llvm::OwningPtr<BugType> &BT, 58 const char *name) { 59 if (BT) 60 return; 61 BT.reset(new BugType(name, "Unix API")); 62} 63 64//===----------------------------------------------------------------------===// 65// "open" (man 2 open) 66//===----------------------------------------------------------------------===// 67 68void UnixAPIChecker::CheckOpen(CheckerContext &C, const CallExpr *CE) const { 69 // The definition of O_CREAT is platform specific. We need a better way 70 // of querying this information from the checking environment. 71 if (!Val_O_CREAT.hasValue()) { 72 if (C.getASTContext().getTargetInfo().getTriple().getVendor() 73 == llvm::Triple::Apple) 74 Val_O_CREAT = 0x0200; 75 else { 76 // FIXME: We need a more general way of getting the O_CREAT value. 77 // We could possibly grovel through the preprocessor state, but 78 // that would require passing the Preprocessor object to the ExprEngine. 79 return; 80 } 81 } 82 83 // Look at the 'oflags' argument for the O_CREAT flag. 84 const ProgramState *state = C.getState(); 85 86 if (CE->getNumArgs() < 2) { 87 // The frontend should issue a warning for this case, so this is a sanity 88 // check. 89 return; 90 } 91 92 // Now check if oflags has O_CREAT set. 93 const Expr *oflagsEx = CE->getArg(1); 94 const SVal V = state->getSVal(oflagsEx, C.getLocationContext()); 95 if (!isa<NonLoc>(V)) { 96 // The case where 'V' can be a location can only be due to a bad header, 97 // so in this case bail out. 98 return; 99 } 100 NonLoc oflags = cast<NonLoc>(V); 101 NonLoc ocreateFlag = 102 cast<NonLoc>(C.getSValBuilder().makeIntVal(Val_O_CREAT.getValue(), 103 oflagsEx->getType())); 104 SVal maskedFlagsUC = C.getSValBuilder().evalBinOpNN(state, BO_And, 105 oflags, ocreateFlag, 106 oflagsEx->getType()); 107 if (maskedFlagsUC.isUnknownOrUndef()) 108 return; 109 DefinedSVal maskedFlags = cast<DefinedSVal>(maskedFlagsUC); 110 111 // Check if maskedFlags is non-zero. 112 const ProgramState *trueState, *falseState; 113 llvm::tie(trueState, falseState) = state->assume(maskedFlags); 114 115 // Only emit an error if the value of 'maskedFlags' is properly 116 // constrained; 117 if (!(trueState && !falseState)) 118 return; 119 120 if (CE->getNumArgs() < 3) { 121 ExplodedNode *N = C.generateSink(trueState); 122 if (!N) 123 return; 124 125 LazyInitialize(BT_open, "Improper use of 'open'"); 126 127 BugReport *report = 128 new BugReport(*BT_open, 129 "Call to 'open' requires a third argument when " 130 "the 'O_CREAT' flag is set", N); 131 report->addRange(oflagsEx->getSourceRange()); 132 C.EmitReport(report); 133 } 134} 135 136//===----------------------------------------------------------------------===// 137// pthread_once 138//===----------------------------------------------------------------------===// 139 140void UnixAPIChecker::CheckPthreadOnce(CheckerContext &C, 141 const CallExpr *CE) const { 142 143 // This is similar to 'CheckDispatchOnce' in the MacOSXAPIChecker. 144 // They can possibly be refactored. 145 146 if (CE->getNumArgs() < 1) 147 return; 148 149 // Check if the first argument is stack allocated. If so, issue a warning 150 // because that's likely to be bad news. 151 const ProgramState *state = C.getState(); 152 const MemRegion *R = 153 state->getSVal(CE->getArg(0), C.getLocationContext()).getAsRegion(); 154 if (!R || !isa<StackSpaceRegion>(R->getMemorySpace())) 155 return; 156 157 ExplodedNode *N = C.generateSink(state); 158 if (!N) 159 return; 160 161 llvm::SmallString<256> S; 162 llvm::raw_svector_ostream os(S); 163 os << "Call to 'pthread_once' uses"; 164 if (const VarRegion *VR = dyn_cast<VarRegion>(R)) 165 os << " the local variable '" << VR->getDecl()->getName() << '\''; 166 else 167 os << " stack allocated memory"; 168 os << " for the \"control\" value. Using such transient memory for " 169 "the control value is potentially dangerous."; 170 if (isa<VarRegion>(R) && isa<StackLocalsSpaceRegion>(R->getMemorySpace())) 171 os << " Perhaps you intended to declare the variable as 'static'?"; 172 173 LazyInitialize(BT_pthreadOnce, "Improper use of 'pthread_once'"); 174 175 BugReport *report = new BugReport(*BT_pthreadOnce, os.str(), N); 176 report->addRange(CE->getArg(0)->getSourceRange()); 177 C.EmitReport(report); 178} 179 180//===----------------------------------------------------------------------===// 181// "calloc", "malloc" and "realloc" with allocation size 0 182//===----------------------------------------------------------------------===// 183 184// Returns true if we try to do a zero byte allocation, false otherwise. 185// Fills in trueState and falseState. 186static bool IsZeroByteAllocation(const ProgramState *state, 187 const SVal argVal, 188 const ProgramState **trueState, 189 const ProgramState **falseState) { 190 llvm::tie(*trueState, *falseState) = state->assume(cast<DefinedSVal>(argVal)); 191 return (*falseState && !*trueState); 192} 193 194// Generates an error report, indicating that the function whose name is given 195// will perform a zero byte allocation. 196// Returns false if an error occured, true otherwise. 197bool UnixAPIChecker::ReportZeroByteAllocation(CheckerContext &C, 198 const ProgramState *falseState, 199 const Expr *arg, 200 const char *fn_name) const { 201 ExplodedNode *N = C.generateSink(falseState); 202 if (!N) 203 return false; 204 205 // FIXME: Add reference to CERT advisory, and/or C99 standard in bug 206 // output. 207 LazyInitialize(BT_mallocZero, "Undefined allocation of 0 bytes"); 208 209 llvm::SmallString<256> S; 210 llvm::raw_svector_ostream os(S); 211 os << "Call to '" << fn_name << "' has an allocation size of 0 bytes"; 212 BugReport *report = new BugReport(*BT_mallocZero, os.str(), N); 213 214 report->addRange(arg->getSourceRange()); 215 report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, arg)); 216 C.EmitReport(report); 217 218 return true; 219} 220 221void UnixAPIChecker::CheckCallocZero(CheckerContext &C, 222 const CallExpr *CE) const { 223 unsigned int nArgs = CE->getNumArgs(); 224 if (nArgs != 2) 225 return; 226 227 const ProgramState *state = C.getState(); 228 const ProgramState *trueState = NULL, *falseState = NULL; 229 230 unsigned int i; 231 for (i = 0; i < nArgs; i++) { 232 const Expr *arg = CE->getArg(i); 233 SVal argVal = state->getSVal(arg, C.getLocationContext()); 234 if (argVal.isUnknownOrUndef()) { 235 if (i == 0) 236 continue; 237 else 238 return; 239 } 240 241 if (IsZeroByteAllocation(state, argVal, &trueState, &falseState)) { 242 if (ReportZeroByteAllocation(C, falseState, arg, "calloc")) 243 return; 244 else if (i == 0) 245 continue; 246 else 247 return; 248 } 249 } 250 251 // Assume the the value is non-zero going forward. 252 assert(trueState); 253 if (trueState != state) 254 C.addTransition(trueState); 255} 256 257// FIXME: Eventually this should be rolled into the MallocChecker, but this 258// check is more basic and is valuable for widespread use. 259void UnixAPIChecker::CheckMallocZero(CheckerContext &C, 260 const CallExpr *CE) const { 261 // Sanity check that malloc takes one argument. 262 if (CE->getNumArgs() != 1) 263 return; 264 265 // Check if the allocation size is 0. 266 const ProgramState *state = C.getState(); 267 const ProgramState *trueState = NULL, *falseState = NULL; 268 const Expr *arg = CE->getArg(0); 269 SVal argVal = state->getSVal(arg, C.getLocationContext()); 270 271 if (argVal.isUnknownOrUndef()) 272 return; 273 274 // Is the value perfectly constrained to zero? 275 if (IsZeroByteAllocation(state, argVal, &trueState, &falseState)) { 276 (void) ReportZeroByteAllocation(C, falseState, arg, "malloc"); 277 return; 278 } 279 // Assume the the value is non-zero going forward. 280 assert(trueState); 281 if (trueState != state) 282 C.addTransition(trueState); 283} 284 285void UnixAPIChecker::CheckReallocZero(CheckerContext &C, 286 const CallExpr *CE) const { 287 if (CE->getNumArgs() != 2) 288 return; 289 290 const ProgramState *state = C.getState(); 291 const ProgramState *trueState = NULL, *falseState = NULL; 292 const Expr *arg = CE->getArg(1); 293 SVal argVal = state->getSVal(arg, C.getLocationContext()); 294 295 if (argVal.isUnknownOrUndef()) 296 return; 297 298 if (IsZeroByteAllocation(state, argVal, &trueState, &falseState)) { 299 ReportZeroByteAllocation(C, falseState, arg, "realloc"); 300 return; 301 } 302 303 // Assume the the value is non-zero going forward. 304 assert(trueState); 305 if (trueState != state) 306 C.addTransition(trueState); 307} 308 309//===----------------------------------------------------------------------===// 310// Central dispatch function. 311//===----------------------------------------------------------------------===// 312 313void UnixAPIChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const { 314 StringRef FName = C.getCalleeName(CE); 315 if (FName.empty()) 316 return; 317 318 SubChecker SC = 319 llvm::StringSwitch<SubChecker>(FName) 320 .Case("open", &UnixAPIChecker::CheckOpen) 321 .Case("pthread_once", &UnixAPIChecker::CheckPthreadOnce) 322 .Case("calloc", &UnixAPIChecker::CheckCallocZero) 323 .Case("malloc", &UnixAPIChecker::CheckMallocZero) 324 .Case("realloc", &UnixAPIChecker::CheckReallocZero) 325 .Default(NULL); 326 327 if (SC) 328 (this->*SC)(C, CE); 329} 330 331//===----------------------------------------------------------------------===// 332// Registration. 333//===----------------------------------------------------------------------===// 334 335void ento::registerUnixAPIChecker(CheckerManager &mgr) { 336 mgr.registerChecker<UnixAPIChecker>(); 337} 338