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