1 //= UnixAPIChecker.h - Checks preconditions for various Unix APIs --*- C++ -*-// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This defines UnixAPIChecker, which is an assortment of checks on calls 10 // to various, widely used UNIX/Posix functions. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 15 #include "clang/Basic/TargetInfo.h" 16 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 17 #include "clang/StaticAnalyzer/Core/Checker.h" 18 #include "clang/StaticAnalyzer/Core/CheckerManager.h" 19 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 20 #include "llvm/ADT/Optional.h" 21 #include "llvm/ADT/STLExtras.h" 22 #include "llvm/ADT/SmallString.h" 23 #include "llvm/Support/raw_ostream.h" 24 25 using namespace clang; 26 using namespace ento; 27 28 enum class OpenVariant { 29 /// The standard open() call: 30 /// int open(const char *path, int oflag, ...); 31 Open, 32 33 /// The variant taking a directory file descriptor and a relative path: 34 /// int openat(int fd, const char *path, int oflag, ...); 35 OpenAt 36 }; 37 38 namespace { 39 40 class UnixAPIMisuseChecker : public Checker< check::PreStmt<CallExpr> > { 41 mutable std::unique_ptr<BugType> BT_open, BT_pthreadOnce; 42 mutable Optional<uint64_t> Val_O_CREAT; 43 44 public: 45 DefaultBool CheckMisuse, CheckPortability; 46 47 void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; 48 49 void CheckOpen(CheckerContext &C, const CallExpr *CE) const; 50 void CheckOpenAt(CheckerContext &C, const CallExpr *CE) const; 51 void CheckPthreadOnce(CheckerContext &C, const CallExpr *CE) const; 52 53 void CheckOpenVariant(CheckerContext &C, 54 const CallExpr *CE, OpenVariant Variant) const; 55 56 void ReportOpenBug(CheckerContext &C, 57 ProgramStateRef State, 58 const char *Msg, 59 SourceRange SR) const; 60 61 }; 62 63 class UnixAPIPortabilityChecker : public Checker< check::PreStmt<CallExpr> > { 64 public: 65 void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; 66 67 private: 68 mutable std::unique_ptr<BugType> BT_mallocZero; 69 70 void CheckCallocZero(CheckerContext &C, const CallExpr *CE) const; 71 void CheckMallocZero(CheckerContext &C, const CallExpr *CE) const; 72 void CheckReallocZero(CheckerContext &C, const CallExpr *CE) const; 73 void CheckReallocfZero(CheckerContext &C, const CallExpr *CE) const; 74 void CheckAllocaZero(CheckerContext &C, const CallExpr *CE) const; 75 void CheckAllocaWithAlignZero(CheckerContext &C, const CallExpr *CE) const; 76 void CheckVallocZero(CheckerContext &C, const CallExpr *CE) const; 77 78 bool ReportZeroByteAllocation(CheckerContext &C, 79 ProgramStateRef falseState, 80 const Expr *arg, 81 const char *fn_name) const; 82 void BasicAllocationCheck(CheckerContext &C, 83 const CallExpr *CE, 84 const unsigned numArgs, 85 const unsigned sizeArg, 86 const char *fn) const; 87 }; 88 89 } //end anonymous namespace 90 91 static void LazyInitialize(const CheckerBase *Checker, 92 std::unique_ptr<BugType> &BT, 93 const char *name) { 94 if (BT) 95 return; 96 BT.reset(new BugType(Checker, name, categories::UnixAPI)); 97 } 98 99 //===----------------------------------------------------------------------===// 100 // "open" (man 2 open) 101 //===----------------------------------------------------------------------===/ 102 103 void UnixAPIMisuseChecker::checkPreStmt(const CallExpr *CE, 104 CheckerContext &C) const { 105 const FunctionDecl *FD = C.getCalleeDecl(CE); 106 if (!FD || FD->getKind() != Decl::Function) 107 return; 108 109 // Don't treat functions in namespaces with the same name a Unix function 110 // as a call to the Unix function. 111 const DeclContext *NamespaceCtx = FD->getEnclosingNamespaceContext(); 112 if (NamespaceCtx && isa<NamespaceDecl>(NamespaceCtx)) 113 return; 114 115 StringRef FName = C.getCalleeName(FD); 116 if (FName.empty()) 117 return; 118 119 if (FName == "open") 120 CheckOpen(C, CE); 121 122 else if (FName == "openat") 123 CheckOpenAt(C, CE); 124 125 else if (FName == "pthread_once") 126 CheckPthreadOnce(C, CE); 127 } 128 void UnixAPIMisuseChecker::ReportOpenBug(CheckerContext &C, 129 ProgramStateRef State, 130 const char *Msg, 131 SourceRange SR) const { 132 ExplodedNode *N = C.generateErrorNode(State); 133 if (!N) 134 return; 135 136 LazyInitialize(this, BT_open, "Improper use of 'open'"); 137 138 auto Report = std::make_unique<PathSensitiveBugReport>(*BT_open, Msg, N); 139 Report->addRange(SR); 140 C.emitReport(std::move(Report)); 141 } 142 143 void UnixAPIMisuseChecker::CheckOpen(CheckerContext &C, 144 const CallExpr *CE) const { 145 CheckOpenVariant(C, CE, OpenVariant::Open); 146 } 147 148 void UnixAPIMisuseChecker::CheckOpenAt(CheckerContext &C, 149 const CallExpr *CE) const { 150 CheckOpenVariant(C, CE, OpenVariant::OpenAt); 151 } 152 153 void UnixAPIMisuseChecker::CheckOpenVariant(CheckerContext &C, 154 const CallExpr *CE, 155 OpenVariant Variant) const { 156 // The index of the argument taking the flags open flags (O_RDONLY, 157 // O_WRONLY, O_CREAT, etc.), 158 unsigned int FlagsArgIndex; 159 const char *VariantName; 160 switch (Variant) { 161 case OpenVariant::Open: 162 FlagsArgIndex = 1; 163 VariantName = "open"; 164 break; 165 case OpenVariant::OpenAt: 166 FlagsArgIndex = 2; 167 VariantName = "openat"; 168 break; 169 }; 170 171 // All calls should at least provide arguments up to the 'flags' parameter. 172 unsigned int MinArgCount = FlagsArgIndex + 1; 173 174 // If the flags has O_CREAT set then open/openat() require an additional 175 // argument specifying the file mode (permission bits) for the created file. 176 unsigned int CreateModeArgIndex = FlagsArgIndex + 1; 177 178 // The create mode argument should be the last argument. 179 unsigned int MaxArgCount = CreateModeArgIndex + 1; 180 181 ProgramStateRef state = C.getState(); 182 183 if (CE->getNumArgs() < MinArgCount) { 184 // The frontend should issue a warning for this case, so this is a sanity 185 // check. 186 return; 187 } else if (CE->getNumArgs() == MaxArgCount) { 188 const Expr *Arg = CE->getArg(CreateModeArgIndex); 189 QualType QT = Arg->getType(); 190 if (!QT->isIntegerType()) { 191 SmallString<256> SBuf; 192 llvm::raw_svector_ostream OS(SBuf); 193 OS << "The " << CreateModeArgIndex + 1 194 << llvm::getOrdinalSuffix(CreateModeArgIndex + 1) 195 << " argument to '" << VariantName << "' is not an integer"; 196 197 ReportOpenBug(C, state, 198 SBuf.c_str(), 199 Arg->getSourceRange()); 200 return; 201 } 202 } else if (CE->getNumArgs() > MaxArgCount) { 203 SmallString<256> SBuf; 204 llvm::raw_svector_ostream OS(SBuf); 205 OS << "Call to '" << VariantName << "' with more than " << MaxArgCount 206 << " arguments"; 207 208 ReportOpenBug(C, state, 209 SBuf.c_str(), 210 CE->getArg(MaxArgCount)->getSourceRange()); 211 return; 212 } 213 214 // The definition of O_CREAT is platform specific. We need a better way 215 // of querying this information from the checking environment. 216 if (!Val_O_CREAT.hasValue()) { 217 if (C.getASTContext().getTargetInfo().getTriple().getVendor() 218 == llvm::Triple::Apple) 219 Val_O_CREAT = 0x0200; 220 else { 221 // FIXME: We need a more general way of getting the O_CREAT value. 222 // We could possibly grovel through the preprocessor state, but 223 // that would require passing the Preprocessor object to the ExprEngine. 224 // See also: MallocChecker.cpp / M_ZERO. 225 return; 226 } 227 } 228 229 // Now check if oflags has O_CREAT set. 230 const Expr *oflagsEx = CE->getArg(FlagsArgIndex); 231 const SVal V = C.getSVal(oflagsEx); 232 if (!V.getAs<NonLoc>()) { 233 // The case where 'V' can be a location can only be due to a bad header, 234 // so in this case bail out. 235 return; 236 } 237 NonLoc oflags = V.castAs<NonLoc>(); 238 NonLoc ocreateFlag = C.getSValBuilder() 239 .makeIntVal(Val_O_CREAT.getValue(), oflagsEx->getType()).castAs<NonLoc>(); 240 SVal maskedFlagsUC = C.getSValBuilder().evalBinOpNN(state, BO_And, 241 oflags, ocreateFlag, 242 oflagsEx->getType()); 243 if (maskedFlagsUC.isUnknownOrUndef()) 244 return; 245 DefinedSVal maskedFlags = maskedFlagsUC.castAs<DefinedSVal>(); 246 247 // Check if maskedFlags is non-zero. 248 ProgramStateRef trueState, falseState; 249 std::tie(trueState, falseState) = state->assume(maskedFlags); 250 251 // Only emit an error if the value of 'maskedFlags' is properly 252 // constrained; 253 if (!(trueState && !falseState)) 254 return; 255 256 if (CE->getNumArgs() < MaxArgCount) { 257 SmallString<256> SBuf; 258 llvm::raw_svector_ostream OS(SBuf); 259 OS << "Call to '" << VariantName << "' requires a " 260 << CreateModeArgIndex + 1 261 << llvm::getOrdinalSuffix(CreateModeArgIndex + 1) 262 << " argument when the 'O_CREAT' flag is set"; 263 ReportOpenBug(C, trueState, 264 SBuf.c_str(), 265 oflagsEx->getSourceRange()); 266 } 267 } 268 269 //===----------------------------------------------------------------------===// 270 // pthread_once 271 //===----------------------------------------------------------------------===// 272 273 void UnixAPIMisuseChecker::CheckPthreadOnce(CheckerContext &C, 274 const CallExpr *CE) const { 275 276 // This is similar to 'CheckDispatchOnce' in the MacOSXAPIChecker. 277 // They can possibly be refactored. 278 279 if (CE->getNumArgs() < 1) 280 return; 281 282 // Check if the first argument is stack allocated. If so, issue a warning 283 // because that's likely to be bad news. 284 ProgramStateRef state = C.getState(); 285 const MemRegion *R = C.getSVal(CE->getArg(0)).getAsRegion(); 286 if (!R || !isa<StackSpaceRegion>(R->getMemorySpace())) 287 return; 288 289 ExplodedNode *N = C.generateErrorNode(state); 290 if (!N) 291 return; 292 293 SmallString<256> S; 294 llvm::raw_svector_ostream os(S); 295 os << "Call to 'pthread_once' uses"; 296 if (const VarRegion *VR = dyn_cast<VarRegion>(R)) 297 os << " the local variable '" << VR->getDecl()->getName() << '\''; 298 else 299 os << " stack allocated memory"; 300 os << " for the \"control\" value. Using such transient memory for " 301 "the control value is potentially dangerous."; 302 if (isa<VarRegion>(R) && isa<StackLocalsSpaceRegion>(R->getMemorySpace())) 303 os << " Perhaps you intended to declare the variable as 'static'?"; 304 305 LazyInitialize(this, BT_pthreadOnce, "Improper use of 'pthread_once'"); 306 307 auto report = 308 std::make_unique<PathSensitiveBugReport>(*BT_pthreadOnce, os.str(), N); 309 report->addRange(CE->getArg(0)->getSourceRange()); 310 C.emitReport(std::move(report)); 311 } 312 313 //===----------------------------------------------------------------------===// 314 // "calloc", "malloc", "realloc", "reallocf", "alloca" and "valloc" 315 // with allocation size 0 316 //===----------------------------------------------------------------------===// 317 318 // FIXME: Eventually these should be rolled into the MallocChecker, but right now 319 // they're more basic and valuable for widespread use. 320 321 // Returns true if we try to do a zero byte allocation, false otherwise. 322 // Fills in trueState and falseState. 323 static bool IsZeroByteAllocation(ProgramStateRef state, 324 const SVal argVal, 325 ProgramStateRef *trueState, 326 ProgramStateRef *falseState) { 327 std::tie(*trueState, *falseState) = 328 state->assume(argVal.castAs<DefinedSVal>()); 329 330 return (*falseState && !*trueState); 331 } 332 333 // Generates an error report, indicating that the function whose name is given 334 // will perform a zero byte allocation. 335 // Returns false if an error occurred, true otherwise. 336 bool UnixAPIPortabilityChecker::ReportZeroByteAllocation( 337 CheckerContext &C, 338 ProgramStateRef falseState, 339 const Expr *arg, 340 const char *fn_name) const { 341 ExplodedNode *N = C.generateErrorNode(falseState); 342 if (!N) 343 return false; 344 345 LazyInitialize(this, BT_mallocZero, 346 "Undefined allocation of 0 bytes (CERT MEM04-C; CWE-131)"); 347 348 SmallString<256> S; 349 llvm::raw_svector_ostream os(S); 350 os << "Call to '" << fn_name << "' has an allocation size of 0 bytes"; 351 auto report = 352 std::make_unique<PathSensitiveBugReport>(*BT_mallocZero, os.str(), N); 353 354 report->addRange(arg->getSourceRange()); 355 bugreporter::trackExpressionValue(N, arg, *report); 356 C.emitReport(std::move(report)); 357 358 return true; 359 } 360 361 // Does a basic check for 0-sized allocations suitable for most of the below 362 // functions (modulo "calloc") 363 void UnixAPIPortabilityChecker::BasicAllocationCheck(CheckerContext &C, 364 const CallExpr *CE, 365 const unsigned numArgs, 366 const unsigned sizeArg, 367 const char *fn) const { 368 // Sanity check for the correct number of arguments 369 if (CE->getNumArgs() != numArgs) 370 return; 371 372 // Check if the allocation size is 0. 373 ProgramStateRef state = C.getState(); 374 ProgramStateRef trueState = nullptr, falseState = nullptr; 375 const Expr *arg = CE->getArg(sizeArg); 376 SVal argVal = C.getSVal(arg); 377 378 if (argVal.isUnknownOrUndef()) 379 return; 380 381 // Is the value perfectly constrained to zero? 382 if (IsZeroByteAllocation(state, argVal, &trueState, &falseState)) { 383 (void) ReportZeroByteAllocation(C, falseState, arg, fn); 384 return; 385 } 386 // Assume the value is non-zero going forward. 387 assert(trueState); 388 if (trueState != state) 389 C.addTransition(trueState); 390 } 391 392 void UnixAPIPortabilityChecker::CheckCallocZero(CheckerContext &C, 393 const CallExpr *CE) const { 394 unsigned int nArgs = CE->getNumArgs(); 395 if (nArgs != 2) 396 return; 397 398 ProgramStateRef state = C.getState(); 399 ProgramStateRef trueState = nullptr, falseState = nullptr; 400 401 unsigned int i; 402 for (i = 0; i < nArgs; i++) { 403 const Expr *arg = CE->getArg(i); 404 SVal argVal = C.getSVal(arg); 405 if (argVal.isUnknownOrUndef()) { 406 if (i == 0) 407 continue; 408 else 409 return; 410 } 411 412 if (IsZeroByteAllocation(state, argVal, &trueState, &falseState)) { 413 if (ReportZeroByteAllocation(C, falseState, arg, "calloc")) 414 return; 415 else if (i == 0) 416 continue; 417 else 418 return; 419 } 420 } 421 422 // Assume the value is non-zero going forward. 423 assert(trueState); 424 if (trueState != state) 425 C.addTransition(trueState); 426 } 427 428 void UnixAPIPortabilityChecker::CheckMallocZero(CheckerContext &C, 429 const CallExpr *CE) const { 430 BasicAllocationCheck(C, CE, 1, 0, "malloc"); 431 } 432 433 void UnixAPIPortabilityChecker::CheckReallocZero(CheckerContext &C, 434 const CallExpr *CE) const { 435 BasicAllocationCheck(C, CE, 2, 1, "realloc"); 436 } 437 438 void UnixAPIPortabilityChecker::CheckReallocfZero(CheckerContext &C, 439 const CallExpr *CE) const { 440 BasicAllocationCheck(C, CE, 2, 1, "reallocf"); 441 } 442 443 void UnixAPIPortabilityChecker::CheckAllocaZero(CheckerContext &C, 444 const CallExpr *CE) const { 445 BasicAllocationCheck(C, CE, 1, 0, "alloca"); 446 } 447 448 void UnixAPIPortabilityChecker::CheckAllocaWithAlignZero( 449 CheckerContext &C, 450 const CallExpr *CE) const { 451 BasicAllocationCheck(C, CE, 2, 0, "__builtin_alloca_with_align"); 452 } 453 454 void UnixAPIPortabilityChecker::CheckVallocZero(CheckerContext &C, 455 const CallExpr *CE) const { 456 BasicAllocationCheck(C, CE, 1, 0, "valloc"); 457 } 458 459 void UnixAPIPortabilityChecker::checkPreStmt(const CallExpr *CE, 460 CheckerContext &C) const { 461 const FunctionDecl *FD = C.getCalleeDecl(CE); 462 if (!FD || FD->getKind() != Decl::Function) 463 return; 464 465 // Don't treat functions in namespaces with the same name a Unix function 466 // as a call to the Unix function. 467 const DeclContext *NamespaceCtx = FD->getEnclosingNamespaceContext(); 468 if (NamespaceCtx && isa<NamespaceDecl>(NamespaceCtx)) 469 return; 470 471 StringRef FName = C.getCalleeName(FD); 472 if (FName.empty()) 473 return; 474 475 if (FName == "calloc") 476 CheckCallocZero(C, CE); 477 478 else if (FName == "malloc") 479 CheckMallocZero(C, CE); 480 481 else if (FName == "realloc") 482 CheckReallocZero(C, CE); 483 484 else if (FName == "reallocf") 485 CheckReallocfZero(C, CE); 486 487 else if (FName == "alloca" || FName == "__builtin_alloca") 488 CheckAllocaZero(C, CE); 489 490 else if (FName == "__builtin_alloca_with_align") 491 CheckAllocaWithAlignZero(C, CE); 492 493 else if (FName == "valloc") 494 CheckVallocZero(C, CE); 495 } 496 497 //===----------------------------------------------------------------------===// 498 // Registration. 499 //===----------------------------------------------------------------------===// 500 501 #define REGISTER_CHECKER(CHECKERNAME) \ 502 void ento::register##CHECKERNAME(CheckerManager &mgr) { \ 503 mgr.registerChecker<CHECKERNAME>(); \ 504 } \ 505 \ 506 bool ento::shouldRegister##CHECKERNAME(const LangOptions &LO) { \ 507 return true; \ 508 } 509 510 REGISTER_CHECKER(UnixAPIMisuseChecker) 511 REGISTER_CHECKER(UnixAPIPortabilityChecker) 512