]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp
Upgrade our copy of llvm/clang to r126079, from upstream's trunk.
[FreeBSD/FreeBSD.git] / contrib / llvm / tools / clang / lib / StaticAnalyzer / Checkers / UnixAPIChecker.cpp
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
24 using namespace clang;
25 using namespace ento;
26 using llvm::Optional;
27
28 namespace {
29 class 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
39 public:
40   Optional<uint64_t> Val_O_CREAT;
41
42 public:
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
50 static void RegisterUnixAPIChecker(ExprEngine &Eng) {
51   Eng.registerCheck(new UnixAPIChecker());
52 }
53
54 void ento::registerUnixAPIChecker(CheckerManager &mgr) {
55   mgr.addCheckerRegisterFunction(RegisterUnixAPIChecker);
56 }
57
58 //===----------------------------------------------------------------------===//
59 // Utility functions.
60 //===----------------------------------------------------------------------===//
61
62 static 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
72 static 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
144 static 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.
189 static 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
237 typedef void (*SubChecker)(CheckerContext &C, UnixAPIChecker &UC,
238                            const CallExpr *CE, BugType *&BT);
239 namespace {
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
256 void 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 }