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