UnixAPIChecker.cpp revision 224145
1240116Smarcel//= UnixAPIChecker.h - Checks preconditions for various Unix APIs --*- C++ -*-// 2240116Smarcel// 3240116Smarcel// The LLVM Compiler Infrastructure 4240116Smarcel// 5240116Smarcel// This file is distributed under the University of Illinois Open Source 6240116Smarcel// License. See LICENSE.TXT for details. 7240116Smarcel// 8240116Smarcel//===----------------------------------------------------------------------===// 9240116Smarcel// 10240116Smarcel// This defines UnixAPIChecker, which is an assortment of checks on calls 11240116Smarcel// to various, widely used UNIX/Posix functions. 12240116Smarcel// 13240116Smarcel//===----------------------------------------------------------------------===// 14240116Smarcel 15240116Smarcel#include "ClangSACheckers.h" 16240116Smarcel#include "clang/StaticAnalyzer/Core/Checker.h" 17240116Smarcel#include "clang/StaticAnalyzer/Core/CheckerManager.h" 18240116Smarcel#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 19240116Smarcel#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 20240116Smarcel#include "clang/Basic/TargetInfo.h" 21240116Smarcel#include "llvm/ADT/Optional.h" 22240116Smarcel#include "llvm/ADT/StringSwitch.h" 23240116Smarcel#include <fcntl.h> 24240116Smarcel 25240116Smarcelusing namespace clang; 26240116Smarcelusing namespace ento; 27240116Smarcelusing llvm::Optional; 28240116Smarcel 29240116Smarcelnamespace { 30240116Smarcelclass UnixAPIChecker : public Checker< check::PreStmt<CallExpr> > { 31240116Smarcel mutable llvm::OwningPtr<BugType> BT_open, BT_pthreadOnce, BT_mallocZero; 32240116Smarcel mutable Optional<uint64_t> Val_O_CREAT; 33240116Smarcel 34240116Smarcelpublic: 35240116Smarcel void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; 36240116Smarcel 37240116Smarcel void CheckOpen(CheckerContext &C, const CallExpr *CE) const; 38240116Smarcel void CheckPthreadOnce(CheckerContext &C, const CallExpr *CE) const; 39240116Smarcel void CheckMallocZero(CheckerContext &C, const CallExpr *CE) const; 40240116Smarcel 41240116Smarcel typedef void (UnixAPIChecker::*SubChecker)(CheckerContext &, 42240116Smarcel const CallExpr *) const; 43240116Smarcel}; 44240116Smarcel} //end anonymous namespace 45240116Smarcel 46240116Smarcel//===----------------------------------------------------------------------===// 47240116Smarcel// Utility functions. 48240116Smarcel//===----------------------------------------------------------------------===// 49 50static inline void LazyInitialize(llvm::OwningPtr<BugType> &BT, 51 const char *name) { 52 if (BT) 53 return; 54 BT.reset(new BugType(name, "Unix API")); 55} 56 57//===----------------------------------------------------------------------===// 58// "open" (man 2 open) 59//===----------------------------------------------------------------------===// 60 61void UnixAPIChecker::CheckOpen(CheckerContext &C, const CallExpr *CE) const { 62 // The definition of O_CREAT is platform specific. We need a better way 63 // of querying this information from the checking environment. 64 if (!Val_O_CREAT.hasValue()) { 65 if (C.getASTContext().Target.getTriple().getVendor() == llvm::Triple::Apple) 66 Val_O_CREAT = 0x0200; 67 else { 68 // FIXME: We need a more general way of getting the O_CREAT value. 69 // We could possibly grovel through the preprocessor state, but 70 // that would require passing the Preprocessor object to the ExprEngine. 71 return; 72 } 73 } 74 75 // Look at the 'oflags' argument for the O_CREAT flag. 76 const GRState *state = C.getState(); 77 78 if (CE->getNumArgs() < 2) { 79 // The frontend should issue a warning for this case, so this is a sanity 80 // check. 81 return; 82 } 83 84 // Now check if oflags has O_CREAT set. 85 const Expr *oflagsEx = CE->getArg(1); 86 const SVal V = state->getSVal(oflagsEx); 87 if (!isa<NonLoc>(V)) { 88 // The case where 'V' can be a location can only be due to a bad header, 89 // so in this case bail out. 90 return; 91 } 92 NonLoc oflags = cast<NonLoc>(V); 93 NonLoc ocreateFlag = 94 cast<NonLoc>(C.getSValBuilder().makeIntVal(Val_O_CREAT.getValue(), 95 oflagsEx->getType())); 96 SVal maskedFlagsUC = C.getSValBuilder().evalBinOpNN(state, BO_And, 97 oflags, ocreateFlag, 98 oflagsEx->getType()); 99 if (maskedFlagsUC.isUnknownOrUndef()) 100 return; 101 DefinedSVal maskedFlags = cast<DefinedSVal>(maskedFlagsUC); 102 103 // Check if maskedFlags is non-zero. 104 const GRState *trueState, *falseState; 105 llvm::tie(trueState, falseState) = state->assume(maskedFlags); 106 107 // Only emit an error if the value of 'maskedFlags' is properly 108 // constrained; 109 if (!(trueState && !falseState)) 110 return; 111 112 if (CE->getNumArgs() < 3) { 113 ExplodedNode *N = C.generateSink(trueState); 114 if (!N) 115 return; 116 117 LazyInitialize(BT_open, "Improper use of 'open'"); 118 119 RangedBugReport *report = 120 new RangedBugReport(*BT_open, 121 "Call to 'open' requires a third argument when " 122 "the 'O_CREAT' flag is set", N); 123 report->addRange(oflagsEx->getSourceRange()); 124 C.EmitReport(report); 125 } 126} 127 128//===----------------------------------------------------------------------===// 129// pthread_once 130//===----------------------------------------------------------------------===// 131 132void UnixAPIChecker::CheckPthreadOnce(CheckerContext &C, 133 const CallExpr *CE) const { 134 135 // This is similar to 'CheckDispatchOnce' in the MacOSXAPIChecker. 136 // They can possibly be refactored. 137 138 if (CE->getNumArgs() < 1) 139 return; 140 141 // Check if the first argument is stack allocated. If so, issue a warning 142 // because that's likely to be bad news. 143 const GRState *state = C.getState(); 144 const MemRegion *R = state->getSVal(CE->getArg(0)).getAsRegion(); 145 if (!R || !isa<StackSpaceRegion>(R->getMemorySpace())) 146 return; 147 148 ExplodedNode *N = C.generateSink(state); 149 if (!N) 150 return; 151 152 llvm::SmallString<256> S; 153 llvm::raw_svector_ostream os(S); 154 os << "Call to 'pthread_once' uses"; 155 if (const VarRegion *VR = dyn_cast<VarRegion>(R)) 156 os << " the local variable '" << VR->getDecl()->getName() << '\''; 157 else 158 os << " stack allocated memory"; 159 os << " for the \"control\" value. Using such transient memory for " 160 "the control value is potentially dangerous."; 161 if (isa<VarRegion>(R) && isa<StackLocalsSpaceRegion>(R->getMemorySpace())) 162 os << " Perhaps you intended to declare the variable as 'static'?"; 163 164 LazyInitialize(BT_pthreadOnce, "Improper use of 'pthread_once'"); 165 166 RangedBugReport *report = new RangedBugReport(*BT_pthreadOnce, os.str(), N); 167 report->addRange(CE->getArg(0)->getSourceRange()); 168 C.EmitReport(report); 169} 170 171//===----------------------------------------------------------------------===// 172// "malloc" with allocation size 0 173//===----------------------------------------------------------------------===// 174 175// FIXME: Eventually this should be rolled into the MallocChecker, but this 176// check is more basic and is valuable for widespread use. 177void UnixAPIChecker::CheckMallocZero(CheckerContext &C, 178 const CallExpr *CE) const { 179 180 // Sanity check that malloc takes one argument. 181 if (CE->getNumArgs() != 1) 182 return; 183 184 // Check if the allocation size is 0. 185 const GRState *state = C.getState(); 186 SVal argVal = state->getSVal(CE->getArg(0)); 187 188 if (argVal.isUnknownOrUndef()) 189 return; 190 191 const GRState *trueState, *falseState; 192 llvm::tie(trueState, falseState) = state->assume(cast<DefinedSVal>(argVal)); 193 194 // Is the value perfectly constrained to zero? 195 if (falseState && !trueState) { 196 ExplodedNode *N = C.generateSink(falseState); 197 if (!N) 198 return; 199 200 // FIXME: Add reference to CERT advisory, and/or C99 standard in bug 201 // output. 202 203 LazyInitialize(BT_mallocZero, "Undefined allocation of 0 bytes"); 204 205 EnhancedBugReport *report = 206 new EnhancedBugReport(*BT_mallocZero, "Call to 'malloc' has an allocation" 207 " size of 0 bytes", N); 208 report->addRange(CE->getArg(0)->getSourceRange()); 209 report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, 210 CE->getArg(0)); 211 C.EmitReport(report); 212 return; 213 } 214 // Assume the the value is non-zero going forward. 215 assert(trueState); 216 if (trueState != state) { 217 C.addTransition(trueState); 218 } 219} 220 221//===----------------------------------------------------------------------===// 222// Central dispatch function. 223//===----------------------------------------------------------------------===// 224 225void UnixAPIChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const { 226 // Get the callee. All the functions we care about are C functions 227 // with simple identifiers. 228 const GRState *state = C.getState(); 229 const Expr *Callee = CE->getCallee(); 230 const FunctionDecl *Fn = state->getSVal(Callee).getAsFunctionDecl(); 231 232 if (!Fn) 233 return; 234 235 const IdentifierInfo *FI = Fn->getIdentifier(); 236 if (!FI) 237 return; 238 239 SubChecker SC = 240 llvm::StringSwitch<SubChecker>(FI->getName()) 241 .Case("open", &UnixAPIChecker::CheckOpen) 242 .Case("pthread_once", &UnixAPIChecker::CheckPthreadOnce) 243 .Case("malloc", &UnixAPIChecker::CheckMallocZero) 244 .Default(NULL); 245 246 if (SC) 247 (this->*SC)(C, CE); 248} 249 250//===----------------------------------------------------------------------===// 251// Registration. 252//===----------------------------------------------------------------------===// 253 254void ento::registerUnixAPIChecker(CheckerManager &mgr) { 255 mgr.registerChecker<UnixAPIChecker>(); 256} 257