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