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