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