clang API Documentation
00001 //==- CheckSecuritySyntaxOnly.cpp - Basic security checks --------*- C++ -*-==// 00002 // 00003 // The LLVM Compiler Infrastructure 00004 // 00005 // This file is distributed under the University of Illinois Open Source 00006 // License. See LICENSE.TXT for details. 00007 // 00008 //===----------------------------------------------------------------------===// 00009 // 00010 // This file defines a set of flow-insensitive security checks. 00011 // 00012 //===----------------------------------------------------------------------===// 00013 00014 #include "ClangSACheckers.h" 00015 #include "clang/Analysis/AnalysisContext.h" 00016 #include "clang/AST/StmtVisitor.h" 00017 #include "clang/Basic/TargetInfo.h" 00018 #include "clang/StaticAnalyzer/Core/Checker.h" 00019 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" 00020 #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" 00021 #include "llvm/ADT/SmallString.h" 00022 #include "llvm/ADT/StringSwitch.h" 00023 #include "llvm/Support/raw_ostream.h" 00024 00025 using namespace clang; 00026 using namespace ento; 00027 00028 static bool isArc4RandomAvailable(const ASTContext &Ctx) { 00029 const llvm::Triple &T = Ctx.getTargetInfo().getTriple(); 00030 return T.getVendor() == llvm::Triple::Apple || 00031 T.getOS() == llvm::Triple::FreeBSD || 00032 T.getOS() == llvm::Triple::NetBSD || 00033 T.getOS() == llvm::Triple::OpenBSD || 00034 T.getOS() == llvm::Triple::DragonFly; 00035 } 00036 00037 namespace { 00038 struct DefaultBool { 00039 bool val; 00040 DefaultBool() : val(false) {} 00041 operator bool() const { return val; } 00042 DefaultBool &operator=(bool b) { val = b; return *this; } 00043 }; 00044 00045 struct ChecksFilter { 00046 DefaultBool check_gets; 00047 DefaultBool check_getpw; 00048 DefaultBool check_mktemp; 00049 DefaultBool check_mkstemp; 00050 DefaultBool check_strcpy; 00051 DefaultBool check_rand; 00052 DefaultBool check_vfork; 00053 DefaultBool check_FloatLoopCounter; 00054 DefaultBool check_UncheckedReturn; 00055 }; 00056 00057 class WalkAST : public StmtVisitor<WalkAST> { 00058 BugReporter &BR; 00059 AnalysisDeclContext* AC; 00060 enum { num_setids = 6 }; 00061 IdentifierInfo *II_setid[num_setids]; 00062 00063 const bool CheckRand; 00064 const ChecksFilter &filter; 00065 00066 public: 00067 WalkAST(BugReporter &br, AnalysisDeclContext* ac, 00068 const ChecksFilter &f) 00069 : BR(br), AC(ac), II_setid(), 00070 CheckRand(isArc4RandomAvailable(BR.getContext())), 00071 filter(f) {} 00072 00073 // Statement visitor methods. 00074 void VisitCallExpr(CallExpr *CE); 00075 void VisitForStmt(ForStmt *S); 00076 void VisitCompoundStmt (CompoundStmt *S); 00077 void VisitStmt(Stmt *S) { VisitChildren(S); } 00078 00079 void VisitChildren(Stmt *S); 00080 00081 // Helpers. 00082 bool checkCall_strCommon(const CallExpr *CE, const FunctionDecl *FD); 00083 00084 typedef void (WalkAST::*FnCheck)(const CallExpr *, 00085 const FunctionDecl *); 00086 00087 // Checker-specific methods. 00088 void checkLoopConditionForFloat(const ForStmt *FS); 00089 void checkCall_gets(const CallExpr *CE, const FunctionDecl *FD); 00090 void checkCall_getpw(const CallExpr *CE, const FunctionDecl *FD); 00091 void checkCall_mktemp(const CallExpr *CE, const FunctionDecl *FD); 00092 void checkCall_mkstemp(const CallExpr *CE, const FunctionDecl *FD); 00093 void checkCall_strcpy(const CallExpr *CE, const FunctionDecl *FD); 00094 void checkCall_strcat(const CallExpr *CE, const FunctionDecl *FD); 00095 void checkCall_rand(const CallExpr *CE, const FunctionDecl *FD); 00096 void checkCall_random(const CallExpr *CE, const FunctionDecl *FD); 00097 void checkCall_vfork(const CallExpr *CE, const FunctionDecl *FD); 00098 void checkUncheckedReturnValue(CallExpr *CE); 00099 }; 00100 } // end anonymous namespace 00101 00102 //===----------------------------------------------------------------------===// 00103 // AST walking. 00104 //===----------------------------------------------------------------------===// 00105 00106 void WalkAST::VisitChildren(Stmt *S) { 00107 for (Stmt::child_iterator I = S->child_begin(), E = S->child_end(); I!=E; ++I) 00108 if (Stmt *child = *I) 00109 Visit(child); 00110 } 00111 00112 void WalkAST::VisitCallExpr(CallExpr *CE) { 00113 // Get the callee. 00114 const FunctionDecl *FD = CE->getDirectCallee(); 00115 00116 if (!FD) 00117 return; 00118 00119 // Get the name of the callee. If it's a builtin, strip off the prefix. 00120 IdentifierInfo *II = FD->getIdentifier(); 00121 if (!II) // if no identifier, not a simple C function 00122 return; 00123 StringRef Name = II->getName(); 00124 if (Name.startswith("__builtin_")) 00125 Name = Name.substr(10); 00126 00127 // Set the evaluation function by switching on the callee name. 00128 FnCheck evalFunction = llvm::StringSwitch<FnCheck>(Name) 00129 .Case("gets", &WalkAST::checkCall_gets) 00130 .Case("getpw", &WalkAST::checkCall_getpw) 00131 .Case("mktemp", &WalkAST::checkCall_mktemp) 00132 .Case("mkstemp", &WalkAST::checkCall_mkstemp) 00133 .Case("mkdtemp", &WalkAST::checkCall_mkstemp) 00134 .Case("mkstemps", &WalkAST::checkCall_mkstemp) 00135 .Cases("strcpy", "__strcpy_chk", &WalkAST::checkCall_strcpy) 00136 .Cases("strcat", "__strcat_chk", &WalkAST::checkCall_strcat) 00137 .Case("drand48", &WalkAST::checkCall_rand) 00138 .Case("erand48", &WalkAST::checkCall_rand) 00139 .Case("jrand48", &WalkAST::checkCall_rand) 00140 .Case("lrand48", &WalkAST::checkCall_rand) 00141 .Case("mrand48", &WalkAST::checkCall_rand) 00142 .Case("nrand48", &WalkAST::checkCall_rand) 00143 .Case("lcong48", &WalkAST::checkCall_rand) 00144 .Case("rand", &WalkAST::checkCall_rand) 00145 .Case("rand_r", &WalkAST::checkCall_rand) 00146 .Case("random", &WalkAST::checkCall_random) 00147 .Case("vfork", &WalkAST::checkCall_vfork) 00148 .Default(NULL); 00149 00150 // If the callee isn't defined, it is not of security concern. 00151 // Check and evaluate the call. 00152 if (evalFunction) 00153 (this->*evalFunction)(CE, FD); 00154 00155 // Recurse and check children. 00156 VisitChildren(CE); 00157 } 00158 00159 void WalkAST::VisitCompoundStmt(CompoundStmt *S) { 00160 for (Stmt::child_iterator I = S->child_begin(), E = S->child_end(); I!=E; ++I) 00161 if (Stmt *child = *I) { 00162 if (CallExpr *CE = dyn_cast<CallExpr>(child)) 00163 checkUncheckedReturnValue(CE); 00164 Visit(child); 00165 } 00166 } 00167 00168 void WalkAST::VisitForStmt(ForStmt *FS) { 00169 checkLoopConditionForFloat(FS); 00170 00171 // Recurse and check children. 00172 VisitChildren(FS); 00173 } 00174 00175 //===----------------------------------------------------------------------===// 00176 // Check: floating poing variable used as loop counter. 00177 // Originally: <rdar://problem/6336718> 00178 // Implements: CERT security coding advisory FLP-30. 00179 //===----------------------------------------------------------------------===// 00180 00181 static const DeclRefExpr* 00182 getIncrementedVar(const Expr *expr, const VarDecl *x, const VarDecl *y) { 00183 expr = expr->IgnoreParenCasts(); 00184 00185 if (const BinaryOperator *B = dyn_cast<BinaryOperator>(expr)) { 00186 if (!(B->isAssignmentOp() || B->isCompoundAssignmentOp() || 00187 B->getOpcode() == BO_Comma)) 00188 return NULL; 00189 00190 if (const DeclRefExpr *lhs = getIncrementedVar(B->getLHS(), x, y)) 00191 return lhs; 00192 00193 if (const DeclRefExpr *rhs = getIncrementedVar(B->getRHS(), x, y)) 00194 return rhs; 00195 00196 return NULL; 00197 } 00198 00199 if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(expr)) { 00200 const NamedDecl *ND = DR->getDecl(); 00201 return ND == x || ND == y ? DR : NULL; 00202 } 00203 00204 if (const UnaryOperator *U = dyn_cast<UnaryOperator>(expr)) 00205 return U->isIncrementDecrementOp() 00206 ? getIncrementedVar(U->getSubExpr(), x, y) : NULL; 00207 00208 return NULL; 00209 } 00210 00211 /// CheckLoopConditionForFloat - This check looks for 'for' statements that 00212 /// use a floating point variable as a loop counter. 00213 /// CERT: FLP30-C, FLP30-CPP. 00214 /// 00215 void WalkAST::checkLoopConditionForFloat(const ForStmt *FS) { 00216 if (!filter.check_FloatLoopCounter) 00217 return; 00218 00219 // Does the loop have a condition? 00220 const Expr *condition = FS->getCond(); 00221 00222 if (!condition) 00223 return; 00224 00225 // Does the loop have an increment? 00226 const Expr *increment = FS->getInc(); 00227 00228 if (!increment) 00229 return; 00230 00231 // Strip away '()' and casts. 00232 condition = condition->IgnoreParenCasts(); 00233 increment = increment->IgnoreParenCasts(); 00234 00235 // Is the loop condition a comparison? 00236 const BinaryOperator *B = dyn_cast<BinaryOperator>(condition); 00237 00238 if (!B) 00239 return; 00240 00241 // Is this a comparison? 00242 if (!(B->isRelationalOp() || B->isEqualityOp())) 00243 return; 00244 00245 // Are we comparing variables? 00246 const DeclRefExpr *drLHS = 00247 dyn_cast<DeclRefExpr>(B->getLHS()->IgnoreParenLValueCasts()); 00248 const DeclRefExpr *drRHS = 00249 dyn_cast<DeclRefExpr>(B->getRHS()->IgnoreParenLValueCasts()); 00250 00251 // Does at least one of the variables have a floating point type? 00252 drLHS = drLHS && drLHS->getType()->isRealFloatingType() ? drLHS : NULL; 00253 drRHS = drRHS && drRHS->getType()->isRealFloatingType() ? drRHS : NULL; 00254 00255 if (!drLHS && !drRHS) 00256 return; 00257 00258 const VarDecl *vdLHS = drLHS ? dyn_cast<VarDecl>(drLHS->getDecl()) : NULL; 00259 const VarDecl *vdRHS = drRHS ? dyn_cast<VarDecl>(drRHS->getDecl()) : NULL; 00260 00261 if (!vdLHS && !vdRHS) 00262 return; 00263 00264 // Does either variable appear in increment? 00265 const DeclRefExpr *drInc = getIncrementedVar(increment, vdLHS, vdRHS); 00266 00267 if (!drInc) 00268 return; 00269 00270 // Emit the error. First figure out which DeclRefExpr in the condition 00271 // referenced the compared variable. 00272 const DeclRefExpr *drCond = vdLHS == drInc->getDecl() ? drLHS : drRHS; 00273 00274 SmallVector<SourceRange, 2> ranges; 00275 SmallString<256> sbuf; 00276 llvm::raw_svector_ostream os(sbuf); 00277 00278 os << "Variable '" << drCond->getDecl()->getName() 00279 << "' with floating point type '" << drCond->getType().getAsString() 00280 << "' should not be used as a loop counter"; 00281 00282 ranges.push_back(drCond->getSourceRange()); 00283 ranges.push_back(drInc->getSourceRange()); 00284 00285 const char *bugType = "Floating point variable used as loop counter"; 00286 00287 PathDiagnosticLocation FSLoc = 00288 PathDiagnosticLocation::createBegin(FS, BR.getSourceManager(), AC); 00289 BR.EmitBasicReport(AC->getDecl(), 00290 bugType, "Security", os.str(), 00291 FSLoc, ranges.data(), ranges.size()); 00292 } 00293 00294 //===----------------------------------------------------------------------===// 00295 // Check: Any use of 'gets' is insecure. 00296 // Originally: <rdar://problem/6335715> 00297 // Implements (part of): 300-BSI (buildsecurityin.us-cert.gov) 00298 // CWE-242: Use of Inherently Dangerous Function 00299 //===----------------------------------------------------------------------===// 00300 00301 void WalkAST::checkCall_gets(const CallExpr *CE, const FunctionDecl *FD) { 00302 if (!filter.check_gets) 00303 return; 00304 00305 const FunctionProtoType *FPT 00306 = dyn_cast<FunctionProtoType>(FD->getType().IgnoreParens()); 00307 if (!FPT) 00308 return; 00309 00310 // Verify that the function takes a single argument. 00311 if (FPT->getNumArgs() != 1) 00312 return; 00313 00314 // Is the argument a 'char*'? 00315 const PointerType *PT = dyn_cast<PointerType>(FPT->getArgType(0)); 00316 if (!PT) 00317 return; 00318 00319 if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy) 00320 return; 00321 00322 // Issue a warning. 00323 SourceRange R = CE->getCallee()->getSourceRange(); 00324 PathDiagnosticLocation CELoc = 00325 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); 00326 BR.EmitBasicReport(AC->getDecl(), 00327 "Potential buffer overflow in call to 'gets'", 00328 "Security", 00329 "Call to function 'gets' is extremely insecure as it can " 00330 "always result in a buffer overflow", 00331 CELoc, &R, 1); 00332 } 00333 00334 //===----------------------------------------------------------------------===// 00335 // Check: Any use of 'getpwd' is insecure. 00336 // CWE-477: Use of Obsolete Functions 00337 //===----------------------------------------------------------------------===// 00338 00339 void WalkAST::checkCall_getpw(const CallExpr *CE, const FunctionDecl *FD) { 00340 if (!filter.check_getpw) 00341 return; 00342 00343 const FunctionProtoType *FPT 00344 = dyn_cast<FunctionProtoType>(FD->getType().IgnoreParens()); 00345 if (!FPT) 00346 return; 00347 00348 // Verify that the function takes two arguments. 00349 if (FPT->getNumArgs() != 2) 00350 return; 00351 00352 // Verify the first argument type is integer. 00353 if (!FPT->getArgType(0)->isIntegerType()) 00354 return; 00355 00356 // Verify the second argument type is char*. 00357 const PointerType *PT = dyn_cast<PointerType>(FPT->getArgType(1)); 00358 if (!PT) 00359 return; 00360 00361 if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy) 00362 return; 00363 00364 // Issue a warning. 00365 SourceRange R = CE->getCallee()->getSourceRange(); 00366 PathDiagnosticLocation CELoc = 00367 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); 00368 BR.EmitBasicReport(AC->getDecl(), 00369 "Potential buffer overflow in call to 'getpw'", 00370 "Security", 00371 "The getpw() function is dangerous as it may overflow the " 00372 "provided buffer. It is obsoleted by getpwuid().", 00373 CELoc, &R, 1); 00374 } 00375 00376 //===----------------------------------------------------------------------===// 00377 // Check: Any use of 'mktemp' is insecure. It is obsoleted by mkstemp(). 00378 // CWE-377: Insecure Temporary File 00379 //===----------------------------------------------------------------------===// 00380 00381 void WalkAST::checkCall_mktemp(const CallExpr *CE, const FunctionDecl *FD) { 00382 if (!filter.check_mktemp) { 00383 // Fall back to the security check of looking for enough 'X's in the 00384 // format string, since that is a less severe warning. 00385 checkCall_mkstemp(CE, FD); 00386 return; 00387 } 00388 00389 const FunctionProtoType *FPT 00390 = dyn_cast<FunctionProtoType>(FD->getType().IgnoreParens()); 00391 if(!FPT) 00392 return; 00393 00394 // Verify that the function takes a single argument. 00395 if (FPT->getNumArgs() != 1) 00396 return; 00397 00398 // Verify that the argument is Pointer Type. 00399 const PointerType *PT = dyn_cast<PointerType>(FPT->getArgType(0)); 00400 if (!PT) 00401 return; 00402 00403 // Verify that the argument is a 'char*'. 00404 if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy) 00405 return; 00406 00407 // Issue a waring. 00408 SourceRange R = CE->getCallee()->getSourceRange(); 00409 PathDiagnosticLocation CELoc = 00410 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); 00411 BR.EmitBasicReport(AC->getDecl(), 00412 "Potential insecure temporary file in call 'mktemp'", 00413 "Security", 00414 "Call to function 'mktemp' is insecure as it always " 00415 "creates or uses insecure temporary file. Use 'mkstemp' " 00416 "instead", 00417 CELoc, &R, 1); 00418 } 00419 00420 00421 //===----------------------------------------------------------------------===// 00422 // Check: Use of 'mkstemp', 'mktemp', 'mkdtemp' should contain at least 6 X's. 00423 //===----------------------------------------------------------------------===// 00424 00425 void WalkAST::checkCall_mkstemp(const CallExpr *CE, const FunctionDecl *FD) { 00426 if (!filter.check_mkstemp) 00427 return; 00428 00429 StringRef Name = FD->getIdentifier()->getName(); 00430 std::pair<signed, signed> ArgSuffix = 00431 llvm::StringSwitch<std::pair<signed, signed> >(Name) 00432 .Case("mktemp", std::make_pair(0,-1)) 00433 .Case("mkstemp", std::make_pair(0,-1)) 00434 .Case("mkdtemp", std::make_pair(0,-1)) 00435 .Case("mkstemps", std::make_pair(0,1)) 00436 .Default(std::make_pair(-1, -1)); 00437 00438 assert(ArgSuffix.first >= 0 && "Unsupported function"); 00439 00440 // Check if the number of arguments is consistent with out expectations. 00441 unsigned numArgs = CE->getNumArgs(); 00442 if ((signed) numArgs <= ArgSuffix.first) 00443 return; 00444 00445 const StringLiteral *strArg = 00446 dyn_cast<StringLiteral>(CE->getArg((unsigned)ArgSuffix.first) 00447 ->IgnoreParenImpCasts()); 00448 00449 // Currently we only handle string literals. It is possible to do better, 00450 // either by looking at references to const variables, or by doing real 00451 // flow analysis. 00452 if (!strArg || strArg->getCharByteWidth() != 1) 00453 return; 00454 00455 // Count the number of X's, taking into account a possible cutoff suffix. 00456 StringRef str = strArg->getString(); 00457 unsigned numX = 0; 00458 unsigned n = str.size(); 00459 00460 // Take into account the suffix. 00461 unsigned suffix = 0; 00462 if (ArgSuffix.second >= 0) { 00463 const Expr *suffixEx = CE->getArg((unsigned)ArgSuffix.second); 00464 llvm::APSInt Result; 00465 if (!suffixEx->EvaluateAsInt(Result, BR.getContext())) 00466 return; 00467 // FIXME: Issue a warning. 00468 if (Result.isNegative()) 00469 return; 00470 suffix = (unsigned) Result.getZExtValue(); 00471 n = (n > suffix) ? n - suffix : 0; 00472 } 00473 00474 for (unsigned i = 0; i < n; ++i) 00475 if (str[i] == 'X') ++numX; 00476 00477 if (numX >= 6) 00478 return; 00479 00480 // Issue a warning. 00481 SourceRange R = strArg->getSourceRange(); 00482 PathDiagnosticLocation CELoc = 00483 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); 00484 SmallString<512> buf; 00485 llvm::raw_svector_ostream out(buf); 00486 out << "Call to '" << Name << "' should have at least 6 'X's in the" 00487 " format string to be secure (" << numX << " 'X'"; 00488 if (numX != 1) 00489 out << 's'; 00490 out << " seen"; 00491 if (suffix) { 00492 out << ", " << suffix << " character"; 00493 if (suffix > 1) 00494 out << 's'; 00495 out << " used as a suffix"; 00496 } 00497 out << ')'; 00498 BR.EmitBasicReport(AC->getDecl(), 00499 "Insecure temporary file creation", "Security", 00500 out.str(), CELoc, &R, 1); 00501 } 00502 00503 //===----------------------------------------------------------------------===// 00504 // Check: Any use of 'strcpy' is insecure. 00505 // 00506 // CWE-119: Improper Restriction of Operations within 00507 // the Bounds of a Memory Buffer 00508 //===----------------------------------------------------------------------===// 00509 void WalkAST::checkCall_strcpy(const CallExpr *CE, const FunctionDecl *FD) { 00510 if (!filter.check_strcpy) 00511 return; 00512 00513 if (!checkCall_strCommon(CE, FD)) 00514 return; 00515 00516 // Issue a warning. 00517 SourceRange R = CE->getCallee()->getSourceRange(); 00518 PathDiagnosticLocation CELoc = 00519 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); 00520 BR.EmitBasicReport(AC->getDecl(), 00521 "Potential insecure memory buffer bounds restriction in " 00522 "call 'strcpy'", 00523 "Security", 00524 "Call to function 'strcpy' is insecure as it does not " 00525 "provide bounding of the memory buffer. Replace " 00526 "unbounded copy functions with analogous functions that " 00527 "support length arguments such as 'strlcpy'. CWE-119.", 00528 CELoc, &R, 1); 00529 } 00530 00531 //===----------------------------------------------------------------------===// 00532 // Check: Any use of 'strcat' is insecure. 00533 // 00534 // CWE-119: Improper Restriction of Operations within 00535 // the Bounds of a Memory Buffer 00536 //===----------------------------------------------------------------------===// 00537 void WalkAST::checkCall_strcat(const CallExpr *CE, const FunctionDecl *FD) { 00538 if (!filter.check_strcpy) 00539 return; 00540 00541 if (!checkCall_strCommon(CE, FD)) 00542 return; 00543 00544 // Issue a warning. 00545 SourceRange R = CE->getCallee()->getSourceRange(); 00546 PathDiagnosticLocation CELoc = 00547 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); 00548 BR.EmitBasicReport(AC->getDecl(), 00549 "Potential insecure memory buffer bounds restriction in " 00550 "call 'strcat'", 00551 "Security", 00552 "Call to function 'strcat' is insecure as it does not " 00553 "provide bounding of the memory buffer. Replace " 00554 "unbounded copy functions with analogous functions that " 00555 "support length arguments such as 'strlcat'. CWE-119.", 00556 CELoc, &R, 1); 00557 } 00558 00559 //===----------------------------------------------------------------------===// 00560 // Common check for str* functions with no bounds parameters. 00561 //===----------------------------------------------------------------------===// 00562 bool WalkAST::checkCall_strCommon(const CallExpr *CE, const FunctionDecl *FD) { 00563 const FunctionProtoType *FPT 00564 = dyn_cast<FunctionProtoType>(FD->getType().IgnoreParens()); 00565 if (!FPT) 00566 return false; 00567 00568 // Verify the function takes two arguments, three in the _chk version. 00569 int numArgs = FPT->getNumArgs(); 00570 if (numArgs != 2 && numArgs != 3) 00571 return false; 00572 00573 // Verify the type for both arguments. 00574 for (int i = 0; i < 2; i++) { 00575 // Verify that the arguments are pointers. 00576 const PointerType *PT = dyn_cast<PointerType>(FPT->getArgType(i)); 00577 if (!PT) 00578 return false; 00579 00580 // Verify that the argument is a 'char*'. 00581 if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy) 00582 return false; 00583 } 00584 00585 return true; 00586 } 00587 00588 //===----------------------------------------------------------------------===// 00589 // Check: Linear congruent random number generators should not be used 00590 // Originally: <rdar://problem/63371000> 00591 // CWE-338: Use of cryptographically weak prng 00592 //===----------------------------------------------------------------------===// 00593 00594 void WalkAST::checkCall_rand(const CallExpr *CE, const FunctionDecl *FD) { 00595 if (!filter.check_rand || !CheckRand) 00596 return; 00597 00598 const FunctionProtoType *FTP 00599 = dyn_cast<FunctionProtoType>(FD->getType().IgnoreParens()); 00600 if (!FTP) 00601 return; 00602 00603 if (FTP->getNumArgs() == 1) { 00604 // Is the argument an 'unsigned short *'? 00605 // (Actually any integer type is allowed.) 00606 const PointerType *PT = dyn_cast<PointerType>(FTP->getArgType(0)); 00607 if (!PT) 00608 return; 00609 00610 if (! PT->getPointeeType()->isIntegerType()) 00611 return; 00612 } 00613 else if (FTP->getNumArgs() != 0) 00614 return; 00615 00616 // Issue a warning. 00617 SmallString<256> buf1; 00618 llvm::raw_svector_ostream os1(buf1); 00619 os1 << '\'' << *FD << "' is a poor random number generator"; 00620 00621 SmallString<256> buf2; 00622 llvm::raw_svector_ostream os2(buf2); 00623 os2 << "Function '" << *FD 00624 << "' is obsolete because it implements a poor random number generator." 00625 << " Use 'arc4random' instead"; 00626 00627 SourceRange R = CE->getCallee()->getSourceRange(); 00628 PathDiagnosticLocation CELoc = 00629 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); 00630 BR.EmitBasicReport(AC->getDecl(), os1.str(), "Security", os2.str(), 00631 CELoc, &R, 1); 00632 } 00633 00634 //===----------------------------------------------------------------------===// 00635 // Check: 'random' should not be used 00636 // Originally: <rdar://problem/63371000> 00637 //===----------------------------------------------------------------------===// 00638 00639 void WalkAST::checkCall_random(const CallExpr *CE, const FunctionDecl *FD) { 00640 if (!CheckRand || !filter.check_rand) 00641 return; 00642 00643 const FunctionProtoType *FTP 00644 = dyn_cast<FunctionProtoType>(FD->getType().IgnoreParens()); 00645 if (!FTP) 00646 return; 00647 00648 // Verify that the function takes no argument. 00649 if (FTP->getNumArgs() != 0) 00650 return; 00651 00652 // Issue a warning. 00653 SourceRange R = CE->getCallee()->getSourceRange(); 00654 PathDiagnosticLocation CELoc = 00655 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); 00656 BR.EmitBasicReport(AC->getDecl(), 00657 "'random' is not a secure random number generator", 00658 "Security", 00659 "The 'random' function produces a sequence of values that " 00660 "an adversary may be able to predict. Use 'arc4random' " 00661 "instead", CELoc, &R, 1); 00662 } 00663 00664 //===----------------------------------------------------------------------===// 00665 // Check: 'vfork' should not be used. 00666 // POS33-C: Do not use vfork(). 00667 //===----------------------------------------------------------------------===// 00668 00669 void WalkAST::checkCall_vfork(const CallExpr *CE, const FunctionDecl *FD) { 00670 if (!filter.check_vfork) 00671 return; 00672 00673 // All calls to vfork() are insecure, issue a warning. 00674 SourceRange R = CE->getCallee()->getSourceRange(); 00675 PathDiagnosticLocation CELoc = 00676 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); 00677 BR.EmitBasicReport(AC->getDecl(), 00678 "Potential insecure implementation-specific behavior in " 00679 "call 'vfork'", 00680 "Security", 00681 "Call to function 'vfork' is insecure as it can lead to " 00682 "denial of service situations in the parent process. " 00683 "Replace calls to vfork with calls to the safer " 00684 "'posix_spawn' function", 00685 CELoc, &R, 1); 00686 } 00687 00688 //===----------------------------------------------------------------------===// 00689 // Check: Should check whether privileges are dropped successfully. 00690 // Originally: <rdar://problem/6337132> 00691 //===----------------------------------------------------------------------===// 00692 00693 void WalkAST::checkUncheckedReturnValue(CallExpr *CE) { 00694 if (!filter.check_UncheckedReturn) 00695 return; 00696 00697 const FunctionDecl *FD = CE->getDirectCallee(); 00698 if (!FD) 00699 return; 00700 00701 if (II_setid[0] == NULL) { 00702 static const char * const identifiers[num_setids] = { 00703 "setuid", "setgid", "seteuid", "setegid", 00704 "setreuid", "setregid" 00705 }; 00706 00707 for (size_t i = 0; i < num_setids; i++) 00708 II_setid[i] = &BR.getContext().Idents.get(identifiers[i]); 00709 } 00710 00711 const IdentifierInfo *id = FD->getIdentifier(); 00712 size_t identifierid; 00713 00714 for (identifierid = 0; identifierid < num_setids; identifierid++) 00715 if (id == II_setid[identifierid]) 00716 break; 00717 00718 if (identifierid >= num_setids) 00719 return; 00720 00721 const FunctionProtoType *FTP 00722 = dyn_cast<FunctionProtoType>(FD->getType().IgnoreParens()); 00723 if (!FTP) 00724 return; 00725 00726 // Verify that the function takes one or two arguments (depending on 00727 // the function). 00728 if (FTP->getNumArgs() != (identifierid < 4 ? 1 : 2)) 00729 return; 00730 00731 // The arguments must be integers. 00732 for (unsigned i = 0; i < FTP->getNumArgs(); i++) 00733 if (! FTP->getArgType(i)->isIntegerType()) 00734 return; 00735 00736 // Issue a warning. 00737 SmallString<256> buf1; 00738 llvm::raw_svector_ostream os1(buf1); 00739 os1 << "Return value is not checked in call to '" << *FD << '\''; 00740 00741 SmallString<256> buf2; 00742 llvm::raw_svector_ostream os2(buf2); 00743 os2 << "The return value from the call to '" << *FD 00744 << "' is not checked. If an error occurs in '" << *FD 00745 << "', the following code may execute with unexpected privileges"; 00746 00747 SourceRange R = CE->getCallee()->getSourceRange(); 00748 PathDiagnosticLocation CELoc = 00749 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); 00750 BR.EmitBasicReport(AC->getDecl(), os1.str(), "Security", os2.str(), 00751 CELoc, &R, 1); 00752 } 00753 00754 //===----------------------------------------------------------------------===// 00755 // SecuritySyntaxChecker 00756 //===----------------------------------------------------------------------===// 00757 00758 namespace { 00759 class SecuritySyntaxChecker : public Checker<check::ASTCodeBody> { 00760 public: 00761 ChecksFilter filter; 00762 00763 void checkASTCodeBody(const Decl *D, AnalysisManager& mgr, 00764 BugReporter &BR) const { 00765 WalkAST walker(BR, mgr.getAnalysisDeclContext(D), filter); 00766 walker.Visit(D->getBody()); 00767 } 00768 }; 00769 } 00770 00771 #define REGISTER_CHECKER(name) \ 00772 void ento::register##name(CheckerManager &mgr) {\ 00773 mgr.registerChecker<SecuritySyntaxChecker>()->filter.check_##name = true;\ 00774 } 00775 00776 REGISTER_CHECKER(gets) 00777 REGISTER_CHECKER(getpw) 00778 REGISTER_CHECKER(mkstemp) 00779 REGISTER_CHECKER(mktemp) 00780 REGISTER_CHECKER(strcpy) 00781 REGISTER_CHECKER(rand) 00782 REGISTER_CHECKER(vfork) 00783 REGISTER_CHECKER(FloatLoopCounter) 00784 REGISTER_CHECKER(UncheckedReturn) 00785 00786