UnixAPIChecker.cpp revision 695fb502825a53ccd178ec1c85c77929d88acb71
1//= UnixAPIChecker.h - Checks preconditions for various Unix APIs --*- 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 defines UnixAPIChecker, which is an assortment of checks on calls 11// to various, widely used UNIX/Posix functions. 12// 13//===----------------------------------------------------------------------===// 14 15#include "ClangSACheckers.h" 16#include "clang/Basic/TargetInfo.h" 17#include "clang/StaticAnalyzer/Core/CheckerManager.h" 18#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 19#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerVisitor.h" 20#include "llvm/ADT/Optional.h" 21#include "llvm/ADT/StringSwitch.h" 22#include <fcntl.h> 23 24using namespace clang; 25using namespace ento; 26using llvm::Optional; 27 28namespace { 29class UnixAPIChecker : public CheckerVisitor<UnixAPIChecker> { 30 enum SubChecks { 31 OpenFn = 0, 32 PthreadOnceFn = 1, 33 MallocZero = 2, 34 NumChecks 35 }; 36 37 BugType *BTypes[NumChecks]; 38 39public: 40 Optional<uint64_t> Val_O_CREAT; 41 42public: 43 UnixAPIChecker() { memset(BTypes, 0, sizeof(*BTypes) * NumChecks); } 44 static void *getTag() { static unsigned tag = 0; return &tag; } 45 46 void PreVisitCallExpr(CheckerContext &C, const CallExpr *CE); 47}; 48} //end anonymous namespace 49 50static void RegisterUnixAPIChecker(ExprEngine &Eng) { 51 Eng.registerCheck(new UnixAPIChecker()); 52} 53 54void ento::registerUnixAPIChecker(CheckerManager &mgr) { 55 mgr.addCheckerRegisterFunction(RegisterUnixAPIChecker); 56} 57 58//===----------------------------------------------------------------------===// 59// Utility functions. 60//===----------------------------------------------------------------------===// 61 62static inline void LazyInitialize(BugType *&BT, const char *name) { 63 if (BT) 64 return; 65 BT = new BugType(name, "Unix API"); 66} 67 68//===----------------------------------------------------------------------===// 69// "open" (man 2 open) 70//===----------------------------------------------------------------------===// 71 72static void CheckOpen(CheckerContext &C, UnixAPIChecker &UC, 73 const CallExpr *CE, BugType *&BT) { 74 // The definition of O_CREAT is platform specific. We need a better way 75 // of querying this information from the checking environment. 76 if (!UC.Val_O_CREAT.hasValue()) { 77 if (C.getASTContext().Target.getTriple().getVendor() == llvm::Triple::Apple) 78 UC.Val_O_CREAT = 0x0200; 79 else { 80 // FIXME: We need a more general way of getting the O_CREAT value. 81 // We could possibly grovel through the preprocessor state, but 82 // that would require passing the Preprocessor object to the ExprEngine. 83 return; 84 } 85 } 86 87 LazyInitialize(BT, "Improper use of 'open'"); 88 89 // Look at the 'oflags' argument for the O_CREAT flag. 90 const GRState *state = C.getState(); 91 92 if (CE->getNumArgs() < 2) { 93 // The frontend should issue a warning for this case, so this is a sanity 94 // check. 95 return; 96 } 97 98 // Now check if oflags has O_CREAT set. 99 const Expr *oflagsEx = CE->getArg(1); 100 const SVal V = state->getSVal(oflagsEx); 101 if (!isa<NonLoc>(V)) { 102 // The case where 'V' can be a location can only be due to a bad header, 103 // so in this case bail out. 104 return; 105 } 106 NonLoc oflags = cast<NonLoc>(V); 107 NonLoc ocreateFlag = 108 cast<NonLoc>(C.getSValBuilder().makeIntVal(UC.Val_O_CREAT.getValue(), 109 oflagsEx->getType())); 110 SVal maskedFlagsUC = C.getSValBuilder().evalBinOpNN(state, BO_And, 111 oflags, ocreateFlag, 112 oflagsEx->getType()); 113 if (maskedFlagsUC.isUnknownOrUndef()) 114 return; 115 DefinedSVal maskedFlags = cast<DefinedSVal>(maskedFlagsUC); 116 117 // Check if maskedFlags is non-zero. 118 const GRState *trueState, *falseState; 119 llvm::tie(trueState, falseState) = state->assume(maskedFlags); 120 121 // Only emit an error if the value of 'maskedFlags' is properly 122 // constrained; 123 if (!(trueState && !falseState)) 124 return; 125 126 if (CE->getNumArgs() < 3) { 127 ExplodedNode *N = C.generateSink(trueState); 128 if (!N) 129 return; 130 131 EnhancedBugReport *report = 132 new EnhancedBugReport(*BT, 133 "Call to 'open' requires a third argument when " 134 "the 'O_CREAT' flag is set", N); 135 report->addRange(oflagsEx->getSourceRange()); 136 C.EmitReport(report); 137 } 138} 139 140//===----------------------------------------------------------------------===// 141// pthread_once 142//===----------------------------------------------------------------------===// 143 144static void CheckPthreadOnce(CheckerContext &C, UnixAPIChecker &, 145 const CallExpr *CE, BugType *&BT) { 146 147 // This is similar to 'CheckDispatchOnce' in the MacOSXAPIChecker. 148 // They can possibly be refactored. 149 150 LazyInitialize(BT, "Improper use of 'pthread_once'"); 151 152 if (CE->getNumArgs() < 1) 153 return; 154 155 // Check if the first argument is stack allocated. If so, issue a warning 156 // because that's likely to be bad news. 157 const GRState *state = C.getState(); 158 const MemRegion *R = state->getSVal(CE->getArg(0)).getAsRegion(); 159 if (!R || !isa<StackSpaceRegion>(R->getMemorySpace())) 160 return; 161 162 ExplodedNode *N = C.generateSink(state); 163 if (!N) 164 return; 165 166 llvm::SmallString<256> S; 167 llvm::raw_svector_ostream os(S); 168 os << "Call to 'pthread_once' uses"; 169 if (const VarRegion *VR = dyn_cast<VarRegion>(R)) 170 os << " the local variable '" << VR->getDecl()->getName() << '\''; 171 else 172 os << " stack allocated memory"; 173 os << " for the \"control\" value. Using such transient memory for " 174 "the control value is potentially dangerous."; 175 if (isa<VarRegion>(R) && isa<StackLocalsSpaceRegion>(R->getMemorySpace())) 176 os << " Perhaps you intended to declare the variable as 'static'?"; 177 178 EnhancedBugReport *report = new EnhancedBugReport(*BT, os.str(), N); 179 report->addRange(CE->getArg(0)->getSourceRange()); 180 C.EmitReport(report); 181} 182 183//===----------------------------------------------------------------------===// 184// "malloc" with allocation size 0 185//===----------------------------------------------------------------------===// 186 187// FIXME: Eventually this should be rolled into the MallocChecker, but this 188// check is more basic and is valuable for widespread use. 189static void CheckMallocZero(CheckerContext &C, UnixAPIChecker &UC, 190 const CallExpr *CE, BugType *&BT) { 191 192 // Sanity check that malloc takes one argument. 193 if (CE->getNumArgs() != 1) 194 return; 195 196 // Check if the allocation size is 0. 197 const GRState *state = C.getState(); 198 SVal argVal = state->getSVal(CE->getArg(0)); 199 200 if (argVal.isUnknownOrUndef()) 201 return; 202 203 const GRState *trueState, *falseState; 204 llvm::tie(trueState, falseState) = state->assume(cast<DefinedSVal>(argVal)); 205 206 // Is the value perfectly constrained to zero? 207 if (falseState && !trueState) { 208 ExplodedNode *N = C.generateSink(falseState); 209 if (!N) 210 return; 211 212 // FIXME: Add reference to CERT advisory, and/or C99 standard in bug 213 // output. 214 215 LazyInitialize(BT, "Undefined allocation of 0 bytes"); 216 217 EnhancedBugReport *report = 218 new EnhancedBugReport(*BT, "Call to 'malloc' has an allocation size" 219 " of 0 bytes", N); 220 report->addRange(CE->getArg(0)->getSourceRange()); 221 report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, 222 CE->getArg(0)); 223 C.EmitReport(report); 224 return; 225 } 226 // Assume the the value is non-zero going forward. 227 assert(trueState); 228 if (trueState != state) { 229 C.addTransition(trueState); 230 } 231} 232 233//===----------------------------------------------------------------------===// 234// Central dispatch function. 235//===----------------------------------------------------------------------===// 236 237typedef void (*SubChecker)(CheckerContext &C, UnixAPIChecker &UC, 238 const CallExpr *CE, BugType *&BT); 239namespace { 240 class SubCheck { 241 SubChecker SC; 242 UnixAPIChecker *UC; 243 BugType **BT; 244 public: 245 SubCheck(SubChecker sc, UnixAPIChecker *uc, BugType *& bt) : SC(sc), UC(uc), 246 BT(&bt) {} 247 SubCheck() : SC(NULL), UC(NULL), BT(NULL) {} 248 249 void run(CheckerContext &C, const CallExpr *CE) const { 250 if (SC) 251 SC(C, *UC, CE, *BT); 252 } 253 }; 254} // end anonymous namespace 255 256void UnixAPIChecker::PreVisitCallExpr(CheckerContext &C, const CallExpr *CE) { 257 // Get the callee. All the functions we care about are C functions 258 // with simple identifiers. 259 const GRState *state = C.getState(); 260 const Expr *Callee = CE->getCallee(); 261 const FunctionTextRegion *Fn = 262 dyn_cast_or_null<FunctionTextRegion>(state->getSVal(Callee).getAsRegion()); 263 264 if (!Fn) 265 return; 266 267 const IdentifierInfo *FI = Fn->getDecl()->getIdentifier(); 268 if (!FI) 269 return; 270 271 const SubCheck &SC = 272 llvm::StringSwitch<SubCheck>(FI->getName()) 273 .Case("open", 274 SubCheck(CheckOpen, this, BTypes[OpenFn])) 275 .Case("pthread_once", 276 SubCheck(CheckPthreadOnce, this, BTypes[PthreadOnceFn])) 277 .Case("malloc", 278 SubCheck(CheckMallocZero, this, BTypes[MallocZero])) 279 .Default(SubCheck()); 280 281 SC.run(C, CE); 282} 283