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