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.isOSFreeBSD() || 31 T.isOSNetBSD() || 32 T.isOSOpenBSD() || 33 T.isOSDragonFly(); 34 } 35 36 namespace { 37 struct ChecksFilter { 38 bool check_bcmp = false; 39 bool check_bcopy = false; 40 bool check_bzero = false; 41 bool check_gets = false; 42 bool check_getpw = false; 43 bool check_mktemp = false; 44 bool check_mkstemp = false; 45 bool check_strcpy = false; 46 bool check_DeprecatedOrUnsafeBufferHandling = false; 47 bool check_rand = false; 48 bool check_vfork = false; 49 bool check_FloatLoopCounter = false; 50 bool check_UncheckedReturn = false; 51 bool check_decodeValueOfObjCType = false; 52 53 CheckerNameRef checkName_bcmp; 54 CheckerNameRef checkName_bcopy; 55 CheckerNameRef checkName_bzero; 56 CheckerNameRef checkName_gets; 57 CheckerNameRef checkName_getpw; 58 CheckerNameRef checkName_mktemp; 59 CheckerNameRef checkName_mkstemp; 60 CheckerNameRef checkName_strcpy; 61 CheckerNameRef checkName_DeprecatedOrUnsafeBufferHandling; 62 CheckerNameRef checkName_rand; 63 CheckerNameRef checkName_vfork; 64 CheckerNameRef checkName_FloatLoopCounter; 65 CheckerNameRef checkName_UncheckedReturn; 66 CheckerNameRef checkName_decodeValueOfObjCType; 67 }; 68 69 class WalkAST : public StmtVisitor<WalkAST> { 70 BugReporter &BR; 71 AnalysisDeclContext* AC; 72 enum { num_setids = 6 }; 73 IdentifierInfo *II_setid[num_setids]; 74 75 const bool CheckRand; 76 const ChecksFilter &filter; 77 78 public: 79 WalkAST(BugReporter &br, AnalysisDeclContext* ac, 80 const ChecksFilter &f) 81 : BR(br), AC(ac), II_setid(), 82 CheckRand(isArc4RandomAvailable(BR.getContext())), 83 filter(f) {} 84 85 // Statement visitor methods. 86 void VisitCallExpr(CallExpr *CE); 87 void VisitObjCMessageExpr(ObjCMessageExpr *CE); 88 void VisitForStmt(ForStmt *S); 89 void VisitCompoundStmt (CompoundStmt *S); 90 void VisitStmt(Stmt *S) { VisitChildren(S); } 91 92 void VisitChildren(Stmt *S); 93 94 // Helpers. 95 bool checkCall_strCommon(const CallExpr *CE, const FunctionDecl *FD); 96 97 typedef void (WalkAST::*FnCheck)(const CallExpr *, const FunctionDecl *); 98 typedef void (WalkAST::*MsgCheck)(const ObjCMessageExpr *); 99 100 // Checker-specific methods. 101 void checkLoopConditionForFloat(const ForStmt *FS); 102 void checkCall_bcmp(const CallExpr *CE, const FunctionDecl *FD); 103 void checkCall_bcopy(const CallExpr *CE, const FunctionDecl *FD); 104 void checkCall_bzero(const CallExpr *CE, const FunctionDecl *FD); 105 void checkCall_gets(const CallExpr *CE, const FunctionDecl *FD); 106 void checkCall_getpw(const CallExpr *CE, const FunctionDecl *FD); 107 void checkCall_mktemp(const CallExpr *CE, const FunctionDecl *FD); 108 void checkCall_mkstemp(const CallExpr *CE, const FunctionDecl *FD); 109 void checkCall_strcpy(const CallExpr *CE, const FunctionDecl *FD); 110 void checkCall_strcat(const CallExpr *CE, const FunctionDecl *FD); 111 void checkDeprecatedOrUnsafeBufferHandling(const CallExpr *CE, 112 const FunctionDecl *FD); 113 void checkCall_rand(const CallExpr *CE, const FunctionDecl *FD); 114 void checkCall_random(const CallExpr *CE, const FunctionDecl *FD); 115 void checkCall_vfork(const CallExpr *CE, const FunctionDecl *FD); 116 void checkMsg_decodeValueOfObjCType(const ObjCMessageExpr *ME); 117 void checkUncheckedReturnValue(CallExpr *CE); 118 }; 119 } // end anonymous namespace 120 121 //===----------------------------------------------------------------------===// 122 // AST walking. 123 //===----------------------------------------------------------------------===// 124 125 void WalkAST::VisitChildren(Stmt *S) { 126 for (Stmt *Child : S->children()) 127 if (Child) 128 Visit(Child); 129 } 130 131 void WalkAST::VisitCallExpr(CallExpr *CE) { 132 // Get the callee. 133 const FunctionDecl *FD = CE->getDirectCallee(); 134 135 if (!FD) 136 return; 137 138 // Get the name of the callee. If it's a builtin, strip off the prefix. 139 IdentifierInfo *II = FD->getIdentifier(); 140 if (!II) // if no identifier, not a simple C function 141 return; 142 StringRef Name = II->getName(); 143 if (Name.starts_with("__builtin_")) 144 Name = Name.substr(10); 145 146 // Set the evaluation function by switching on the callee name. 147 FnCheck evalFunction = 148 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", "fprintf", 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. Most man pages literally says this. 470 // 471 // Implements (part of): 300-BSI (buildsecurityin.us-cert.gov) 472 // CWE-242: Use of Inherently Dangerous Function 473 //===----------------------------------------------------------------------===// 474 475 void WalkAST::checkCall_gets(const CallExpr *CE, const FunctionDecl *FD) { 476 if (!filter.check_gets) 477 return; 478 479 const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>(); 480 if (!FPT) 481 return; 482 483 // Verify that the function takes a single argument. 484 if (FPT->getNumParams() != 1) 485 return; 486 487 // Is the argument a 'char*'? 488 const PointerType *PT = FPT->getParamType(0)->getAs<PointerType>(); 489 if (!PT) 490 return; 491 492 if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy) 493 return; 494 495 // Issue a warning. 496 PathDiagnosticLocation CELoc = 497 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); 498 BR.EmitBasicReport(AC->getDecl(), filter.checkName_gets, 499 "Potential buffer overflow in call to 'gets'", 500 "Security", 501 "Call to function 'gets' is extremely insecure as it can " 502 "always result in a buffer overflow", 503 CELoc, CE->getCallee()->getSourceRange()); 504 } 505 506 //===----------------------------------------------------------------------===// 507 // Check: Any use of 'getpwd' is insecure. 508 // CWE-477: Use of Obsolete Functions 509 //===----------------------------------------------------------------------===// 510 511 void WalkAST::checkCall_getpw(const CallExpr *CE, const FunctionDecl *FD) { 512 if (!filter.check_getpw) 513 return; 514 515 const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>(); 516 if (!FPT) 517 return; 518 519 // Verify that the function takes two arguments. 520 if (FPT->getNumParams() != 2) 521 return; 522 523 // Verify the first argument type is integer. 524 if (!FPT->getParamType(0)->isIntegralOrUnscopedEnumerationType()) 525 return; 526 527 // Verify the second argument type is char*. 528 const PointerType *PT = FPT->getParamType(1)->getAs<PointerType>(); 529 if (!PT) 530 return; 531 532 if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy) 533 return; 534 535 // Issue a warning. 536 PathDiagnosticLocation CELoc = 537 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); 538 BR.EmitBasicReport(AC->getDecl(), filter.checkName_getpw, 539 "Potential buffer overflow in call to 'getpw'", 540 "Security", 541 "The getpw() function is dangerous as it may overflow the " 542 "provided buffer. It is obsoleted by getpwuid().", 543 CELoc, CE->getCallee()->getSourceRange()); 544 } 545 546 //===----------------------------------------------------------------------===// 547 // Check: Any use of 'mktemp' is insecure. It is obsoleted by mkstemp(). 548 // CWE-377: Insecure Temporary File 549 //===----------------------------------------------------------------------===// 550 551 void WalkAST::checkCall_mktemp(const CallExpr *CE, const FunctionDecl *FD) { 552 if (!filter.check_mktemp) { 553 // Fall back to the security check of looking for enough 'X's in the 554 // format string, since that is a less severe warning. 555 checkCall_mkstemp(CE, FD); 556 return; 557 } 558 559 const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>(); 560 if(!FPT) 561 return; 562 563 // Verify that the function takes a single argument. 564 if (FPT->getNumParams() != 1) 565 return; 566 567 // Verify that the argument is Pointer Type. 568 const PointerType *PT = FPT->getParamType(0)->getAs<PointerType>(); 569 if (!PT) 570 return; 571 572 // Verify that the argument is a 'char*'. 573 if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy) 574 return; 575 576 // Issue a warning. 577 PathDiagnosticLocation CELoc = 578 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); 579 BR.EmitBasicReport(AC->getDecl(), filter.checkName_mktemp, 580 "Potential insecure temporary file in call 'mktemp'", 581 "Security", 582 "Call to function 'mktemp' is insecure as it always " 583 "creates or uses insecure temporary file. Use 'mkstemp' " 584 "instead", 585 CELoc, CE->getCallee()->getSourceRange()); 586 } 587 588 //===----------------------------------------------------------------------===// 589 // Check: Use of 'mkstemp', 'mktemp', 'mkdtemp' should contain at least 6 X's. 590 //===----------------------------------------------------------------------===// 591 592 void WalkAST::checkCall_mkstemp(const CallExpr *CE, const FunctionDecl *FD) { 593 if (!filter.check_mkstemp) 594 return; 595 596 StringRef Name = FD->getIdentifier()->getName(); 597 std::pair<signed, signed> ArgSuffix = 598 llvm::StringSwitch<std::pair<signed, signed> >(Name) 599 .Case("mktemp", std::make_pair(0,-1)) 600 .Case("mkstemp", std::make_pair(0,-1)) 601 .Case("mkdtemp", std::make_pair(0,-1)) 602 .Case("mkstemps", std::make_pair(0,1)) 603 .Default(std::make_pair(-1, -1)); 604 605 assert(ArgSuffix.first >= 0 && "Unsupported function"); 606 607 // Check if the number of arguments is consistent with out expectations. 608 unsigned numArgs = CE->getNumArgs(); 609 if ((signed) numArgs <= ArgSuffix.first) 610 return; 611 612 const StringLiteral *strArg = 613 dyn_cast<StringLiteral>(CE->getArg((unsigned)ArgSuffix.first) 614 ->IgnoreParenImpCasts()); 615 616 // Currently we only handle string literals. It is possible to do better, 617 // either by looking at references to const variables, or by doing real 618 // flow analysis. 619 if (!strArg || strArg->getCharByteWidth() != 1) 620 return; 621 622 // Count the number of X's, taking into account a possible cutoff suffix. 623 StringRef str = strArg->getString(); 624 unsigned numX = 0; 625 unsigned n = str.size(); 626 627 // Take into account the suffix. 628 unsigned suffix = 0; 629 if (ArgSuffix.second >= 0) { 630 const Expr *suffixEx = CE->getArg((unsigned)ArgSuffix.second); 631 Expr::EvalResult EVResult; 632 if (!suffixEx->EvaluateAsInt(EVResult, BR.getContext())) 633 return; 634 llvm::APSInt Result = EVResult.Val.getInt(); 635 // FIXME: Issue a warning. 636 if (Result.isNegative()) 637 return; 638 suffix = (unsigned) Result.getZExtValue(); 639 n = (n > suffix) ? n - suffix : 0; 640 } 641 642 for (unsigned i = 0; i < n; ++i) 643 if (str[i] == 'X') ++numX; 644 645 if (numX >= 6) 646 return; 647 648 // Issue a warning. 649 PathDiagnosticLocation CELoc = 650 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); 651 SmallString<512> buf; 652 llvm::raw_svector_ostream out(buf); 653 out << "Call to '" << Name << "' should have at least 6 'X's in the" 654 " format string to be secure (" << numX << " 'X'"; 655 if (numX != 1) 656 out << 's'; 657 out << " seen"; 658 if (suffix) { 659 out << ", " << suffix << " character"; 660 if (suffix > 1) 661 out << 's'; 662 out << " used as a suffix"; 663 } 664 out << ')'; 665 BR.EmitBasicReport(AC->getDecl(), filter.checkName_mkstemp, 666 "Insecure temporary file creation", "Security", 667 out.str(), CELoc, strArg->getSourceRange()); 668 } 669 670 //===----------------------------------------------------------------------===// 671 // Check: Any use of 'strcpy' is insecure. 672 // 673 // CWE-119: Improper Restriction of Operations within 674 // the Bounds of a Memory Buffer 675 //===----------------------------------------------------------------------===// 676 677 void WalkAST::checkCall_strcpy(const CallExpr *CE, const FunctionDecl *FD) { 678 if (!filter.check_strcpy) 679 return; 680 681 if (!checkCall_strCommon(CE, FD)) 682 return; 683 684 const auto *Target = CE->getArg(0)->IgnoreImpCasts(), 685 *Source = CE->getArg(1)->IgnoreImpCasts(); 686 687 if (const auto *Array = dyn_cast<ConstantArrayType>(Target->getType())) { 688 uint64_t ArraySize = BR.getContext().getTypeSize(Array) / 8; 689 if (const auto *String = dyn_cast<StringLiteral>(Source)) { 690 if (ArraySize >= String->getLength() + 1) 691 return; 692 } 693 } 694 695 // Issue a warning. 696 PathDiagnosticLocation CELoc = 697 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); 698 BR.EmitBasicReport(AC->getDecl(), filter.checkName_strcpy, 699 "Potential insecure memory buffer bounds restriction in " 700 "call 'strcpy'", 701 "Security", 702 "Call to function 'strcpy' is insecure as it does not " 703 "provide bounding of the memory buffer. Replace " 704 "unbounded copy functions with analogous functions that " 705 "support length arguments such as 'strlcpy'. CWE-119.", 706 CELoc, CE->getCallee()->getSourceRange()); 707 } 708 709 //===----------------------------------------------------------------------===// 710 // Check: Any use of 'strcat' is insecure. 711 // 712 // CWE-119: Improper Restriction of Operations within 713 // the Bounds of a Memory Buffer 714 //===----------------------------------------------------------------------===// 715 716 void WalkAST::checkCall_strcat(const CallExpr *CE, const FunctionDecl *FD) { 717 if (!filter.check_strcpy) 718 return; 719 720 if (!checkCall_strCommon(CE, FD)) 721 return; 722 723 // Issue a warning. 724 PathDiagnosticLocation CELoc = 725 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); 726 BR.EmitBasicReport(AC->getDecl(), filter.checkName_strcpy, 727 "Potential insecure memory buffer bounds restriction in " 728 "call 'strcat'", 729 "Security", 730 "Call to function 'strcat' is insecure as it does not " 731 "provide bounding of the memory buffer. Replace " 732 "unbounded copy functions with analogous functions that " 733 "support length arguments such as 'strlcat'. CWE-119.", 734 CELoc, CE->getCallee()->getSourceRange()); 735 } 736 737 //===----------------------------------------------------------------------===// 738 // Check: Any use of 'sprintf', 'vsprintf', 'scanf', 'wscanf', 'fscanf', 739 // 'fwscanf', 'vscanf', 'vwscanf', 'vfscanf', 'vfwscanf', 'sscanf', 740 // 'swscanf', 'vsscanf', 'vswscanf', 'swprintf', 'snprintf', 'vswprintf', 741 // 'vsnprintf', 'memcpy', 'memmove', 'strncpy', 'strncat', 'memset', 742 // 'fprintf' is deprecated since C11. 743 // 744 // Use of 'sprintf', 'fprintf', 'vsprintf', 'scanf', 'wscanf', 'fscanf', 745 // 'fwscanf', 'vscanf', 'vwscanf', 'vfscanf', 'vfwscanf', 'sscanf', 746 // 'swscanf', 'vsscanf', 'vswscanf' without buffer limitations 747 // is insecure. 748 // 749 // CWE-119: Improper Restriction of Operations within 750 // the Bounds of a Memory Buffer 751 //===----------------------------------------------------------------------===// 752 753 void WalkAST::checkDeprecatedOrUnsafeBufferHandling(const CallExpr *CE, 754 const FunctionDecl *FD) { 755 if (!filter.check_DeprecatedOrUnsafeBufferHandling) 756 return; 757 758 if (!BR.getContext().getLangOpts().C11) 759 return; 760 761 // Issue a warning. ArgIndex == -1: Deprecated but not unsafe (has size 762 // restrictions). 763 enum { DEPR_ONLY = -1, UNKNOWN_CALL = -2 }; 764 765 StringRef Name = FD->getIdentifier()->getName(); 766 if (Name.starts_with("__builtin_")) 767 Name = Name.substr(10); 768 769 int ArgIndex = 770 llvm::StringSwitch<int>(Name) 771 .Cases("scanf", "wscanf", "vscanf", "vwscanf", 0) 772 .Cases("fscanf", "fwscanf", "vfscanf", "vfwscanf", "sscanf", 773 "swscanf", "vsscanf", "vswscanf", 1) 774 .Cases("sprintf", "vsprintf", "fprintf", 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 // i.e. rand(), random(). 852 // 853 // E. Bach, "Efficient prediction of Marsaglia-Zaman random number generators," 854 // in IEEE Transactions on Information Theory, vol. 44, no. 3, pp. 1253-1257, 855 // May 1998, https://doi.org/10.1109/18.669305 856 // 857 // CWE-338: Use of cryptographically weak prng 858 //===----------------------------------------------------------------------===// 859 860 void WalkAST::checkCall_rand(const CallExpr *CE, const FunctionDecl *FD) { 861 if (!filter.check_rand || !CheckRand) 862 return; 863 864 const FunctionProtoType *FTP = FD->getType()->getAs<FunctionProtoType>(); 865 if (!FTP) 866 return; 867 868 if (FTP->getNumParams() == 1) { 869 // Is the argument an 'unsigned short *'? 870 // (Actually any integer type is allowed.) 871 const PointerType *PT = FTP->getParamType(0)->getAs<PointerType>(); 872 if (!PT) 873 return; 874 875 if (! PT->getPointeeType()->isIntegralOrUnscopedEnumerationType()) 876 return; 877 } else if (FTP->getNumParams() != 0) 878 return; 879 880 // Issue a warning. 881 SmallString<256> buf1; 882 llvm::raw_svector_ostream os1(buf1); 883 os1 << '\'' << *FD << "' is a poor random number generator"; 884 885 SmallString<256> buf2; 886 llvm::raw_svector_ostream os2(buf2); 887 os2 << "Function '" << *FD 888 << "' is obsolete because it implements a poor random number generator." 889 << " Use 'arc4random' instead"; 890 891 PathDiagnosticLocation CELoc = 892 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); 893 BR.EmitBasicReport(AC->getDecl(), filter.checkName_rand, os1.str(), 894 "Security", os2.str(), CELoc, 895 CE->getCallee()->getSourceRange()); 896 } 897 898 // See justification for rand(). 899 void WalkAST::checkCall_random(const CallExpr *CE, const FunctionDecl *FD) { 900 if (!CheckRand || !filter.check_rand) 901 return; 902 903 const FunctionProtoType *FTP = FD->getType()->getAs<FunctionProtoType>(); 904 if (!FTP) 905 return; 906 907 // Verify that the function takes no argument. 908 if (FTP->getNumParams() != 0) 909 return; 910 911 // Issue a warning. 912 PathDiagnosticLocation CELoc = 913 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); 914 BR.EmitBasicReport(AC->getDecl(), filter.checkName_rand, 915 "'random' is not a secure random number generator", 916 "Security", 917 "The 'random' function produces a sequence of values that " 918 "an adversary may be able to predict. Use 'arc4random' " 919 "instead", CELoc, CE->getCallee()->getSourceRange()); 920 } 921 922 //===----------------------------------------------------------------------===// 923 // Check: 'vfork' should not be used. 924 // POS33-C: Do not use vfork(). 925 //===----------------------------------------------------------------------===// 926 927 void WalkAST::checkCall_vfork(const CallExpr *CE, const FunctionDecl *FD) { 928 if (!filter.check_vfork) 929 return; 930 931 // All calls to vfork() are insecure, issue a warning. 932 PathDiagnosticLocation CELoc = 933 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); 934 BR.EmitBasicReport(AC->getDecl(), filter.checkName_vfork, 935 "Potential insecure implementation-specific behavior in " 936 "call 'vfork'", 937 "Security", 938 "Call to function 'vfork' is insecure as it can lead to " 939 "denial of service situations in the parent process. " 940 "Replace calls to vfork with calls to the safer " 941 "'posix_spawn' function", 942 CELoc, CE->getCallee()->getSourceRange()); 943 } 944 945 //===----------------------------------------------------------------------===// 946 // Check: '-decodeValueOfObjCType:at:' should not be used. 947 // It is deprecated in favor of '-decodeValueOfObjCType:at:size:' due to 948 // likelihood of buffer overflows. 949 //===----------------------------------------------------------------------===// 950 951 void WalkAST::checkMsg_decodeValueOfObjCType(const ObjCMessageExpr *ME) { 952 if (!filter.check_decodeValueOfObjCType) 953 return; 954 955 // Check availability of the secure alternative: 956 // iOS 11+, macOS 10.13+, tvOS 11+, and watchOS 4.0+ 957 // FIXME: We probably shouldn't register the check if it's not available. 958 const TargetInfo &TI = AC->getASTContext().getTargetInfo(); 959 const llvm::Triple &T = TI.getTriple(); 960 const VersionTuple &VT = TI.getPlatformMinVersion(); 961 switch (T.getOS()) { 962 case llvm::Triple::IOS: 963 if (VT < VersionTuple(11, 0)) 964 return; 965 break; 966 case llvm::Triple::MacOSX: 967 if (VT < VersionTuple(10, 13)) 968 return; 969 break; 970 case llvm::Triple::WatchOS: 971 if (VT < VersionTuple(4, 0)) 972 return; 973 break; 974 case llvm::Triple::TvOS: 975 if (VT < VersionTuple(11, 0)) 976 return; 977 break; 978 default: 979 return; 980 } 981 982 PathDiagnosticLocation MELoc = 983 PathDiagnosticLocation::createBegin(ME, BR.getSourceManager(), AC); 984 BR.EmitBasicReport( 985 AC->getDecl(), filter.checkName_decodeValueOfObjCType, 986 "Potential buffer overflow in '-decodeValueOfObjCType:at:'", "Security", 987 "Deprecated method '-decodeValueOfObjCType:at:' is insecure " 988 "as it can lead to potential buffer overflows. Use the safer " 989 "'-decodeValueOfObjCType:at:size:' method.", 990 MELoc, ME->getSourceRange()); 991 } 992 993 //===----------------------------------------------------------------------===// 994 // Check: The caller should always verify that the privileges 995 // were dropped successfully. 996 // 997 // Some library functions, like setuid() and setgid(), should always be used 998 // with a check of the return value to verify that the function completed 999 // successfully. If the drop fails, the software will continue to run 1000 // with the raised privileges, which might provide additional access 1001 // to unprivileged users. 1002 // 1003 // (Note that this check predates __attribute__((warn_unused_result)). 1004 // Do we still need it now that we have a compiler warning for this? 1005 // Are these standard functions already annotated this way?) 1006 //===----------------------------------------------------------------------===// 1007 1008 void WalkAST::checkUncheckedReturnValue(CallExpr *CE) { 1009 if (!filter.check_UncheckedReturn) 1010 return; 1011 1012 const FunctionDecl *FD = CE->getDirectCallee(); 1013 if (!FD) 1014 return; 1015 1016 if (II_setid[0] == nullptr) { 1017 static const char * const identifiers[num_setids] = { 1018 "setuid", "setgid", "seteuid", "setegid", 1019 "setreuid", "setregid" 1020 }; 1021 1022 for (size_t i = 0; i < num_setids; i++) 1023 II_setid[i] = &BR.getContext().Idents.get(identifiers[i]); 1024 } 1025 1026 const IdentifierInfo *id = FD->getIdentifier(); 1027 size_t identifierid; 1028 1029 for (identifierid = 0; identifierid < num_setids; identifierid++) 1030 if (id == II_setid[identifierid]) 1031 break; 1032 1033 if (identifierid >= num_setids) 1034 return; 1035 1036 const FunctionProtoType *FTP = FD->getType()->getAs<FunctionProtoType>(); 1037 if (!FTP) 1038 return; 1039 1040 // Verify that the function takes one or two arguments (depending on 1041 // the function). 1042 if (FTP->getNumParams() != (identifierid < 4 ? 1 : 2)) 1043 return; 1044 1045 // The arguments must be integers. 1046 for (unsigned i = 0; i < FTP->getNumParams(); i++) 1047 if (!FTP->getParamType(i)->isIntegralOrUnscopedEnumerationType()) 1048 return; 1049 1050 // Issue a warning. 1051 SmallString<256> buf1; 1052 llvm::raw_svector_ostream os1(buf1); 1053 os1 << "Return value is not checked in call to '" << *FD << '\''; 1054 1055 SmallString<256> buf2; 1056 llvm::raw_svector_ostream os2(buf2); 1057 os2 << "The return value from the call to '" << *FD 1058 << "' is not checked. If an error occurs in '" << *FD 1059 << "', the following code may execute with unexpected privileges"; 1060 1061 PathDiagnosticLocation CELoc = 1062 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); 1063 BR.EmitBasicReport(AC->getDecl(), filter.checkName_UncheckedReturn, os1.str(), 1064 "Security", os2.str(), CELoc, 1065 CE->getCallee()->getSourceRange()); 1066 } 1067 1068 //===----------------------------------------------------------------------===// 1069 // SecuritySyntaxChecker 1070 //===----------------------------------------------------------------------===// 1071 1072 namespace { 1073 class SecuritySyntaxChecker : public Checker<check::ASTCodeBody> { 1074 public: 1075 ChecksFilter filter; 1076 1077 void checkASTCodeBody(const Decl *D, AnalysisManager& mgr, 1078 BugReporter &BR) const { 1079 WalkAST walker(BR, mgr.getAnalysisDeclContext(D), filter); 1080 walker.Visit(D->getBody()); 1081 } 1082 }; 1083 } 1084 1085 void ento::registerSecuritySyntaxChecker(CheckerManager &mgr) { 1086 mgr.registerChecker<SecuritySyntaxChecker>(); 1087 } 1088 1089 bool ento::shouldRegisterSecuritySyntaxChecker(const CheckerManager &mgr) { 1090 return true; 1091 } 1092 1093 #define REGISTER_CHECKER(name) \ 1094 void ento::register##name(CheckerManager &mgr) { \ 1095 SecuritySyntaxChecker *checker = mgr.getChecker<SecuritySyntaxChecker>(); \ 1096 checker->filter.check_##name = true; \ 1097 checker->filter.checkName_##name = mgr.getCurrentCheckerName(); \ 1098 } \ 1099 \ 1100 bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; } 1101 1102 REGISTER_CHECKER(bcmp) 1103 REGISTER_CHECKER(bcopy) 1104 REGISTER_CHECKER(bzero) 1105 REGISTER_CHECKER(gets) 1106 REGISTER_CHECKER(getpw) 1107 REGISTER_CHECKER(mkstemp) 1108 REGISTER_CHECKER(mktemp) 1109 REGISTER_CHECKER(strcpy) 1110 REGISTER_CHECKER(rand) 1111 REGISTER_CHECKER(vfork) 1112 REGISTER_CHECKER(FloatLoopCounter) 1113 REGISTER_CHECKER(UncheckedReturn) 1114 REGISTER_CHECKER(DeprecatedOrUnsafeBufferHandling) 1115 REGISTER_CHECKER(decodeValueOfObjCType) 1116