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 25 using namespace clang; 26 using namespace ento; 27 using llvm::Optional; 28 29 namespace { 30 class 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 34 public: 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; 45 private: 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 57 static 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 68 void 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); 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 140 void 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 = state->getSVal(CE->getArg(0)).getAsRegion(); 153 if (!R || !isa<StackSpaceRegion>(R->getMemorySpace())) 154 return; 155 156 ExplodedNode *N = C.generateSink(state); 157 if (!N) 158 return; 159 160 llvm::SmallString<256> S; 161 llvm::raw_svector_ostream os(S); 162 os << "Call to 'pthread_once' uses"; 163 if (const VarRegion *VR = dyn_cast<VarRegion>(R)) 164 os << " the local variable '" << VR->getDecl()->getName() << '\''; 165 else 166 os << " stack allocated memory"; 167 os << " for the \"control\" value. Using such transient memory for " 168 "the control value is potentially dangerous."; 169 if (isa<VarRegion>(R) && isa<StackLocalsSpaceRegion>(R->getMemorySpace())) 170 os << " Perhaps you intended to declare the variable as 'static'?"; 171 172 LazyInitialize(BT_pthreadOnce, "Improper use of 'pthread_once'"); 173 174 BugReport *report = new BugReport(*BT_pthreadOnce, os.str(), N); 175 report->addRange(CE->getArg(0)->getSourceRange()); 176 C.EmitReport(report); 177 } 178 179 //===----------------------------------------------------------------------===// 180 // "calloc", "malloc" and "realloc" with allocation size 0 181 //===----------------------------------------------------------------------===// 182 183 // Returns true if we try to do a zero byte allocation, false otherwise. 184 // Fills in trueState and falseState. 185 static bool IsZeroByteAllocation(const ProgramState *state, 186 const SVal argVal, 187 const ProgramState **trueState, 188 const ProgramState **falseState) { 189 llvm::tie(*trueState, *falseState) = state->assume(cast<DefinedSVal>(argVal)); 190 return (*falseState && !*trueState); 191 } 192 193 // Generates an error report, indicating that the function whose name is given 194 // will perform a zero byte allocation. 195 // Returns false if an error occured, true otherwise. 196 bool UnixAPIChecker::ReportZeroByteAllocation(CheckerContext &C, 197 const ProgramState *falseState, 198 const Expr *arg, 199 const char *fn_name) const { 200 ExplodedNode *N = C.generateSink(falseState); 201 if (!N) 202 return false; 203 204 // FIXME: Add reference to CERT advisory, and/or C99 standard in bug 205 // output. 206 LazyInitialize(BT_mallocZero, "Undefined allocation of 0 bytes"); 207 208 llvm::SmallString<256> S; 209 llvm::raw_svector_ostream os(S); 210 os << "Call to '" << fn_name << "' has an allocation size of 0 bytes"; 211 BugReport *report = new BugReport(*BT_mallocZero, os.str(), N); 212 213 report->addRange(arg->getSourceRange()); 214 report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, arg)); 215 C.EmitReport(report); 216 217 return true; 218 } 219 220 void UnixAPIChecker::CheckCallocZero(CheckerContext &C, 221 const CallExpr *CE) const { 222 unsigned int nArgs = CE->getNumArgs(); 223 if (nArgs != 2) 224 return; 225 226 const ProgramState *state = C.getState(); 227 const ProgramState *trueState = NULL, *falseState = NULL; 228 229 unsigned int i; 230 for (i = 0; i < nArgs; i++) { 231 const Expr *arg = CE->getArg(i); 232 SVal argVal = state->getSVal(arg); 233 if (argVal.isUnknownOrUndef()) { 234 if (i == 0) 235 continue; 236 else 237 return; 238 } 239 240 if (IsZeroByteAllocation(state, argVal, &trueState, &falseState)) { 241 if (ReportZeroByteAllocation(C, falseState, arg, "calloc")) 242 return; 243 else if (i == 0) 244 continue; 245 else 246 return; 247 } 248 } 249 250 // Assume the the value is non-zero going forward. 251 assert(trueState); 252 if (trueState != state) 253 C.addTransition(trueState); 254 } 255 256 // FIXME: Eventually this should be rolled into the MallocChecker, but this 257 // check is more basic and is valuable for widespread use. 258 void UnixAPIChecker::CheckMallocZero(CheckerContext &C, 259 const CallExpr *CE) const { 260 // Sanity check that malloc takes one argument. 261 if (CE->getNumArgs() != 1) 262 return; 263 264 // Check if the allocation size is 0. 265 const ProgramState *state = C.getState(); 266 const ProgramState *trueState = NULL, *falseState = NULL; 267 const Expr *arg = CE->getArg(0); 268 SVal argVal = state->getSVal(arg); 269 270 if (argVal.isUnknownOrUndef()) 271 return; 272 273 // Is the value perfectly constrained to zero? 274 if (IsZeroByteAllocation(state, argVal, &trueState, &falseState)) { 275 (void) ReportZeroByteAllocation(C, falseState, arg, "malloc"); 276 return; 277 } 278 // Assume the the value is non-zero going forward. 279 assert(trueState); 280 if (trueState != state) 281 C.addTransition(trueState); 282 } 283 284 void UnixAPIChecker::CheckReallocZero(CheckerContext &C, 285 const CallExpr *CE) const { 286 if (CE->getNumArgs() != 2) 287 return; 288 289 const ProgramState *state = C.getState(); 290 const ProgramState *trueState = NULL, *falseState = NULL; 291 const Expr *arg = CE->getArg(1); 292 SVal argVal = state->getSVal(arg); 293 294 if (argVal.isUnknownOrUndef()) 295 return; 296 297 if (IsZeroByteAllocation(state, argVal, &trueState, &falseState)) { 298 ReportZeroByteAllocation(C, falseState, arg, "realloc"); 299 return; 300 } 301 302 // Assume the the value is non-zero going forward. 303 assert(trueState); 304 if (trueState != state) 305 C.addTransition(trueState); 306 } 307 308 //===----------------------------------------------------------------------===// 309 // Central dispatch function. 310 //===----------------------------------------------------------------------===// 311 312 void UnixAPIChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const { 313 StringRef FName = C.getCalleeName(CE); 314 if (FName.empty()) 315 return; 316 317 SubChecker SC = 318 llvm::StringSwitch<SubChecker>(FName) 319 .Case("open", &UnixAPIChecker::CheckOpen) 320 .Case("pthread_once", &UnixAPIChecker::CheckPthreadOnce) 321 .Case("calloc", &UnixAPIChecker::CheckCallocZero) 322 .Case("malloc", &UnixAPIChecker::CheckMallocZero) 323 .Case("realloc", &UnixAPIChecker::CheckReallocZero) 324 .Default(NULL); 325 326 if (SC) 327 (this->*SC)(C, CE); 328 } 329 330 //===----------------------------------------------------------------------===// 331 // Registration. 332 //===----------------------------------------------------------------------===// 333 334 void ento::registerUnixAPIChecker(CheckerManager &mgr) { 335 mgr.registerChecker<UnixAPIChecker>(); 336 } 337