1 //==- CheckSecuritySyntaxOnly.cpp - Basic security checks --------*- 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 file defines a set of flow-insensitive security checks. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 14 #include "clang/AST/StmtVisitor.h" 15 #include "clang/Analysis/AnalysisDeclContext.h" 16 #include "clang/Basic/TargetInfo.h" 17 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" 18 #include "clang/StaticAnalyzer/Core/Checker.h" 19 #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" 20 #include "llvm/ADT/SmallString.h" 21 #include "llvm/ADT/StringSwitch.h" 22 #include "llvm/Support/raw_ostream.h" 23 24 using namespace clang; 25 using namespace ento; 26 27 static bool isArc4RandomAvailable(const ASTContext &Ctx) { 28 const llvm::Triple &T = Ctx.getTargetInfo().getTriple(); 29 return T.getVendor() == llvm::Triple::Apple || 30 T.getOS() == llvm::Triple::CloudABI || 31 T.isOSFreeBSD() || 32 T.isOSNetBSD() || 33 T.isOSOpenBSD() || 34 T.isOSDragonFly(); 35 } 36 37 namespace { 38 struct ChecksFilter { 39 DefaultBool check_bcmp; 40 DefaultBool check_bcopy; 41 DefaultBool check_bzero; 42 DefaultBool check_gets; 43 DefaultBool check_getpw; 44 DefaultBool check_mktemp; 45 DefaultBool check_mkstemp; 46 DefaultBool check_strcpy; 47 DefaultBool check_DeprecatedOrUnsafeBufferHandling; 48 DefaultBool check_rand; 49 DefaultBool check_vfork; 50 DefaultBool check_FloatLoopCounter; 51 DefaultBool check_UncheckedReturn; 52 53 CheckerNameRef checkName_bcmp; 54 CheckerNameRef checkName_bcopy; 55 CheckerNameRef checkName_bzero; 56 CheckerNameRef checkName_gets; 57 CheckerNameRef checkName_getpw; 58 CheckerNameRef checkName_mktemp; 59 CheckerNameRef checkName_mkstemp; 60 CheckerNameRef checkName_strcpy; 61 CheckerNameRef checkName_DeprecatedOrUnsafeBufferHandling; 62 CheckerNameRef checkName_rand; 63 CheckerNameRef checkName_vfork; 64 CheckerNameRef checkName_FloatLoopCounter; 65 CheckerNameRef checkName_UncheckedReturn; 66 }; 67 68 class WalkAST : public StmtVisitor<WalkAST> { 69 BugReporter &BR; 70 AnalysisDeclContext* AC; 71 enum { num_setids = 6 }; 72 IdentifierInfo *II_setid[num_setids]; 73 74 const bool CheckRand; 75 const ChecksFilter &filter; 76 77 public: 78 WalkAST(BugReporter &br, AnalysisDeclContext* ac, 79 const ChecksFilter &f) 80 : BR(br), AC(ac), II_setid(), 81 CheckRand(isArc4RandomAvailable(BR.getContext())), 82 filter(f) {} 83 84 // Statement visitor methods. 85 void VisitCallExpr(CallExpr *CE); 86 void VisitForStmt(ForStmt *S); 87 void VisitCompoundStmt (CompoundStmt *S); 88 void VisitStmt(Stmt *S) { VisitChildren(S); } 89 90 void VisitChildren(Stmt *S); 91 92 // Helpers. 93 bool checkCall_strCommon(const CallExpr *CE, const FunctionDecl *FD); 94 95 typedef void (WalkAST::*FnCheck)(const CallExpr *, const FunctionDecl *); 96 97 // Checker-specific methods. 98 void checkLoopConditionForFloat(const ForStmt *FS); 99 void checkCall_bcmp(const CallExpr *CE, const FunctionDecl *FD); 100 void checkCall_bcopy(const CallExpr *CE, const FunctionDecl *FD); 101 void checkCall_bzero(const CallExpr *CE, const FunctionDecl *FD); 102 void checkCall_gets(const CallExpr *CE, const FunctionDecl *FD); 103 void checkCall_getpw(const CallExpr *CE, const FunctionDecl *FD); 104 void checkCall_mktemp(const CallExpr *CE, const FunctionDecl *FD); 105 void checkCall_mkstemp(const CallExpr *CE, const FunctionDecl *FD); 106 void checkCall_strcpy(const CallExpr *CE, const FunctionDecl *FD); 107 void checkCall_strcat(const CallExpr *CE, const FunctionDecl *FD); 108 void checkDeprecatedOrUnsafeBufferHandling(const CallExpr *CE, 109 const FunctionDecl *FD); 110 void checkCall_rand(const CallExpr *CE, const FunctionDecl *FD); 111 void checkCall_random(const CallExpr *CE, const FunctionDecl *FD); 112 void checkCall_vfork(const CallExpr *CE, const FunctionDecl *FD); 113 void checkUncheckedReturnValue(CallExpr *CE); 114 }; 115 } // end anonymous namespace 116 117 //===----------------------------------------------------------------------===// 118 // AST walking. 119 //===----------------------------------------------------------------------===// 120 121 void WalkAST::VisitChildren(Stmt *S) { 122 for (Stmt *Child : S->children()) 123 if (Child) 124 Visit(Child); 125 } 126 127 void WalkAST::VisitCallExpr(CallExpr *CE) { 128 // Get the callee. 129 const FunctionDecl *FD = CE->getDirectCallee(); 130 131 if (!FD) 132 return; 133 134 // Get the name of the callee. If it's a builtin, strip off the prefix. 135 IdentifierInfo *II = FD->getIdentifier(); 136 if (!II) // if no identifier, not a simple C function 137 return; 138 StringRef Name = II->getName(); 139 if (Name.startswith("__builtin_")) 140 Name = Name.substr(10); 141 142 // Set the evaluation function by switching on the callee name. 143 FnCheck evalFunction = llvm::StringSwitch<FnCheck>(Name) 144 .Case("bcmp", &WalkAST::checkCall_bcmp) 145 .Case("bcopy", &WalkAST::checkCall_bcopy) 146 .Case("bzero", &WalkAST::checkCall_bzero) 147 .Case("gets", &WalkAST::checkCall_gets) 148 .Case("getpw", &WalkAST::checkCall_getpw) 149 .Case("mktemp", &WalkAST::checkCall_mktemp) 150 .Case("mkstemp", &WalkAST::checkCall_mkstemp) 151 .Case("mkdtemp", &WalkAST::checkCall_mkstemp) 152 .Case("mkstemps", &WalkAST::checkCall_mkstemp) 153 .Cases("strcpy", "__strcpy_chk", &WalkAST::checkCall_strcpy) 154 .Cases("strcat", "__strcat_chk", &WalkAST::checkCall_strcat) 155 .Cases("sprintf", "vsprintf", "scanf", "wscanf", "fscanf", "fwscanf", 156 "vscanf", "vwscanf", "vfscanf", "vfwscanf", 157 &WalkAST::checkDeprecatedOrUnsafeBufferHandling) 158 .Cases("sscanf", "swscanf", "vsscanf", "vswscanf", "swprintf", 159 "snprintf", "vswprintf", "vsnprintf", "memcpy", "memmove", 160 &WalkAST::checkDeprecatedOrUnsafeBufferHandling) 161 .Cases("strncpy", "strncat", "memset", 162 &WalkAST::checkDeprecatedOrUnsafeBufferHandling) 163 .Case("drand48", &WalkAST::checkCall_rand) 164 .Case("erand48", &WalkAST::checkCall_rand) 165 .Case("jrand48", &WalkAST::checkCall_rand) 166 .Case("lrand48", &WalkAST::checkCall_rand) 167 .Case("mrand48", &WalkAST::checkCall_rand) 168 .Case("nrand48", &WalkAST::checkCall_rand) 169 .Case("lcong48", &WalkAST::checkCall_rand) 170 .Case("rand", &WalkAST::checkCall_rand) 171 .Case("rand_r", &WalkAST::checkCall_rand) 172 .Case("random", &WalkAST::checkCall_random) 173 .Case("vfork", &WalkAST::checkCall_vfork) 174 .Default(nullptr); 175 176 // If the callee isn't defined, it is not of security concern. 177 // Check and evaluate the call. 178 if (evalFunction) 179 (this->*evalFunction)(CE, FD); 180 181 // Recurse and check children. 182 VisitChildren(CE); 183 } 184 185 void WalkAST::VisitCompoundStmt(CompoundStmt *S) { 186 for (Stmt *Child : S->children()) 187 if (Child) { 188 if (CallExpr *CE = dyn_cast<CallExpr>(Child)) 189 checkUncheckedReturnValue(CE); 190 Visit(Child); 191 } 192 } 193 194 void WalkAST::VisitForStmt(ForStmt *FS) { 195 checkLoopConditionForFloat(FS); 196 197 // Recurse and check children. 198 VisitChildren(FS); 199 } 200 201 //===----------------------------------------------------------------------===// 202 // Check: floating point variable used as loop counter. 203 // Originally: <rdar://problem/6336718> 204 // Implements: CERT security coding advisory FLP-30. 205 //===----------------------------------------------------------------------===// 206 207 // Returns either 'x' or 'y', depending on which one of them is incremented 208 // in 'expr', or nullptr if none of them is incremented. 209 static const DeclRefExpr* 210 getIncrementedVar(const Expr *expr, const VarDecl *x, const VarDecl *y) { 211 expr = expr->IgnoreParenCasts(); 212 213 if (const BinaryOperator *B = dyn_cast<BinaryOperator>(expr)) { 214 if (!(B->isAssignmentOp() || B->isCompoundAssignmentOp() || 215 B->getOpcode() == BO_Comma)) 216 return nullptr; 217 218 if (const DeclRefExpr *lhs = getIncrementedVar(B->getLHS(), x, y)) 219 return lhs; 220 221 if (const DeclRefExpr *rhs = getIncrementedVar(B->getRHS(), x, y)) 222 return rhs; 223 224 return nullptr; 225 } 226 227 if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(expr)) { 228 const NamedDecl *ND = DR->getDecl(); 229 return ND == x || ND == y ? DR : nullptr; 230 } 231 232 if (const UnaryOperator *U = dyn_cast<UnaryOperator>(expr)) 233 return U->isIncrementDecrementOp() 234 ? getIncrementedVar(U->getSubExpr(), x, y) : nullptr; 235 236 return nullptr; 237 } 238 239 /// CheckLoopConditionForFloat - This check looks for 'for' statements that 240 /// use a floating point variable as a loop counter. 241 /// CERT: FLP30-C, FLP30-CPP. 242 /// 243 void WalkAST::checkLoopConditionForFloat(const ForStmt *FS) { 244 if (!filter.check_FloatLoopCounter) 245 return; 246 247 // Does the loop have a condition? 248 const Expr *condition = FS->getCond(); 249 250 if (!condition) 251 return; 252 253 // Does the loop have an increment? 254 const Expr *increment = FS->getInc(); 255 256 if (!increment) 257 return; 258 259 // Strip away '()' and casts. 260 condition = condition->IgnoreParenCasts(); 261 increment = increment->IgnoreParenCasts(); 262 263 // Is the loop condition a comparison? 264 const BinaryOperator *B = dyn_cast<BinaryOperator>(condition); 265 266 if (!B) 267 return; 268 269 // Is this a comparison? 270 if (!(B->isRelationalOp() || B->isEqualityOp())) 271 return; 272 273 // Are we comparing variables? 274 const DeclRefExpr *drLHS = 275 dyn_cast<DeclRefExpr>(B->getLHS()->IgnoreParenLValueCasts()); 276 const DeclRefExpr *drRHS = 277 dyn_cast<DeclRefExpr>(B->getRHS()->IgnoreParenLValueCasts()); 278 279 // Does at least one of the variables have a floating point type? 280 drLHS = drLHS && drLHS->getType()->isRealFloatingType() ? drLHS : nullptr; 281 drRHS = drRHS && drRHS->getType()->isRealFloatingType() ? drRHS : nullptr; 282 283 if (!drLHS && !drRHS) 284 return; 285 286 const VarDecl *vdLHS = drLHS ? dyn_cast<VarDecl>(drLHS->getDecl()) : nullptr; 287 const VarDecl *vdRHS = drRHS ? dyn_cast<VarDecl>(drRHS->getDecl()) : nullptr; 288 289 if (!vdLHS && !vdRHS) 290 return; 291 292 // Does either variable appear in increment? 293 const DeclRefExpr *drInc = getIncrementedVar(increment, vdLHS, vdRHS); 294 if (!drInc) 295 return; 296 297 const VarDecl *vdInc = cast<VarDecl>(drInc->getDecl()); 298 assert(vdInc && (vdInc == vdLHS || vdInc == vdRHS)); 299 300 // Emit the error. First figure out which DeclRefExpr in the condition 301 // referenced the compared variable. 302 const DeclRefExpr *drCond = vdLHS == vdInc ? drLHS : drRHS; 303 304 SmallVector<SourceRange, 2> ranges; 305 SmallString<256> sbuf; 306 llvm::raw_svector_ostream os(sbuf); 307 308 os << "Variable '" << drCond->getDecl()->getName() 309 << "' with floating point type '" << drCond->getType().getAsString() 310 << "' should not be used as a loop counter"; 311 312 ranges.push_back(drCond->getSourceRange()); 313 ranges.push_back(drInc->getSourceRange()); 314 315 const char *bugType = "Floating point variable used as loop counter"; 316 317 PathDiagnosticLocation FSLoc = 318 PathDiagnosticLocation::createBegin(FS, BR.getSourceManager(), AC); 319 BR.EmitBasicReport(AC->getDecl(), filter.checkName_FloatLoopCounter, 320 bugType, "Security", os.str(), 321 FSLoc, ranges); 322 } 323 324 //===----------------------------------------------------------------------===// 325 // Check: Any use of bcmp. 326 // CWE-477: Use of Obsolete Functions 327 // bcmp was deprecated in POSIX.1-2008 328 //===----------------------------------------------------------------------===// 329 330 void WalkAST::checkCall_bcmp(const CallExpr *CE, const FunctionDecl *FD) { 331 if (!filter.check_bcmp) 332 return; 333 334 const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>(); 335 if (!FPT) 336 return; 337 338 // Verify that the function takes three arguments. 339 if (FPT->getNumParams() != 3) 340 return; 341 342 for (int i = 0; i < 2; i++) { 343 // Verify the first and second argument type is void*. 344 const PointerType *PT = FPT->getParamType(i)->getAs<PointerType>(); 345 if (!PT) 346 return; 347 348 if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().VoidTy) 349 return; 350 } 351 352 // Verify the third argument type is integer. 353 if (!FPT->getParamType(2)->isIntegralOrUnscopedEnumerationType()) 354 return; 355 356 // Issue a warning. 357 PathDiagnosticLocation CELoc = 358 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); 359 BR.EmitBasicReport(AC->getDecl(), filter.checkName_bcmp, 360 "Use of deprecated function in call to 'bcmp()'", 361 "Security", 362 "The bcmp() function is obsoleted by memcmp().", 363 CELoc, CE->getCallee()->getSourceRange()); 364 } 365 366 //===----------------------------------------------------------------------===// 367 // Check: Any use of bcopy. 368 // CWE-477: Use of Obsolete Functions 369 // bcopy was deprecated in POSIX.1-2008 370 //===----------------------------------------------------------------------===// 371 372 void WalkAST::checkCall_bcopy(const CallExpr *CE, const FunctionDecl *FD) { 373 if (!filter.check_bcopy) 374 return; 375 376 const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>(); 377 if (!FPT) 378 return; 379 380 // Verify that the function takes three arguments. 381 if (FPT->getNumParams() != 3) 382 return; 383 384 for (int i = 0; i < 2; i++) { 385 // Verify the first and second argument type is void*. 386 const PointerType *PT = FPT->getParamType(i)->getAs<PointerType>(); 387 if (!PT) 388 return; 389 390 if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().VoidTy) 391 return; 392 } 393 394 // Verify the third argument type is integer. 395 if (!FPT->getParamType(2)->isIntegralOrUnscopedEnumerationType()) 396 return; 397 398 // Issue a warning. 399 PathDiagnosticLocation CELoc = 400 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); 401 BR.EmitBasicReport(AC->getDecl(), filter.checkName_bcopy, 402 "Use of deprecated function in call to 'bcopy()'", 403 "Security", 404 "The bcopy() function is obsoleted by memcpy() " 405 "or memmove().", 406 CELoc, CE->getCallee()->getSourceRange()); 407 } 408 409 //===----------------------------------------------------------------------===// 410 // Check: Any use of bzero. 411 // CWE-477: Use of Obsolete Functions 412 // bzero was deprecated in POSIX.1-2008 413 //===----------------------------------------------------------------------===// 414 415 void WalkAST::checkCall_bzero(const CallExpr *CE, const FunctionDecl *FD) { 416 if (!filter.check_bzero) 417 return; 418 419 const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>(); 420 if (!FPT) 421 return; 422 423 // Verify that the function takes two arguments. 424 if (FPT->getNumParams() != 2) 425 return; 426 427 // Verify the first argument type is void*. 428 const PointerType *PT = FPT->getParamType(0)->getAs<PointerType>(); 429 if (!PT) 430 return; 431 432 if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().VoidTy) 433 return; 434 435 // Verify the second argument type is integer. 436 if (!FPT->getParamType(1)->isIntegralOrUnscopedEnumerationType()) 437 return; 438 439 // Issue a warning. 440 PathDiagnosticLocation CELoc = 441 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); 442 BR.EmitBasicReport(AC->getDecl(), filter.checkName_bzero, 443 "Use of deprecated function in call to 'bzero()'", 444 "Security", 445 "The bzero() function is obsoleted by memset().", 446 CELoc, CE->getCallee()->getSourceRange()); 447 } 448 449 450 //===----------------------------------------------------------------------===// 451 // Check: Any use of 'gets' is insecure. 452 // Originally: <rdar://problem/6335715> 453 // Implements (part of): 300-BSI (buildsecurityin.us-cert.gov) 454 // CWE-242: Use of Inherently Dangerous Function 455 //===----------------------------------------------------------------------===// 456 457 void WalkAST::checkCall_gets(const CallExpr *CE, const FunctionDecl *FD) { 458 if (!filter.check_gets) 459 return; 460 461 const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>(); 462 if (!FPT) 463 return; 464 465 // Verify that the function takes a single argument. 466 if (FPT->getNumParams() != 1) 467 return; 468 469 // Is the argument a 'char*'? 470 const PointerType *PT = FPT->getParamType(0)->getAs<PointerType>(); 471 if (!PT) 472 return; 473 474 if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy) 475 return; 476 477 // Issue a warning. 478 PathDiagnosticLocation CELoc = 479 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); 480 BR.EmitBasicReport(AC->getDecl(), filter.checkName_gets, 481 "Potential buffer overflow in call to 'gets'", 482 "Security", 483 "Call to function 'gets' is extremely insecure as it can " 484 "always result in a buffer overflow", 485 CELoc, CE->getCallee()->getSourceRange()); 486 } 487 488 //===----------------------------------------------------------------------===// 489 // Check: Any use of 'getpwd' is insecure. 490 // CWE-477: Use of Obsolete Functions 491 //===----------------------------------------------------------------------===// 492 493 void WalkAST::checkCall_getpw(const CallExpr *CE, const FunctionDecl *FD) { 494 if (!filter.check_getpw) 495 return; 496 497 const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>(); 498 if (!FPT) 499 return; 500 501 // Verify that the function takes two arguments. 502 if (FPT->getNumParams() != 2) 503 return; 504 505 // Verify the first argument type is integer. 506 if (!FPT->getParamType(0)->isIntegralOrUnscopedEnumerationType()) 507 return; 508 509 // Verify the second argument type is char*. 510 const PointerType *PT = FPT->getParamType(1)->getAs<PointerType>(); 511 if (!PT) 512 return; 513 514 if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy) 515 return; 516 517 // Issue a warning. 518 PathDiagnosticLocation CELoc = 519 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); 520 BR.EmitBasicReport(AC->getDecl(), filter.checkName_getpw, 521 "Potential buffer overflow in call to 'getpw'", 522 "Security", 523 "The getpw() function is dangerous as it may overflow the " 524 "provided buffer. It is obsoleted by getpwuid().", 525 CELoc, CE->getCallee()->getSourceRange()); 526 } 527 528 //===----------------------------------------------------------------------===// 529 // Check: Any use of 'mktemp' is insecure. It is obsoleted by mkstemp(). 530 // CWE-377: Insecure Temporary File 531 //===----------------------------------------------------------------------===// 532 533 void WalkAST::checkCall_mktemp(const CallExpr *CE, const FunctionDecl *FD) { 534 if (!filter.check_mktemp) { 535 // Fall back to the security check of looking for enough 'X's in the 536 // format string, since that is a less severe warning. 537 checkCall_mkstemp(CE, FD); 538 return; 539 } 540 541 const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>(); 542 if(!FPT) 543 return; 544 545 // Verify that the function takes a single argument. 546 if (FPT->getNumParams() != 1) 547 return; 548 549 // Verify that the argument is Pointer Type. 550 const PointerType *PT = FPT->getParamType(0)->getAs<PointerType>(); 551 if (!PT) 552 return; 553 554 // Verify that the argument is a 'char*'. 555 if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy) 556 return; 557 558 // Issue a warning. 559 PathDiagnosticLocation CELoc = 560 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); 561 BR.EmitBasicReport(AC->getDecl(), filter.checkName_mktemp, 562 "Potential insecure temporary file in call 'mktemp'", 563 "Security", 564 "Call to function 'mktemp' is insecure as it always " 565 "creates or uses insecure temporary file. Use 'mkstemp' " 566 "instead", 567 CELoc, CE->getCallee()->getSourceRange()); 568 } 569 570 //===----------------------------------------------------------------------===// 571 // Check: Use of 'mkstemp', 'mktemp', 'mkdtemp' should contain at least 6 X's. 572 //===----------------------------------------------------------------------===// 573 574 void WalkAST::checkCall_mkstemp(const CallExpr *CE, const FunctionDecl *FD) { 575 if (!filter.check_mkstemp) 576 return; 577 578 StringRef Name = FD->getIdentifier()->getName(); 579 std::pair<signed, signed> ArgSuffix = 580 llvm::StringSwitch<std::pair<signed, signed> >(Name) 581 .Case("mktemp", std::make_pair(0,-1)) 582 .Case("mkstemp", std::make_pair(0,-1)) 583 .Case("mkdtemp", std::make_pair(0,-1)) 584 .Case("mkstemps", std::make_pair(0,1)) 585 .Default(std::make_pair(-1, -1)); 586 587 assert(ArgSuffix.first >= 0 && "Unsupported function"); 588 589 // Check if the number of arguments is consistent with out expectations. 590 unsigned numArgs = CE->getNumArgs(); 591 if ((signed) numArgs <= ArgSuffix.first) 592 return; 593 594 const StringLiteral *strArg = 595 dyn_cast<StringLiteral>(CE->getArg((unsigned)ArgSuffix.first) 596 ->IgnoreParenImpCasts()); 597 598 // Currently we only handle string literals. It is possible to do better, 599 // either by looking at references to const variables, or by doing real 600 // flow analysis. 601 if (!strArg || strArg->getCharByteWidth() != 1) 602 return; 603 604 // Count the number of X's, taking into account a possible cutoff suffix. 605 StringRef str = strArg->getString(); 606 unsigned numX = 0; 607 unsigned n = str.size(); 608 609 // Take into account the suffix. 610 unsigned suffix = 0; 611 if (ArgSuffix.second >= 0) { 612 const Expr *suffixEx = CE->getArg((unsigned)ArgSuffix.second); 613 Expr::EvalResult EVResult; 614 if (!suffixEx->EvaluateAsInt(EVResult, BR.getContext())) 615 return; 616 llvm::APSInt Result = EVResult.Val.getInt(); 617 // FIXME: Issue a warning. 618 if (Result.isNegative()) 619 return; 620 suffix = (unsigned) Result.getZExtValue(); 621 n = (n > suffix) ? n - suffix : 0; 622 } 623 624 for (unsigned i = 0; i < n; ++i) 625 if (str[i] == 'X') ++numX; 626 627 if (numX >= 6) 628 return; 629 630 // Issue a warning. 631 PathDiagnosticLocation CELoc = 632 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); 633 SmallString<512> buf; 634 llvm::raw_svector_ostream out(buf); 635 out << "Call to '" << Name << "' should have at least 6 'X's in the" 636 " format string to be secure (" << numX << " 'X'"; 637 if (numX != 1) 638 out << 's'; 639 out << " seen"; 640 if (suffix) { 641 out << ", " << suffix << " character"; 642 if (suffix > 1) 643 out << 's'; 644 out << " used as a suffix"; 645 } 646 out << ')'; 647 BR.EmitBasicReport(AC->getDecl(), filter.checkName_mkstemp, 648 "Insecure temporary file creation", "Security", 649 out.str(), CELoc, strArg->getSourceRange()); 650 } 651 652 //===----------------------------------------------------------------------===// 653 // Check: Any use of 'strcpy' is insecure. 654 // 655 // CWE-119: Improper Restriction of Operations within 656 // the Bounds of a Memory Buffer 657 //===----------------------------------------------------------------------===// 658 659 void WalkAST::checkCall_strcpy(const CallExpr *CE, const FunctionDecl *FD) { 660 if (!filter.check_strcpy) 661 return; 662 663 if (!checkCall_strCommon(CE, FD)) 664 return; 665 666 const auto *Target = CE->getArg(0)->IgnoreImpCasts(), 667 *Source = CE->getArg(1)->IgnoreImpCasts(); 668 669 if (const auto *Array = dyn_cast<ConstantArrayType>(Target->getType())) { 670 uint64_t ArraySize = BR.getContext().getTypeSize(Array) / 8; 671 if (const auto *String = dyn_cast<StringLiteral>(Source)) { 672 if (ArraySize >= String->getLength() + 1) 673 return; 674 } 675 } 676 677 // Issue a warning. 678 PathDiagnosticLocation CELoc = 679 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); 680 BR.EmitBasicReport(AC->getDecl(), filter.checkName_strcpy, 681 "Potential insecure memory buffer bounds restriction in " 682 "call 'strcpy'", 683 "Security", 684 "Call to function 'strcpy' is insecure as it does not " 685 "provide bounding of the memory buffer. Replace " 686 "unbounded copy functions with analogous functions that " 687 "support length arguments such as 'strlcpy'. CWE-119.", 688 CELoc, CE->getCallee()->getSourceRange()); 689 } 690 691 //===----------------------------------------------------------------------===// 692 // Check: Any use of 'strcat' is insecure. 693 // 694 // CWE-119: Improper Restriction of Operations within 695 // the Bounds of a Memory Buffer 696 //===----------------------------------------------------------------------===// 697 698 void WalkAST::checkCall_strcat(const CallExpr *CE, const FunctionDecl *FD) { 699 if (!filter.check_strcpy) 700 return; 701 702 if (!checkCall_strCommon(CE, FD)) 703 return; 704 705 // Issue a warning. 706 PathDiagnosticLocation CELoc = 707 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); 708 BR.EmitBasicReport(AC->getDecl(), filter.checkName_strcpy, 709 "Potential insecure memory buffer bounds restriction in " 710 "call 'strcat'", 711 "Security", 712 "Call to function 'strcat' is insecure as it does not " 713 "provide bounding of the memory buffer. Replace " 714 "unbounded copy functions with analogous functions that " 715 "support length arguments such as 'strlcat'. CWE-119.", 716 CELoc, CE->getCallee()->getSourceRange()); 717 } 718 719 //===----------------------------------------------------------------------===// 720 // Check: Any use of 'sprintf', 'vsprintf', 'scanf', 'wscanf', 'fscanf', 721 // 'fwscanf', 'vscanf', 'vwscanf', 'vfscanf', 'vfwscanf', 'sscanf', 722 // 'swscanf', 'vsscanf', 'vswscanf', 'swprintf', 'snprintf', 'vswprintf', 723 // 'vsnprintf', 'memcpy', 'memmove', 'strncpy', 'strncat', 'memset' 724 // is deprecated since C11. 725 // 726 // Use of 'sprintf', 'vsprintf', 'scanf', 'wscanf','fscanf', 727 // 'fwscanf', 'vscanf', 'vwscanf', 'vfscanf', 'vfwscanf', 'sscanf', 728 // 'swscanf', 'vsscanf', 'vswscanf' without buffer limitations 729 // is insecure. 730 // 731 // CWE-119: Improper Restriction of Operations within 732 // the Bounds of a Memory Buffer 733 //===----------------------------------------------------------------------===// 734 735 void WalkAST::checkDeprecatedOrUnsafeBufferHandling(const CallExpr *CE, 736 const FunctionDecl *FD) { 737 if (!filter.check_DeprecatedOrUnsafeBufferHandling) 738 return; 739 740 if (!BR.getContext().getLangOpts().C11) 741 return; 742 743 // Issue a warning. ArgIndex == -1: Deprecated but not unsafe (has size 744 // restrictions). 745 enum { DEPR_ONLY = -1, UNKNOWN_CALL = -2 }; 746 747 StringRef Name = FD->getIdentifier()->getName(); 748 if (Name.startswith("__builtin_")) 749 Name = Name.substr(10); 750 751 int ArgIndex = 752 llvm::StringSwitch<int>(Name) 753 .Cases("scanf", "wscanf", "vscanf", "vwscanf", 0) 754 .Cases("sprintf", "vsprintf", "fscanf", "fwscanf", "vfscanf", 755 "vfwscanf", "sscanf", "swscanf", "vsscanf", "vswscanf", 1) 756 .Cases("swprintf", "snprintf", "vswprintf", "vsnprintf", "memcpy", 757 "memmove", "memset", "strncpy", "strncat", DEPR_ONLY) 758 .Default(UNKNOWN_CALL); 759 760 assert(ArgIndex != UNKNOWN_CALL && "Unsupported function"); 761 bool BoundsProvided = ArgIndex == DEPR_ONLY; 762 763 if (!BoundsProvided) { 764 // Currently we only handle (not wide) string literals. It is possible to do 765 // better, either by looking at references to const variables, or by doing 766 // real flow analysis. 767 auto FormatString = 768 dyn_cast<StringLiteral>(CE->getArg(ArgIndex)->IgnoreParenImpCasts()); 769 if (FormatString && 770 FormatString->getString().find("%s") == StringRef::npos && 771 FormatString->getString().find("%[") == StringRef::npos) 772 BoundsProvided = true; 773 } 774 775 SmallString<128> Buf1; 776 SmallString<512> Buf2; 777 llvm::raw_svector_ostream Out1(Buf1); 778 llvm::raw_svector_ostream Out2(Buf2); 779 780 Out1 << "Potential insecure memory buffer bounds restriction in call '" 781 << Name << "'"; 782 Out2 << "Call to function '" << Name 783 << "' is insecure as it does not provide "; 784 785 if (!BoundsProvided) { 786 Out2 << "bounding of the memory buffer or "; 787 } 788 789 Out2 << "security checks introduced " 790 "in the C11 standard. Replace with analogous functions that " 791 "support length arguments or provides boundary checks such as '" 792 << Name << "_s' in case of C11"; 793 794 PathDiagnosticLocation CELoc = 795 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); 796 BR.EmitBasicReport(AC->getDecl(), 797 filter.checkName_DeprecatedOrUnsafeBufferHandling, 798 Out1.str(), "Security", Out2.str(), CELoc, 799 CE->getCallee()->getSourceRange()); 800 } 801 802 //===----------------------------------------------------------------------===// 803 // Common check for str* functions with no bounds parameters. 804 //===----------------------------------------------------------------------===// 805 806 bool WalkAST::checkCall_strCommon(const CallExpr *CE, const FunctionDecl *FD) { 807 const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>(); 808 if (!FPT) 809 return false; 810 811 // Verify the function takes two arguments, three in the _chk version. 812 int numArgs = FPT->getNumParams(); 813 if (numArgs != 2 && numArgs != 3) 814 return false; 815 816 // Verify the type for both arguments. 817 for (int i = 0; i < 2; i++) { 818 // Verify that the arguments are pointers. 819 const PointerType *PT = FPT->getParamType(i)->getAs<PointerType>(); 820 if (!PT) 821 return false; 822 823 // Verify that the argument is a 'char*'. 824 if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy) 825 return false; 826 } 827 828 return true; 829 } 830 831 //===----------------------------------------------------------------------===// 832 // Check: Linear congruent random number generators should not be used 833 // Originally: <rdar://problem/63371000> 834 // CWE-338: Use of cryptographically weak prng 835 //===----------------------------------------------------------------------===// 836 837 void WalkAST::checkCall_rand(const CallExpr *CE, const FunctionDecl *FD) { 838 if (!filter.check_rand || !CheckRand) 839 return; 840 841 const FunctionProtoType *FTP = FD->getType()->getAs<FunctionProtoType>(); 842 if (!FTP) 843 return; 844 845 if (FTP->getNumParams() == 1) { 846 // Is the argument an 'unsigned short *'? 847 // (Actually any integer type is allowed.) 848 const PointerType *PT = FTP->getParamType(0)->getAs<PointerType>(); 849 if (!PT) 850 return; 851 852 if (! PT->getPointeeType()->isIntegralOrUnscopedEnumerationType()) 853 return; 854 } else if (FTP->getNumParams() != 0) 855 return; 856 857 // Issue a warning. 858 SmallString<256> buf1; 859 llvm::raw_svector_ostream os1(buf1); 860 os1 << '\'' << *FD << "' is a poor random number generator"; 861 862 SmallString<256> buf2; 863 llvm::raw_svector_ostream os2(buf2); 864 os2 << "Function '" << *FD 865 << "' is obsolete because it implements a poor random number generator." 866 << " Use 'arc4random' instead"; 867 868 PathDiagnosticLocation CELoc = 869 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); 870 BR.EmitBasicReport(AC->getDecl(), filter.checkName_rand, os1.str(), 871 "Security", os2.str(), CELoc, 872 CE->getCallee()->getSourceRange()); 873 } 874 875 //===----------------------------------------------------------------------===// 876 // Check: 'random' should not be used 877 // Originally: <rdar://problem/63371000> 878 //===----------------------------------------------------------------------===// 879 880 void WalkAST::checkCall_random(const CallExpr *CE, const FunctionDecl *FD) { 881 if (!CheckRand || !filter.check_rand) 882 return; 883 884 const FunctionProtoType *FTP = FD->getType()->getAs<FunctionProtoType>(); 885 if (!FTP) 886 return; 887 888 // Verify that the function takes no argument. 889 if (FTP->getNumParams() != 0) 890 return; 891 892 // Issue a warning. 893 PathDiagnosticLocation CELoc = 894 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); 895 BR.EmitBasicReport(AC->getDecl(), filter.checkName_rand, 896 "'random' is not a secure random number generator", 897 "Security", 898 "The 'random' function produces a sequence of values that " 899 "an adversary may be able to predict. Use 'arc4random' " 900 "instead", CELoc, CE->getCallee()->getSourceRange()); 901 } 902 903 //===----------------------------------------------------------------------===// 904 // Check: 'vfork' should not be used. 905 // POS33-C: Do not use vfork(). 906 //===----------------------------------------------------------------------===// 907 908 void WalkAST::checkCall_vfork(const CallExpr *CE, const FunctionDecl *FD) { 909 if (!filter.check_vfork) 910 return; 911 912 // All calls to vfork() are insecure, issue a warning. 913 PathDiagnosticLocation CELoc = 914 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); 915 BR.EmitBasicReport(AC->getDecl(), filter.checkName_vfork, 916 "Potential insecure implementation-specific behavior in " 917 "call 'vfork'", 918 "Security", 919 "Call to function 'vfork' is insecure as it can lead to " 920 "denial of service situations in the parent process. " 921 "Replace calls to vfork with calls to the safer " 922 "'posix_spawn' function", 923 CELoc, CE->getCallee()->getSourceRange()); 924 } 925 926 //===----------------------------------------------------------------------===// 927 // Check: Should check whether privileges are dropped successfully. 928 // Originally: <rdar://problem/6337132> 929 //===----------------------------------------------------------------------===// 930 931 void WalkAST::checkUncheckedReturnValue(CallExpr *CE) { 932 if (!filter.check_UncheckedReturn) 933 return; 934 935 const FunctionDecl *FD = CE->getDirectCallee(); 936 if (!FD) 937 return; 938 939 if (II_setid[0] == nullptr) { 940 static const char * const identifiers[num_setids] = { 941 "setuid", "setgid", "seteuid", "setegid", 942 "setreuid", "setregid" 943 }; 944 945 for (size_t i = 0; i < num_setids; i++) 946 II_setid[i] = &BR.getContext().Idents.get(identifiers[i]); 947 } 948 949 const IdentifierInfo *id = FD->getIdentifier(); 950 size_t identifierid; 951 952 for (identifierid = 0; identifierid < num_setids; identifierid++) 953 if (id == II_setid[identifierid]) 954 break; 955 956 if (identifierid >= num_setids) 957 return; 958 959 const FunctionProtoType *FTP = FD->getType()->getAs<FunctionProtoType>(); 960 if (!FTP) 961 return; 962 963 // Verify that the function takes one or two arguments (depending on 964 // the function). 965 if (FTP->getNumParams() != (identifierid < 4 ? 1 : 2)) 966 return; 967 968 // The arguments must be integers. 969 for (unsigned i = 0; i < FTP->getNumParams(); i++) 970 if (!FTP->getParamType(i)->isIntegralOrUnscopedEnumerationType()) 971 return; 972 973 // Issue a warning. 974 SmallString<256> buf1; 975 llvm::raw_svector_ostream os1(buf1); 976 os1 << "Return value is not checked in call to '" << *FD << '\''; 977 978 SmallString<256> buf2; 979 llvm::raw_svector_ostream os2(buf2); 980 os2 << "The return value from the call to '" << *FD 981 << "' is not checked. If an error occurs in '" << *FD 982 << "', the following code may execute with unexpected privileges"; 983 984 PathDiagnosticLocation CELoc = 985 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); 986 BR.EmitBasicReport(AC->getDecl(), filter.checkName_UncheckedReturn, os1.str(), 987 "Security", os2.str(), CELoc, 988 CE->getCallee()->getSourceRange()); 989 } 990 991 //===----------------------------------------------------------------------===// 992 // SecuritySyntaxChecker 993 //===----------------------------------------------------------------------===// 994 995 namespace { 996 class SecuritySyntaxChecker : public Checker<check::ASTCodeBody> { 997 public: 998 ChecksFilter filter; 999 1000 void checkASTCodeBody(const Decl *D, AnalysisManager& mgr, 1001 BugReporter &BR) const { 1002 WalkAST walker(BR, mgr.getAnalysisDeclContext(D), filter); 1003 walker.Visit(D->getBody()); 1004 } 1005 }; 1006 } 1007 1008 void ento::registerSecuritySyntaxChecker(CheckerManager &mgr) { 1009 mgr.registerChecker<SecuritySyntaxChecker>(); 1010 } 1011 1012 bool ento::shouldRegisterSecuritySyntaxChecker(const LangOptions &LO) { 1013 return true; 1014 } 1015 1016 #define REGISTER_CHECKER(name) \ 1017 void ento::register##name(CheckerManager &mgr) { \ 1018 SecuritySyntaxChecker *checker = mgr.getChecker<SecuritySyntaxChecker>(); \ 1019 checker->filter.check_##name = true; \ 1020 checker->filter.checkName_##name = mgr.getCurrentCheckerName(); \ 1021 } \ 1022 \ 1023 bool ento::shouldRegister##name(const LangOptions &LO) { return true; } 1024 1025 REGISTER_CHECKER(bcmp) 1026 REGISTER_CHECKER(bcopy) 1027 REGISTER_CHECKER(bzero) 1028 REGISTER_CHECKER(gets) 1029 REGISTER_CHECKER(getpw) 1030 REGISTER_CHECKER(mkstemp) 1031 REGISTER_CHECKER(mktemp) 1032 REGISTER_CHECKER(strcpy) 1033 REGISTER_CHECKER(rand) 1034 REGISTER_CHECKER(vfork) 1035 REGISTER_CHECKER(FloatLoopCounter) 1036 REGISTER_CHECKER(UncheckedReturn) 1037 REGISTER_CHECKER(DeprecatedOrUnsafeBufferHandling) 1038