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