1 //==- CheckSecuritySyntaxOnly.cpp - Basic security checks --------*- C++ -*-==// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 // 10 // This file defines a set of flow-insensitive security checks. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #include "ClangSACheckers.h" 15 #include "clang/StaticAnalyzer/Core/Checker.h" 16 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" 17 #include "clang/Basic/TargetInfo.h" 18 #include "clang/AST/StmtVisitor.h" 19 #include "llvm/Support/raw_ostream.h" 20 21 using namespace clang; 22 using namespace ento; 23 24 static bool isArc4RandomAvailable(const ASTContext &Ctx) { 25 const llvm::Triple &T = Ctx.Target.getTriple(); 26 return T.getVendor() == llvm::Triple::Apple || 27 T.getOS() == llvm::Triple::FreeBSD || 28 T.getOS() == llvm::Triple::NetBSD || 29 T.getOS() == llvm::Triple::OpenBSD || 30 T.getOS() == llvm::Triple::DragonFly; 31 } 32 33 namespace { 34 class WalkAST : public StmtVisitor<WalkAST> { 35 BugReporter &BR; 36 IdentifierInfo *II_gets; 37 IdentifierInfo *II_getpw; 38 IdentifierInfo *II_mktemp; 39 enum { num_rands = 9 }; 40 IdentifierInfo *II_rand[num_rands]; 41 IdentifierInfo *II_random; 42 enum { num_setids = 6 }; 43 IdentifierInfo *II_setid[num_setids]; 44 45 const bool CheckRand; 46 47 public: 48 WalkAST(BugReporter &br) : BR(br), 49 II_gets(0), II_getpw(0), II_mktemp(0), 50 II_rand(), II_random(0), II_setid(), 51 CheckRand(isArc4RandomAvailable(BR.getContext())) {} 52 53 // Statement visitor methods. 54 void VisitCallExpr(CallExpr *CE); 55 void VisitForStmt(ForStmt *S); 56 void VisitCompoundStmt (CompoundStmt *S); 57 void VisitStmt(Stmt *S) { VisitChildren(S); } 58 59 void VisitChildren(Stmt *S); 60 61 // Helpers. 62 IdentifierInfo *GetIdentifier(IdentifierInfo *& II, const char *str); 63 64 // Checker-specific methods. 65 void CheckLoopConditionForFloat(const ForStmt *FS); 66 void CheckCall_gets(const CallExpr *CE, const FunctionDecl *FD); 67 void CheckCall_getpw(const CallExpr *CE, const FunctionDecl *FD); 68 void CheckCall_mktemp(const CallExpr *CE, const FunctionDecl *FD); 69 void CheckCall_strcpy(const CallExpr *CE, const FunctionDecl *FD); 70 void CheckCall_rand(const CallExpr *CE, const FunctionDecl *FD); 71 void CheckCall_random(const CallExpr *CE, const FunctionDecl *FD); 72 void CheckUncheckedReturnValue(CallExpr *CE); 73 }; 74 } // end anonymous namespace 75 76 //===----------------------------------------------------------------------===// 77 // Helper methods. 78 //===----------------------------------------------------------------------===// 79 80 IdentifierInfo *WalkAST::GetIdentifier(IdentifierInfo *& II, const char *str) { 81 if (!II) 82 II = &BR.getContext().Idents.get(str); 83 84 return II; 85 } 86 87 //===----------------------------------------------------------------------===// 88 // AST walking. 89 //===----------------------------------------------------------------------===// 90 91 void WalkAST::VisitChildren(Stmt *S) { 92 for (Stmt::child_iterator I = S->child_begin(), E = S->child_end(); I!=E; ++I) 93 if (Stmt *child = *I) 94 Visit(child); 95 } 96 97 void WalkAST::VisitCallExpr(CallExpr *CE) { 98 if (const FunctionDecl *FD = CE->getDirectCallee()) { 99 CheckCall_gets(CE, FD); 100 CheckCall_getpw(CE, FD); 101 CheckCall_mktemp(CE, FD); 102 CheckCall_strcpy(CE, FD); 103 if (CheckRand) { 104 CheckCall_rand(CE, FD); 105 CheckCall_random(CE, FD); 106 } 107 } 108 109 // Recurse and check children. 110 VisitChildren(CE); 111 } 112 113 void WalkAST::VisitCompoundStmt(CompoundStmt *S) { 114 for (Stmt::child_iterator I = S->child_begin(), E = S->child_end(); I!=E; ++I) 115 if (Stmt *child = *I) { 116 if (CallExpr *CE = dyn_cast<CallExpr>(child)) 117 CheckUncheckedReturnValue(CE); 118 Visit(child); 119 } 120 } 121 122 void WalkAST::VisitForStmt(ForStmt *FS) { 123 CheckLoopConditionForFloat(FS); 124 125 // Recurse and check children. 126 VisitChildren(FS); 127 } 128 129 //===----------------------------------------------------------------------===// 130 // Check: floating poing variable used as loop counter. 131 // Originally: <rdar://problem/6336718> 132 // Implements: CERT security coding advisory FLP-30. 133 //===----------------------------------------------------------------------===// 134 135 static const DeclRefExpr* 136 GetIncrementedVar(const Expr *expr, const VarDecl *x, const VarDecl *y) { 137 expr = expr->IgnoreParenCasts(); 138 139 if (const BinaryOperator *B = dyn_cast<BinaryOperator>(expr)) { 140 if (!(B->isAssignmentOp() || B->isCompoundAssignmentOp() || 141 B->getOpcode() == BO_Comma)) 142 return NULL; 143 144 if (const DeclRefExpr *lhs = GetIncrementedVar(B->getLHS(), x, y)) 145 return lhs; 146 147 if (const DeclRefExpr *rhs = GetIncrementedVar(B->getRHS(), x, y)) 148 return rhs; 149 150 return NULL; 151 } 152 153 if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(expr)) { 154 const NamedDecl *ND = DR->getDecl(); 155 return ND == x || ND == y ? DR : NULL; 156 } 157 158 if (const UnaryOperator *U = dyn_cast<UnaryOperator>(expr)) 159 return U->isIncrementDecrementOp() 160 ? GetIncrementedVar(U->getSubExpr(), x, y) : NULL; 161 162 return NULL; 163 } 164 165 /// CheckLoopConditionForFloat - This check looks for 'for' statements that 166 /// use a floating point variable as a loop counter. 167 /// CERT: FLP30-C, FLP30-CPP. 168 /// 169 void WalkAST::CheckLoopConditionForFloat(const ForStmt *FS) { 170 // Does the loop have a condition? 171 const Expr *condition = FS->getCond(); 172 173 if (!condition) 174 return; 175 176 // Does the loop have an increment? 177 const Expr *increment = FS->getInc(); 178 179 if (!increment) 180 return; 181 182 // Strip away '()' and casts. 183 condition = condition->IgnoreParenCasts(); 184 increment = increment->IgnoreParenCasts(); 185 186 // Is the loop condition a comparison? 187 const BinaryOperator *B = dyn_cast<BinaryOperator>(condition); 188 189 if (!B) 190 return; 191 192 // Is this a comparison? 193 if (!(B->isRelationalOp() || B->isEqualityOp())) 194 return; 195 196 // Are we comparing variables? 197 const DeclRefExpr *drLHS = 198 dyn_cast<DeclRefExpr>(B->getLHS()->IgnoreParenLValueCasts()); 199 const DeclRefExpr *drRHS = 200 dyn_cast<DeclRefExpr>(B->getRHS()->IgnoreParenLValueCasts()); 201 202 // Does at least one of the variables have a floating point type? 203 drLHS = drLHS && drLHS->getType()->isRealFloatingType() ? drLHS : NULL; 204 drRHS = drRHS && drRHS->getType()->isRealFloatingType() ? drRHS : NULL; 205 206 if (!drLHS && !drRHS) 207 return; 208 209 const VarDecl *vdLHS = drLHS ? dyn_cast<VarDecl>(drLHS->getDecl()) : NULL; 210 const VarDecl *vdRHS = drRHS ? dyn_cast<VarDecl>(drRHS->getDecl()) : NULL; 211 212 if (!vdLHS && !vdRHS) 213 return; 214 215 // Does either variable appear in increment? 216 const DeclRefExpr *drInc = GetIncrementedVar(increment, vdLHS, vdRHS); 217 218 if (!drInc) 219 return; 220 221 // Emit the error. First figure out which DeclRefExpr in the condition 222 // referenced the compared variable. 223 const DeclRefExpr *drCond = vdLHS == drInc->getDecl() ? drLHS : drRHS; 224 225 llvm::SmallVector<SourceRange, 2> ranges; 226 llvm::SmallString<256> sbuf; 227 llvm::raw_svector_ostream os(sbuf); 228 229 os << "Variable '" << drCond->getDecl()->getName() 230 << "' with floating point type '" << drCond->getType().getAsString() 231 << "' should not be used as a loop counter"; 232 233 ranges.push_back(drCond->getSourceRange()); 234 ranges.push_back(drInc->getSourceRange()); 235 236 const char *bugType = "Floating point variable used as loop counter"; 237 BR.EmitBasicReport(bugType, "Security", os.str(), 238 FS->getLocStart(), ranges.data(), ranges.size()); 239 } 240 241 //===----------------------------------------------------------------------===// 242 // Check: Any use of 'gets' is insecure. 243 // Originally: <rdar://problem/6335715> 244 // Implements (part of): 300-BSI (buildsecurityin.us-cert.gov) 245 // CWE-242: Use of Inherently Dangerous Function 246 //===----------------------------------------------------------------------===// 247 248 void WalkAST::CheckCall_gets(const CallExpr *CE, const FunctionDecl *FD) { 249 if (FD->getIdentifier() != GetIdentifier(II_gets, "gets")) 250 return; 251 252 const FunctionProtoType *FPT 253 = dyn_cast<FunctionProtoType>(FD->getType().IgnoreParens()); 254 if (!FPT) 255 return; 256 257 // Verify that the function takes a single argument. 258 if (FPT->getNumArgs() != 1) 259 return; 260 261 // Is the argument a 'char*'? 262 const PointerType *PT = dyn_cast<PointerType>(FPT->getArgType(0)); 263 if (!PT) 264 return; 265 266 if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy) 267 return; 268 269 // Issue a warning. 270 SourceRange R = CE->getCallee()->getSourceRange(); 271 BR.EmitBasicReport("Potential buffer overflow in call to 'gets'", 272 "Security", 273 "Call to function 'gets' is extremely insecure as it can " 274 "always result in a buffer overflow", 275 CE->getLocStart(), &R, 1); 276 } 277 278 //===----------------------------------------------------------------------===// 279 // Check: Any use of 'getpwd' is insecure. 280 // CWE-477: Use of Obsolete Functions 281 //===----------------------------------------------------------------------===// 282 283 void WalkAST::CheckCall_getpw(const CallExpr *CE, const FunctionDecl *FD) { 284 if (FD->getIdentifier() != GetIdentifier(II_getpw, "getpw")) 285 return; 286 287 const FunctionProtoType *FPT 288 = dyn_cast<FunctionProtoType>(FD->getType().IgnoreParens()); 289 if (!FPT) 290 return; 291 292 // Verify that the function takes two arguments. 293 if (FPT->getNumArgs() != 2) 294 return; 295 296 // Verify the first argument type is integer. 297 if (!FPT->getArgType(0)->isIntegerType()) 298 return; 299 300 // Verify the second argument type is char*. 301 const PointerType *PT = dyn_cast<PointerType>(FPT->getArgType(1)); 302 if (!PT) 303 return; 304 305 if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy) 306 return; 307 308 // Issue a warning. 309 SourceRange R = CE->getCallee()->getSourceRange(); 310 BR.EmitBasicReport("Potential buffer overflow in call to 'getpw'", 311 "Security", 312 "The getpw() function is dangerous as it may overflow the " 313 "provided buffer. It is obsoleted by getpwuid().", 314 CE->getLocStart(), &R, 1); 315 } 316 317 //===----------------------------------------------------------------------===// 318 // Check: Any use of 'mktemp' is insecure.It is obsoleted by mkstemp(). 319 // CWE-377: Insecure Temporary File 320 //===----------------------------------------------------------------------===// 321 322 void WalkAST::CheckCall_mktemp(const CallExpr *CE, const FunctionDecl *FD) { 323 if (FD->getIdentifier() != GetIdentifier(II_mktemp, "mktemp")) 324 return; 325 326 const FunctionProtoType *FPT 327 = dyn_cast<FunctionProtoType>(FD->getType().IgnoreParens()); 328 if(!FPT) 329 return; 330 331 // Verify that the function takes a single argument. 332 if (FPT->getNumArgs() != 1) 333 return; 334 335 // Verify that the argument is Pointer Type. 336 const PointerType *PT = dyn_cast<PointerType>(FPT->getArgType(0)); 337 if (!PT) 338 return; 339 340 // Verify that the argument is a 'char*'. 341 if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy) 342 return; 343 344 // Issue a waring. 345 SourceRange R = CE->getCallee()->getSourceRange(); 346 BR.EmitBasicReport("Potential insecure temporary file in call 'mktemp'", 347 "Security", 348 "Call to function 'mktemp' is insecure as it always " 349 "creates or uses insecure temporary file. Use 'mkstemp' instead", 350 CE->getLocStart(), &R, 1); 351 } 352 353 //===----------------------------------------------------------------------===// 354 // Check: Any use of 'strcpy' is insecure. 355 // 356 // CWE-119: Improper Restriction of Operations within 357 // the Bounds of a Memory Buffer 358 //===----------------------------------------------------------------------===// 359 void WalkAST::CheckCall_strcpy(const CallExpr *CE, const FunctionDecl *FD) { 360 IdentifierInfo *II = FD->getIdentifier(); 361 if (!II) // if no identifier, not a simple C function 362 return; 363 llvm::StringRef Name = II->getName(); 364 if (Name.startswith("__builtin_")) 365 Name = Name.substr(10); 366 367 if ((Name != "strcpy") && 368 (Name != "__strcpy_chk")) 369 return; 370 371 const FunctionProtoType *FPT 372 = dyn_cast<FunctionProtoType>(FD->getType().IgnoreParens()); 373 if (!FPT) 374 return; 375 376 // Verify the function takes two arguments 377 int numArgs = FPT->getNumArgs(); 378 if (numArgs != 2 && numArgs != 3) 379 return; 380 381 // Verify the type for both arguments 382 for (int i = 0; i < 2; i++) { 383 // Verify that the arguments are pointers 384 const PointerType *PT = dyn_cast<PointerType>(FPT->getArgType(i)); 385 if (!PT) 386 return; 387 388 // Verify that the argument is a 'char*'. 389 if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy) 390 return; 391 } 392 393 // Issue a warning 394 SourceRange R = CE->getCallee()->getSourceRange(); 395 BR.EmitBasicReport("Potential insecure memory buffer bounds restriction in " 396 "call 'strcpy'", 397 "Security", 398 "Call to function 'strcpy' is insecure as it does not " 399 "provide bounding of the memory buffer. Replace " 400 "unbounded copy functions with analogous functions that " 401 "support length arguments such as 'strncpy'. CWE-119.", 402 CE->getLocStart(), &R, 1); 403 } 404 405 //===----------------------------------------------------------------------===// 406 // Check: Linear congruent random number generators should not be used 407 // Originally: <rdar://problem/63371000> 408 // CWE-338: Use of cryptographically weak prng 409 //===----------------------------------------------------------------------===// 410 411 void WalkAST::CheckCall_rand(const CallExpr *CE, const FunctionDecl *FD) { 412 if (II_rand[0] == NULL) { 413 // This check applies to these functions 414 static const char * const identifiers[num_rands] = { 415 "drand48", "erand48", "jrand48", "lrand48", "mrand48", "nrand48", 416 "lcong48", 417 "rand", "rand_r" 418 }; 419 420 for (size_t i = 0; i < num_rands; i++) 421 II_rand[i] = &BR.getContext().Idents.get(identifiers[i]); 422 } 423 424 const IdentifierInfo *id = FD->getIdentifier(); 425 size_t identifierid; 426 427 for (identifierid = 0; identifierid < num_rands; identifierid++) 428 if (id == II_rand[identifierid]) 429 break; 430 431 if (identifierid >= num_rands) 432 return; 433 434 const FunctionProtoType *FTP 435 = dyn_cast<FunctionProtoType>(FD->getType().IgnoreParens()); 436 if (!FTP) 437 return; 438 439 if (FTP->getNumArgs() == 1) { 440 // Is the argument an 'unsigned short *'? 441 // (Actually any integer type is allowed.) 442 const PointerType *PT = dyn_cast<PointerType>(FTP->getArgType(0)); 443 if (!PT) 444 return; 445 446 if (! PT->getPointeeType()->isIntegerType()) 447 return; 448 } 449 else if (FTP->getNumArgs() != 0) 450 return; 451 452 // Issue a warning. 453 llvm::SmallString<256> buf1; 454 llvm::raw_svector_ostream os1(buf1); 455 os1 << '\'' << FD << "' is a poor random number generator"; 456 457 llvm::SmallString<256> buf2; 458 llvm::raw_svector_ostream os2(buf2); 459 os2 << "Function '" << FD 460 << "' is obsolete because it implements a poor random number generator." 461 << " Use 'arc4random' instead"; 462 463 SourceRange R = CE->getCallee()->getSourceRange(); 464 BR.EmitBasicReport(os1.str(), "Security", os2.str(),CE->getLocStart(), &R, 1); 465 } 466 467 //===----------------------------------------------------------------------===// 468 // Check: 'random' should not be used 469 // Originally: <rdar://problem/63371000> 470 //===----------------------------------------------------------------------===// 471 472 void WalkAST::CheckCall_random(const CallExpr *CE, const FunctionDecl *FD) { 473 if (FD->getIdentifier() != GetIdentifier(II_random, "random")) 474 return; 475 476 const FunctionProtoType *FTP 477 = dyn_cast<FunctionProtoType>(FD->getType().IgnoreParens()); 478 if (!FTP) 479 return; 480 481 // Verify that the function takes no argument. 482 if (FTP->getNumArgs() != 0) 483 return; 484 485 // Issue a warning. 486 SourceRange R = CE->getCallee()->getSourceRange(); 487 BR.EmitBasicReport("'random' is not a secure random number generator", 488 "Security", 489 "The 'random' function produces a sequence of values that " 490 "an adversary may be able to predict. Use 'arc4random' " 491 "instead", CE->getLocStart(), &R, 1); 492 } 493 494 //===----------------------------------------------------------------------===// 495 // Check: Should check whether privileges are dropped successfully. 496 // Originally: <rdar://problem/6337132> 497 //===----------------------------------------------------------------------===// 498 499 void WalkAST::CheckUncheckedReturnValue(CallExpr *CE) { 500 const FunctionDecl *FD = CE->getDirectCallee(); 501 if (!FD) 502 return; 503 504 if (II_setid[0] == NULL) { 505 static const char * const identifiers[num_setids] = { 506 "setuid", "setgid", "seteuid", "setegid", 507 "setreuid", "setregid" 508 }; 509 510 for (size_t i = 0; i < num_setids; i++) 511 II_setid[i] = &BR.getContext().Idents.get(identifiers[i]); 512 } 513 514 const IdentifierInfo *id = FD->getIdentifier(); 515 size_t identifierid; 516 517 for (identifierid = 0; identifierid < num_setids; identifierid++) 518 if (id == II_setid[identifierid]) 519 break; 520 521 if (identifierid >= num_setids) 522 return; 523 524 const FunctionProtoType *FTP 525 = dyn_cast<FunctionProtoType>(FD->getType().IgnoreParens()); 526 if (!FTP) 527 return; 528 529 // Verify that the function takes one or two arguments (depending on 530 // the function). 531 if (FTP->getNumArgs() != (identifierid < 4 ? 1 : 2)) 532 return; 533 534 // The arguments must be integers. 535 for (unsigned i = 0; i < FTP->getNumArgs(); i++) 536 if (! FTP->getArgType(i)->isIntegerType()) 537 return; 538 539 // Issue a warning. 540 llvm::SmallString<256> buf1; 541 llvm::raw_svector_ostream os1(buf1); 542 os1 << "Return value is not checked in call to '" << FD << '\''; 543 544 llvm::SmallString<256> buf2; 545 llvm::raw_svector_ostream os2(buf2); 546 os2 << "The return value from the call to '" << FD 547 << "' is not checked. If an error occurs in '" << FD 548 << "', the following code may execute with unexpected privileges"; 549 550 SourceRange R = CE->getCallee()->getSourceRange(); 551 BR.EmitBasicReport(os1.str(), "Security", os2.str(),CE->getLocStart(), &R, 1); 552 } 553 554 //===----------------------------------------------------------------------===// 555 // SecuritySyntaxChecker 556 //===----------------------------------------------------------------------===// 557 558 namespace { 559 class SecuritySyntaxChecker : public Checker<check::ASTCodeBody> { 560 public: 561 void checkASTCodeBody(const Decl *D, AnalysisManager& mgr, 562 BugReporter &BR) const { 563 WalkAST walker(BR); 564 walker.Visit(D->getBody()); 565 } 566 }; 567 } 568 569 void ento::registerSecuritySyntaxChecker(CheckerManager &mgr) { 570 mgr.registerChecker<SecuritySyntaxChecker>(); 571 } 572