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