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