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