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