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