xref: /llvm-project/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp (revision 940e00f2991a31f75d9bd04517b863c23b87511d)
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 
25 using namespace clang;
26 using namespace ento;
27 using llvm::Optional;
28 
29 namespace {
30 class 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 
34 public:
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 CheckCallocZero(CheckerContext &C, const CallExpr *CE) const;
40   void CheckMallocZero(CheckerContext &C, const CallExpr *CE) const;
41   void CheckReallocZero(CheckerContext &C, const CallExpr *CE) const;
42   void CheckAllocaZero(CheckerContext &C, const CallExpr *CE) const;
43   void CheckVallocZero(CheckerContext &C, const CallExpr *CE) const;
44 
45   typedef void (UnixAPIChecker::*SubChecker)(CheckerContext &,
46                                              const CallExpr *) const;
47 private:
48   bool ReportZeroByteAllocation(CheckerContext &C,
49                                 const ProgramState *falseState,
50                                 const Expr *arg,
51                                 const char *fn_name) const;
52   void BasicAllocationCheck(CheckerContext &C,
53                             const CallExpr *CE,
54                             const unsigned numArgs,
55                             const unsigned sizeArg,
56                             const char *fn) const;
57 };
58 } //end anonymous namespace
59 
60 //===----------------------------------------------------------------------===//
61 // Utility functions.
62 //===----------------------------------------------------------------------===//
63 
64 static inline void LazyInitialize(llvm::OwningPtr<BugType> &BT,
65                                   const char *name) {
66   if (BT)
67     return;
68   BT.reset(new BugType(name, "Unix API"));
69 }
70 
71 //===----------------------------------------------------------------------===//
72 // "open" (man 2 open)
73 //===----------------------------------------------------------------------===//
74 
75 void UnixAPIChecker::CheckOpen(CheckerContext &C, const CallExpr *CE) const {
76   // The definition of O_CREAT is platform specific.  We need a better way
77   // of querying this information from the checking environment.
78   if (!Val_O_CREAT.hasValue()) {
79     if (C.getASTContext().getTargetInfo().getTriple().getVendor()
80                                                       == llvm::Triple::Apple)
81       Val_O_CREAT = 0x0200;
82     else {
83       // FIXME: We need a more general way of getting the O_CREAT value.
84       // We could possibly grovel through the preprocessor state, but
85       // that would require passing the Preprocessor object to the ExprEngine.
86       return;
87     }
88   }
89 
90   // Look at the 'oflags' argument for the O_CREAT flag.
91   const ProgramState *state = C.getState();
92 
93   if (CE->getNumArgs() < 2) {
94     // The frontend should issue a warning for this case, so this is a sanity
95     // check.
96     return;
97   }
98 
99   // Now check if oflags has O_CREAT set.
100   const Expr *oflagsEx = CE->getArg(1);
101   const SVal V = state->getSVal(oflagsEx, C.getLocationContext());
102   if (!isa<NonLoc>(V)) {
103     // The case where 'V' can be a location can only be due to a bad header,
104     // so in this case bail out.
105     return;
106   }
107   NonLoc oflags = cast<NonLoc>(V);
108   NonLoc ocreateFlag =
109     cast<NonLoc>(C.getSValBuilder().makeIntVal(Val_O_CREAT.getValue(),
110                                                 oflagsEx->getType()));
111   SVal maskedFlagsUC = C.getSValBuilder().evalBinOpNN(state, BO_And,
112                                                       oflags, ocreateFlag,
113                                                       oflagsEx->getType());
114   if (maskedFlagsUC.isUnknownOrUndef())
115     return;
116   DefinedSVal maskedFlags = cast<DefinedSVal>(maskedFlagsUC);
117 
118   // Check if maskedFlags is non-zero.
119   const ProgramState *trueState, *falseState;
120   llvm::tie(trueState, falseState) = state->assume(maskedFlags);
121 
122   // Only emit an error if the value of 'maskedFlags' is properly
123   // constrained;
124   if (!(trueState && !falseState))
125     return;
126 
127   if (CE->getNumArgs() < 3) {
128     ExplodedNode *N = C.generateSink(trueState);
129     if (!N)
130       return;
131 
132     LazyInitialize(BT_open, "Improper use of 'open'");
133 
134     BugReport *report =
135       new BugReport(*BT_open,
136                             "Call to 'open' requires a third argument when "
137                             "the 'O_CREAT' flag is set", N);
138     report->addRange(oflagsEx->getSourceRange());
139     C.EmitReport(report);
140   }
141 }
142 
143 //===----------------------------------------------------------------------===//
144 // pthread_once
145 //===----------------------------------------------------------------------===//
146 
147 void UnixAPIChecker::CheckPthreadOnce(CheckerContext &C,
148                                       const CallExpr *CE) const {
149 
150   // This is similar to 'CheckDispatchOnce' in the MacOSXAPIChecker.
151   // They can possibly be refactored.
152 
153   if (CE->getNumArgs() < 1)
154     return;
155 
156   // Check if the first argument is stack allocated.  If so, issue a warning
157   // because that's likely to be bad news.
158   const ProgramState *state = C.getState();
159   const MemRegion *R =
160     state->getSVal(CE->getArg(0), C.getLocationContext()).getAsRegion();
161   if (!R || !isa<StackSpaceRegion>(R->getMemorySpace()))
162     return;
163 
164   ExplodedNode *N = C.generateSink(state);
165   if (!N)
166     return;
167 
168   llvm::SmallString<256> S;
169   llvm::raw_svector_ostream os(S);
170   os << "Call to 'pthread_once' uses";
171   if (const VarRegion *VR = dyn_cast<VarRegion>(R))
172     os << " the local variable '" << VR->getDecl()->getName() << '\'';
173   else
174     os << " stack allocated memory";
175   os << " for the \"control\" value.  Using such transient memory for "
176   "the control value is potentially dangerous.";
177   if (isa<VarRegion>(R) && isa<StackLocalsSpaceRegion>(R->getMemorySpace()))
178     os << "  Perhaps you intended to declare the variable as 'static'?";
179 
180   LazyInitialize(BT_pthreadOnce, "Improper use of 'pthread_once'");
181 
182   BugReport *report = new BugReport(*BT_pthreadOnce, os.str(), N);
183   report->addRange(CE->getArg(0)->getSourceRange());
184   C.EmitReport(report);
185 }
186 
187 //===----------------------------------------------------------------------===//
188 // "calloc", "malloc", "realloc", "alloca" and "valloc" with allocation size 0
189 //===----------------------------------------------------------------------===//
190 // FIXME: Eventually these should be rolled into the MallocChecker, but right now
191 // they're more basic and valuable for widespread use.
192 
193 // Returns true if we try to do a zero byte allocation, false otherwise.
194 // Fills in trueState and falseState.
195 static bool IsZeroByteAllocation(const ProgramState *state,
196                                 const SVal argVal,
197                                 const ProgramState **trueState,
198                                 const ProgramState **falseState) {
199   llvm::tie(*trueState, *falseState) =
200     state->assume(cast<DefinedSVal>(argVal));
201 
202   return (*falseState && !*trueState);
203 }
204 
205 // Generates an error report, indicating that the function whose name is given
206 // will perform a zero byte allocation.
207 // Returns false if an error occured, true otherwise.
208 bool UnixAPIChecker::ReportZeroByteAllocation(CheckerContext &C,
209                                               const ProgramState *falseState,
210                                               const Expr *arg,
211                                               const char *fn_name) const {
212   ExplodedNode *N = C.generateSink(falseState);
213   if (!N)
214     return false;
215 
216   LazyInitialize(BT_mallocZero,
217     "Undefined allocation of 0 bytes (CERT MEM04-C; CWE-131)");
218 
219   llvm::SmallString<256> S;
220   llvm::raw_svector_ostream os(S);
221   os << "Call to '" << fn_name << "' has an allocation size of 0 bytes";
222   BugReport *report = new BugReport(*BT_mallocZero, os.str(), N);
223 
224   report->addRange(arg->getSourceRange());
225   report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, arg));
226   C.EmitReport(report);
227 
228   return true;
229 }
230 
231 // Does a basic check for 0-sized allocations suitable for most of the below
232 // functions (modulo "calloc")
233 void UnixAPIChecker::BasicAllocationCheck(CheckerContext &C,
234                                           const CallExpr *CE,
235                                           const unsigned numArgs,
236                                           const unsigned sizeArg,
237                                           const char *fn) const {
238   // Sanity check for the correct number of arguments
239   if (CE->getNumArgs() != numArgs)
240     return;
241 
242   // Check if the allocation size is 0.
243   const ProgramState *state = C.getState();
244   const ProgramState *trueState = NULL, *falseState = NULL;
245   const Expr *arg = CE->getArg(sizeArg);
246   SVal argVal = state->getSVal(arg, C.getLocationContext());
247 
248   if (argVal.isUnknownOrUndef())
249     return;
250 
251   // Is the value perfectly constrained to zero?
252   if (IsZeroByteAllocation(state, argVal, &trueState, &falseState)) {
253     (void) ReportZeroByteAllocation(C, falseState, arg, fn);
254     return;
255   }
256   // Assume the the value is non-zero going forward.
257   assert(trueState);
258   if (trueState != state)
259     C.addTransition(trueState);
260 }
261 
262 void UnixAPIChecker::CheckCallocZero(CheckerContext &C,
263                                      const CallExpr *CE) const {
264   unsigned int nArgs = CE->getNumArgs();
265   if (nArgs != 2)
266     return;
267 
268   const ProgramState *state = C.getState();
269   const ProgramState *trueState = NULL, *falseState = NULL;
270 
271   unsigned int i;
272   for (i = 0; i < nArgs; i++) {
273     const Expr *arg = CE->getArg(i);
274     SVal argVal = state->getSVal(arg, C.getLocationContext());
275     if (argVal.isUnknownOrUndef()) {
276       if (i == 0)
277         continue;
278       else
279         return;
280     }
281 
282     if (IsZeroByteAllocation(state, argVal, &trueState, &falseState)) {
283       if (ReportZeroByteAllocation(C, falseState, arg, "calloc"))
284         return;
285       else if (i == 0)
286         continue;
287       else
288         return;
289     }
290   }
291 
292   // Assume the the value is non-zero going forward.
293   assert(trueState);
294   if (trueState != state)
295     C.addTransition(trueState);
296 }
297 
298 void UnixAPIChecker::CheckMallocZero(CheckerContext &C,
299                                      const CallExpr *CE) const {
300   BasicAllocationCheck(C, CE, 1, 0, "malloc");
301 }
302 
303 void UnixAPIChecker::CheckReallocZero(CheckerContext &C,
304                                       const CallExpr *CE) const {
305   BasicAllocationCheck(C, CE, 2, 1, "realloc");
306 }
307 
308 void UnixAPIChecker::CheckAllocaZero(CheckerContext &C,
309                                      const CallExpr *CE) const {
310   BasicAllocationCheck(C, CE, 1, 0, "alloca");
311 }
312 
313 void UnixAPIChecker::CheckVallocZero(CheckerContext &C,
314                                      const CallExpr *CE) const {
315   BasicAllocationCheck(C, CE, 1, 0, "valloc");
316 }
317 
318 
319 //===----------------------------------------------------------------------===//
320 // Central dispatch function.
321 //===----------------------------------------------------------------------===//
322 
323 void UnixAPIChecker::checkPreStmt(const CallExpr *CE,
324                                   CheckerContext &C) const {
325   StringRef FName = C.getCalleeName(CE);
326   if (FName.empty())
327     return;
328 
329   SubChecker SC =
330     llvm::StringSwitch<SubChecker>(FName)
331       .Case("open", &UnixAPIChecker::CheckOpen)
332       .Case("pthread_once", &UnixAPIChecker::CheckPthreadOnce)
333       .Case("calloc", &UnixAPIChecker::CheckCallocZero)
334       .Case("malloc", &UnixAPIChecker::CheckMallocZero)
335       .Case("realloc", &UnixAPIChecker::CheckReallocZero)
336       .Cases("alloca", "__builtin_alloca", &UnixAPIChecker::CheckAllocaZero)
337       .Case("valloc", &UnixAPIChecker::CheckVallocZero)
338       .Default(NULL);
339 
340   if (SC)
341     (this->*SC)(C, CE);
342 }
343 
344 //===----------------------------------------------------------------------===//
345 // Registration.
346 //===----------------------------------------------------------------------===//
347 
348 void ento::registerUnixAPIChecker(CheckerManager &mgr) {
349   mgr.registerChecker<UnixAPIChecker>();
350 }
351