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