1 //= UnixAPIChecker.h - Checks preconditions for various Unix APIs --*- C++ -*-//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This defines UnixAPIChecker, which is an assortment of checks on calls
10 // to various, widely used UNIX/Posix functions.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
15 #include "clang/Basic/TargetInfo.h"
16 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
17 #include "clang/StaticAnalyzer/Core/Checker.h"
18 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
19 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
20 #include "llvm/ADT/Optional.h"
21 #include "llvm/ADT/STLExtras.h"
22 #include "llvm/ADT/SmallString.h"
23 #include "llvm/ADT/StringExtras.h"
24 #include "llvm/Support/raw_ostream.h"
25 
26 using namespace clang;
27 using namespace ento;
28 
29 enum class OpenVariant {
30   /// The standard open() call:
31   ///    int open(const char *path, int oflag, ...);
32   Open,
33 
34   /// The variant taking a directory file descriptor and a relative path:
35   ///    int openat(int fd, const char *path, int oflag, ...);
36   OpenAt
37 };
38 
39 namespace {
40 
41 class UnixAPIMisuseChecker : public Checker< check::PreStmt<CallExpr> > {
42   mutable std::unique_ptr<BugType> BT_open, BT_pthreadOnce;
43   mutable Optional<uint64_t> Val_O_CREAT;
44 
45 public:
46   DefaultBool CheckMisuse, CheckPortability;
47 
48   void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
49 
50   void CheckOpen(CheckerContext &C, const CallExpr *CE) const;
51   void CheckOpenAt(CheckerContext &C, const CallExpr *CE) const;
52   void CheckPthreadOnce(CheckerContext &C, const CallExpr *CE) const;
53 
54   void CheckOpenVariant(CheckerContext &C,
55                         const CallExpr *CE, OpenVariant Variant) const;
56 
57   void ReportOpenBug(CheckerContext &C,
58                      ProgramStateRef State,
59                      const char *Msg,
60                      SourceRange SR) const;
61 
62 };
63 
64 class UnixAPIPortabilityChecker : public Checker< check::PreStmt<CallExpr> > {
65 public:
66   void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
67 
68 private:
69   mutable std::unique_ptr<BugType> BT_mallocZero;
70 
71   void CheckCallocZero(CheckerContext &C, const CallExpr *CE) const;
72   void CheckMallocZero(CheckerContext &C, const CallExpr *CE) const;
73   void CheckReallocZero(CheckerContext &C, const CallExpr *CE) const;
74   void CheckReallocfZero(CheckerContext &C, const CallExpr *CE) const;
75   void CheckAllocaZero(CheckerContext &C, const CallExpr *CE) const;
76   void CheckAllocaWithAlignZero(CheckerContext &C, const CallExpr *CE) const;
77   void CheckVallocZero(CheckerContext &C, const CallExpr *CE) const;
78 
79   bool ReportZeroByteAllocation(CheckerContext &C,
80                                 ProgramStateRef falseState,
81                                 const Expr *arg,
82                                 const char *fn_name) const;
83   void BasicAllocationCheck(CheckerContext &C,
84                             const CallExpr *CE,
85                             const unsigned numArgs,
86                             const unsigned sizeArg,
87                             const char *fn) const;
88 };
89 
90 } //end anonymous namespace
91 
LazyInitialize(const CheckerBase * Checker,std::unique_ptr<BugType> & BT,const char * name)92 static void LazyInitialize(const CheckerBase *Checker,
93                            std::unique_ptr<BugType> &BT,
94                            const char *name) {
95   if (BT)
96     return;
97   BT.reset(new BugType(Checker, name, categories::UnixAPI));
98 }
99 
100 //===----------------------------------------------------------------------===//
101 // "open" (man 2 open)
102 //===----------------------------------------------------------------------===/
103 
checkPreStmt(const CallExpr * CE,CheckerContext & C) const104 void UnixAPIMisuseChecker::checkPreStmt(const CallExpr *CE,
105                                         CheckerContext &C) const {
106   const FunctionDecl *FD = C.getCalleeDecl(CE);
107   if (!FD || FD->getKind() != Decl::Function)
108     return;
109 
110   // Don't treat functions in namespaces with the same name a Unix function
111   // as a call to the Unix function.
112   const DeclContext *NamespaceCtx = FD->getEnclosingNamespaceContext();
113   if (NamespaceCtx && isa<NamespaceDecl>(NamespaceCtx))
114     return;
115 
116   StringRef FName = C.getCalleeName(FD);
117   if (FName.empty())
118     return;
119 
120   if (FName == "open")
121     CheckOpen(C, CE);
122 
123   else if (FName == "openat")
124     CheckOpenAt(C, CE);
125 
126   else if (FName == "pthread_once")
127     CheckPthreadOnce(C, CE);
128 }
ReportOpenBug(CheckerContext & C,ProgramStateRef State,const char * Msg,SourceRange SR) const129 void UnixAPIMisuseChecker::ReportOpenBug(CheckerContext &C,
130                                          ProgramStateRef State,
131                                          const char *Msg,
132                                          SourceRange SR) const {
133   ExplodedNode *N = C.generateErrorNode(State);
134   if (!N)
135     return;
136 
137   LazyInitialize(this, BT_open, "Improper use of 'open'");
138 
139   auto Report = std::make_unique<PathSensitiveBugReport>(*BT_open, Msg, N);
140   Report->addRange(SR);
141   C.emitReport(std::move(Report));
142 }
143 
CheckOpen(CheckerContext & C,const CallExpr * CE) const144 void UnixAPIMisuseChecker::CheckOpen(CheckerContext &C,
145                                      const CallExpr *CE) const {
146   CheckOpenVariant(C, CE, OpenVariant::Open);
147 }
148 
CheckOpenAt(CheckerContext & C,const CallExpr * CE) const149 void UnixAPIMisuseChecker::CheckOpenAt(CheckerContext &C,
150                                        const CallExpr *CE) const {
151   CheckOpenVariant(C, CE, OpenVariant::OpenAt);
152 }
153 
CheckOpenVariant(CheckerContext & C,const CallExpr * CE,OpenVariant Variant) const154 void UnixAPIMisuseChecker::CheckOpenVariant(CheckerContext &C,
155                                             const CallExpr *CE,
156                                             OpenVariant Variant) const {
157   // The index of the argument taking the flags open flags (O_RDONLY,
158   // O_WRONLY, O_CREAT, etc.),
159   unsigned int FlagsArgIndex;
160   const char *VariantName;
161   switch (Variant) {
162   case OpenVariant::Open:
163     FlagsArgIndex = 1;
164     VariantName = "open";
165     break;
166   case OpenVariant::OpenAt:
167     FlagsArgIndex = 2;
168     VariantName = "openat";
169     break;
170   };
171 
172   // All calls should at least provide arguments up to the 'flags' parameter.
173   unsigned int MinArgCount = FlagsArgIndex + 1;
174 
175   // If the flags has O_CREAT set then open/openat() require an additional
176   // argument specifying the file mode (permission bits) for the created file.
177   unsigned int CreateModeArgIndex = FlagsArgIndex + 1;
178 
179   // The create mode argument should be the last argument.
180   unsigned int MaxArgCount = CreateModeArgIndex + 1;
181 
182   ProgramStateRef state = C.getState();
183 
184   if (CE->getNumArgs() < MinArgCount) {
185     // The frontend should issue a warning for this case, so this is a sanity
186     // check.
187     return;
188   } else if (CE->getNumArgs() == MaxArgCount) {
189     const Expr *Arg = CE->getArg(CreateModeArgIndex);
190     QualType QT = Arg->getType();
191     if (!QT->isIntegerType()) {
192       SmallString<256> SBuf;
193       llvm::raw_svector_ostream OS(SBuf);
194       OS << "The " << CreateModeArgIndex + 1
195          << llvm::getOrdinalSuffix(CreateModeArgIndex + 1)
196          << " argument to '" << VariantName << "' is not an integer";
197 
198       ReportOpenBug(C, state,
199                     SBuf.c_str(),
200                     Arg->getSourceRange());
201       return;
202     }
203   } else if (CE->getNumArgs() > MaxArgCount) {
204     SmallString<256> SBuf;
205     llvm::raw_svector_ostream OS(SBuf);
206     OS << "Call to '" << VariantName << "' with more than " << MaxArgCount
207        << " arguments";
208 
209     ReportOpenBug(C, state,
210                   SBuf.c_str(),
211                   CE->getArg(MaxArgCount)->getSourceRange());
212     return;
213   }
214 
215   // The definition of O_CREAT is platform specific.  We need a better way
216   // of querying this information from the checking environment.
217   if (!Val_O_CREAT.hasValue()) {
218     if (C.getASTContext().getTargetInfo().getTriple().getVendor()
219                                                       == llvm::Triple::Apple)
220       Val_O_CREAT = 0x0200;
221     else {
222       // FIXME: We need a more general way of getting the O_CREAT value.
223       // We could possibly grovel through the preprocessor state, but
224       // that would require passing the Preprocessor object to the ExprEngine.
225       // See also: MallocChecker.cpp / M_ZERO.
226       return;
227     }
228   }
229 
230   // Now check if oflags has O_CREAT set.
231   const Expr *oflagsEx = CE->getArg(FlagsArgIndex);
232   const SVal V = C.getSVal(oflagsEx);
233   if (!V.getAs<NonLoc>()) {
234     // The case where 'V' can be a location can only be due to a bad header,
235     // so in this case bail out.
236     return;
237   }
238   NonLoc oflags = V.castAs<NonLoc>();
239   NonLoc ocreateFlag = C.getSValBuilder()
240       .makeIntVal(Val_O_CREAT.getValue(), oflagsEx->getType()).castAs<NonLoc>();
241   SVal maskedFlagsUC = C.getSValBuilder().evalBinOpNN(state, BO_And,
242                                                       oflags, ocreateFlag,
243                                                       oflagsEx->getType());
244   if (maskedFlagsUC.isUnknownOrUndef())
245     return;
246   DefinedSVal maskedFlags = maskedFlagsUC.castAs<DefinedSVal>();
247 
248   // Check if maskedFlags is non-zero.
249   ProgramStateRef trueState, falseState;
250   std::tie(trueState, falseState) = state->assume(maskedFlags);
251 
252   // Only emit an error if the value of 'maskedFlags' is properly
253   // constrained;
254   if (!(trueState && !falseState))
255     return;
256 
257   if (CE->getNumArgs() < MaxArgCount) {
258     SmallString<256> SBuf;
259     llvm::raw_svector_ostream OS(SBuf);
260     OS << "Call to '" << VariantName << "' requires a "
261        << CreateModeArgIndex + 1
262        << llvm::getOrdinalSuffix(CreateModeArgIndex + 1)
263        << " argument when the 'O_CREAT' flag is set";
264     ReportOpenBug(C, trueState,
265                   SBuf.c_str(),
266                   oflagsEx->getSourceRange());
267   }
268 }
269 
270 //===----------------------------------------------------------------------===//
271 // pthread_once
272 //===----------------------------------------------------------------------===//
273 
CheckPthreadOnce(CheckerContext & C,const CallExpr * CE) const274 void UnixAPIMisuseChecker::CheckPthreadOnce(CheckerContext &C,
275                                       const CallExpr *CE) const {
276 
277   // This is similar to 'CheckDispatchOnce' in the MacOSXAPIChecker.
278   // They can possibly be refactored.
279 
280   if (CE->getNumArgs() < 1)
281     return;
282 
283   // Check if the first argument is stack allocated.  If so, issue a warning
284   // because that's likely to be bad news.
285   ProgramStateRef state = C.getState();
286   const MemRegion *R = C.getSVal(CE->getArg(0)).getAsRegion();
287   if (!R || !isa<StackSpaceRegion>(R->getMemorySpace()))
288     return;
289 
290   ExplodedNode *N = C.generateErrorNode(state);
291   if (!N)
292     return;
293 
294   SmallString<256> S;
295   llvm::raw_svector_ostream os(S);
296   os << "Call to 'pthread_once' uses";
297   if (const VarRegion *VR = dyn_cast<VarRegion>(R))
298     os << " the local variable '" << VR->getDecl()->getName() << '\'';
299   else
300     os << " stack allocated memory";
301   os << " for the \"control\" value.  Using such transient memory for "
302   "the control value is potentially dangerous.";
303   if (isa<VarRegion>(R) && isa<StackLocalsSpaceRegion>(R->getMemorySpace()))
304     os << "  Perhaps you intended to declare the variable as 'static'?";
305 
306   LazyInitialize(this, BT_pthreadOnce, "Improper use of 'pthread_once'");
307 
308   auto report =
309       std::make_unique<PathSensitiveBugReport>(*BT_pthreadOnce, os.str(), N);
310   report->addRange(CE->getArg(0)->getSourceRange());
311   C.emitReport(std::move(report));
312 }
313 
314 //===----------------------------------------------------------------------===//
315 // "calloc", "malloc", "realloc", "reallocf", "alloca" and "valloc"
316 // with allocation size 0
317 //===----------------------------------------------------------------------===//
318 
319 // FIXME: Eventually these should be rolled into the MallocChecker, but right now
320 // they're more basic and valuable for widespread use.
321 
322 // Returns true if we try to do a zero byte allocation, false otherwise.
323 // Fills in trueState and falseState.
IsZeroByteAllocation(ProgramStateRef state,const SVal argVal,ProgramStateRef * trueState,ProgramStateRef * falseState)324 static bool IsZeroByteAllocation(ProgramStateRef state,
325                                  const SVal argVal,
326                                  ProgramStateRef *trueState,
327                                  ProgramStateRef *falseState) {
328   std::tie(*trueState, *falseState) =
329     state->assume(argVal.castAs<DefinedSVal>());
330 
331   return (*falseState && !*trueState);
332 }
333 
334 // Generates an error report, indicating that the function whose name is given
335 // will perform a zero byte allocation.
336 // Returns false if an error occurred, true otherwise.
ReportZeroByteAllocation(CheckerContext & C,ProgramStateRef falseState,const Expr * arg,const char * fn_name) const337 bool UnixAPIPortabilityChecker::ReportZeroByteAllocation(
338                                                     CheckerContext &C,
339                                                     ProgramStateRef falseState,
340                                                     const Expr *arg,
341                                                     const char *fn_name) const {
342   ExplodedNode *N = C.generateErrorNode(falseState);
343   if (!N)
344     return false;
345 
346   LazyInitialize(this, BT_mallocZero,
347                  "Undefined allocation of 0 bytes (CERT MEM04-C; CWE-131)");
348 
349   SmallString<256> S;
350   llvm::raw_svector_ostream os(S);
351   os << "Call to '" << fn_name << "' has an allocation size of 0 bytes";
352   auto report =
353       std::make_unique<PathSensitiveBugReport>(*BT_mallocZero, os.str(), N);
354 
355   report->addRange(arg->getSourceRange());
356   bugreporter::trackExpressionValue(N, arg, *report);
357   C.emitReport(std::move(report));
358 
359   return true;
360 }
361 
362 // Does a basic check for 0-sized allocations suitable for most of the below
363 // functions (modulo "calloc")
BasicAllocationCheck(CheckerContext & C,const CallExpr * CE,const unsigned numArgs,const unsigned sizeArg,const char * fn) const364 void UnixAPIPortabilityChecker::BasicAllocationCheck(CheckerContext &C,
365                                                      const CallExpr *CE,
366                                                      const unsigned numArgs,
367                                                      const unsigned sizeArg,
368                                                      const char *fn) const {
369   // Sanity check for the correct number of arguments
370   if (CE->getNumArgs() != numArgs)
371     return;
372 
373   // Check if the allocation size is 0.
374   ProgramStateRef state = C.getState();
375   ProgramStateRef trueState = nullptr, falseState = nullptr;
376   const Expr *arg = CE->getArg(sizeArg);
377   SVal argVal = C.getSVal(arg);
378 
379   if (argVal.isUnknownOrUndef())
380     return;
381 
382   // Is the value perfectly constrained to zero?
383   if (IsZeroByteAllocation(state, argVal, &trueState, &falseState)) {
384     (void) ReportZeroByteAllocation(C, falseState, arg, fn);
385     return;
386   }
387   // Assume the value is non-zero going forward.
388   assert(trueState);
389   if (trueState != state)
390     C.addTransition(trueState);
391 }
392 
CheckCallocZero(CheckerContext & C,const CallExpr * CE) const393 void UnixAPIPortabilityChecker::CheckCallocZero(CheckerContext &C,
394                                                 const CallExpr *CE) const {
395   unsigned int nArgs = CE->getNumArgs();
396   if (nArgs != 2)
397     return;
398 
399   ProgramStateRef state = C.getState();
400   ProgramStateRef trueState = nullptr, falseState = nullptr;
401 
402   unsigned int i;
403   for (i = 0; i < nArgs; i++) {
404     const Expr *arg = CE->getArg(i);
405     SVal argVal = C.getSVal(arg);
406     if (argVal.isUnknownOrUndef()) {
407       if (i == 0)
408         continue;
409       else
410         return;
411     }
412 
413     if (IsZeroByteAllocation(state, argVal, &trueState, &falseState)) {
414       if (ReportZeroByteAllocation(C, falseState, arg, "calloc"))
415         return;
416       else if (i == 0)
417         continue;
418       else
419         return;
420     }
421   }
422 
423   // Assume the value is non-zero going forward.
424   assert(trueState);
425   if (trueState != state)
426     C.addTransition(trueState);
427 }
428 
CheckMallocZero(CheckerContext & C,const CallExpr * CE) const429 void UnixAPIPortabilityChecker::CheckMallocZero(CheckerContext &C,
430                                                 const CallExpr *CE) const {
431   BasicAllocationCheck(C, CE, 1, 0, "malloc");
432 }
433 
CheckReallocZero(CheckerContext & C,const CallExpr * CE) const434 void UnixAPIPortabilityChecker::CheckReallocZero(CheckerContext &C,
435                                                  const CallExpr *CE) const {
436   BasicAllocationCheck(C, CE, 2, 1, "realloc");
437 }
438 
CheckReallocfZero(CheckerContext & C,const CallExpr * CE) const439 void UnixAPIPortabilityChecker::CheckReallocfZero(CheckerContext &C,
440                                                   const CallExpr *CE) const {
441   BasicAllocationCheck(C, CE, 2, 1, "reallocf");
442 }
443 
CheckAllocaZero(CheckerContext & C,const CallExpr * CE) const444 void UnixAPIPortabilityChecker::CheckAllocaZero(CheckerContext &C,
445                                                 const CallExpr *CE) const {
446   BasicAllocationCheck(C, CE, 1, 0, "alloca");
447 }
448 
CheckAllocaWithAlignZero(CheckerContext & C,const CallExpr * CE) const449 void UnixAPIPortabilityChecker::CheckAllocaWithAlignZero(
450                                                      CheckerContext &C,
451                                                      const CallExpr *CE) const {
452   BasicAllocationCheck(C, CE, 2, 0, "__builtin_alloca_with_align");
453 }
454 
CheckVallocZero(CheckerContext & C,const CallExpr * CE) const455 void UnixAPIPortabilityChecker::CheckVallocZero(CheckerContext &C,
456                                                 const CallExpr *CE) const {
457   BasicAllocationCheck(C, CE, 1, 0, "valloc");
458 }
459 
checkPreStmt(const CallExpr * CE,CheckerContext & C) const460 void UnixAPIPortabilityChecker::checkPreStmt(const CallExpr *CE,
461                                              CheckerContext &C) const {
462   const FunctionDecl *FD = C.getCalleeDecl(CE);
463   if (!FD || FD->getKind() != Decl::Function)
464     return;
465 
466   // Don't treat functions in namespaces with the same name a Unix function
467   // as a call to the Unix function.
468   const DeclContext *NamespaceCtx = FD->getEnclosingNamespaceContext();
469   if (NamespaceCtx && isa<NamespaceDecl>(NamespaceCtx))
470     return;
471 
472   StringRef FName = C.getCalleeName(FD);
473   if (FName.empty())
474     return;
475 
476   if (FName == "calloc")
477     CheckCallocZero(C, CE);
478 
479   else if (FName == "malloc")
480     CheckMallocZero(C, CE);
481 
482   else if (FName == "realloc")
483     CheckReallocZero(C, CE);
484 
485   else if (FName == "reallocf")
486     CheckReallocfZero(C, CE);
487 
488   else if (FName == "alloca" || FName ==  "__builtin_alloca")
489     CheckAllocaZero(C, CE);
490 
491   else if (FName == "__builtin_alloca_with_align")
492     CheckAllocaWithAlignZero(C, CE);
493 
494   else if (FName == "valloc")
495     CheckVallocZero(C, CE);
496 }
497 
498 //===----------------------------------------------------------------------===//
499 // Registration.
500 //===----------------------------------------------------------------------===//
501 
502 #define REGISTER_CHECKER(CHECKERNAME)                                          \
503   void ento::register##CHECKERNAME(CheckerManager &mgr) {                      \
504     mgr.registerChecker<CHECKERNAME>();                                        \
505   }                                                                            \
506                                                                                \
507   bool ento::shouldRegister##CHECKERNAME(const CheckerManager &mgr) {              \
508     return true;                                                               \
509   }
510 
511 REGISTER_CHECKER(UnixAPIMisuseChecker)
512 REGISTER_CHECKER(UnixAPIPortabilityChecker)
513