xref: /freebsd/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
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/CallEvent.h"
21 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
22 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h"
23 #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h"
24 #include "llvm/ADT/STLExtras.h"
25 #include "llvm/ADT/StringExtras.h"
26 #include "llvm/Support/raw_ostream.h"
27 #include <optional>
28 
29 using namespace clang;
30 using namespace ento;
31 
32 enum class OpenVariant {
33   /// The standard open() call:
34   ///    int open(const char *path, int oflag, ...);
35   Open,
36 
37   /// The variant taking a directory file descriptor and a relative path:
38   ///    int openat(int fd, const char *path, int oflag, ...);
39   OpenAt
40 };
41 
getCreateFlagValue(const ASTContext & Ctx,const Preprocessor & PP)42 static std::optional<int> getCreateFlagValue(const ASTContext &Ctx,
43                                              const Preprocessor &PP) {
44   std::optional<int> MacroVal = tryExpandAsInteger("O_CREAT", PP);
45   if (MacroVal.has_value())
46     return MacroVal;
47 
48   // If we failed, fall-back to known values.
49   if (Ctx.getTargetInfo().getTriple().getVendor() == llvm::Triple::Apple)
50     return {0x0200};
51   return MacroVal;
52 }
53 
54 namespace {
55 
56 class UnixAPIMisuseChecker : public Checker<check::PreCall> {
57   const BugType BT_open{this, "Improper use of 'open'", categories::UnixAPI};
58   const BugType BT_getline{this, "Improper use of getdelim",
59                            categories::UnixAPI};
60   const BugType BT_pthreadOnce{this, "Improper use of 'pthread_once'",
61                                categories::UnixAPI};
62   const BugType BT_ArgumentNull{this, "NULL pointer", categories::UnixAPI};
63   const std::optional<int> Val_O_CREAT;
64 
65   ProgramStateRef
66   EnsurePtrNotNull(SVal PtrVal, const Expr *PtrExpr, CheckerContext &C,
67                    ProgramStateRef State, const StringRef PtrDescr,
68                    std::optional<std::reference_wrapper<const BugType>> BT =
69                        std::nullopt) const;
70 
71   ProgramStateRef EnsureGetdelimBufferAndSizeCorrect(
72       SVal LinePtrPtrSVal, SVal SizePtrSVal, const Expr *LinePtrPtrExpr,
73       const Expr *SizePtrExpr, CheckerContext &C, ProgramStateRef State) const;
74 
75 public:
UnixAPIMisuseChecker(const ASTContext & Ctx,const Preprocessor & PP)76   UnixAPIMisuseChecker(const ASTContext &Ctx, const Preprocessor &PP)
77       : Val_O_CREAT(getCreateFlagValue(Ctx, PP)) {}
78 
79   void checkASTDecl(const TranslationUnitDecl *TU, AnalysisManager &Mgr,
80                     BugReporter &BR) const;
81 
82   void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
83 
84   void CheckOpen(CheckerContext &C, const CallEvent &Call) const;
85   void CheckOpenAt(CheckerContext &C, const CallEvent &Call) const;
86   void CheckGetDelimOrGetline(CheckerContext &C, const CallEvent &Call) const;
87   void CheckPthreadOnce(CheckerContext &C, const CallEvent &Call) const;
88 
89   void CheckOpenVariant(CheckerContext &C, const CallEvent &Call,
90                         OpenVariant Variant) const;
91 
92   void ReportOpenBug(CheckerContext &C, ProgramStateRef State, const char *Msg,
93                      SourceRange SR) const;
94 };
95 
96 class UnixAPIPortabilityChecker : public Checker< check::PreStmt<CallExpr> > {
97 public:
98   void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
99 
100 private:
101   const BugType BT_mallocZero{
102       this, "Undefined allocation of 0 bytes (CERT MEM04-C; CWE-131)",
103       categories::UnixAPI};
104 
105   void CheckCallocZero(CheckerContext &C, const CallExpr *CE) const;
106   void CheckMallocZero(CheckerContext &C, const CallExpr *CE) const;
107   void CheckReallocZero(CheckerContext &C, const CallExpr *CE) const;
108   void CheckReallocfZero(CheckerContext &C, const CallExpr *CE) const;
109   void CheckAllocaZero(CheckerContext &C, const CallExpr *CE) const;
110   void CheckAllocaWithAlignZero(CheckerContext &C, const CallExpr *CE) const;
111   void CheckVallocZero(CheckerContext &C, const CallExpr *CE) const;
112 
113   bool ReportZeroByteAllocation(CheckerContext &C,
114                                 ProgramStateRef falseState,
115                                 const Expr *arg,
116                                 const char *fn_name) const;
117   void BasicAllocationCheck(CheckerContext &C,
118                             const CallExpr *CE,
119                             const unsigned numArgs,
120                             const unsigned sizeArg,
121                             const char *fn) const;
122 };
123 
124 } // end anonymous namespace
125 
EnsurePtrNotNull(SVal PtrVal,const Expr * PtrExpr,CheckerContext & C,ProgramStateRef State,const StringRef PtrDescr,std::optional<std::reference_wrapper<const BugType>> BT) const126 ProgramStateRef UnixAPIMisuseChecker::EnsurePtrNotNull(
127     SVal PtrVal, const Expr *PtrExpr, CheckerContext &C, ProgramStateRef State,
128     const StringRef PtrDescr,
129     std::optional<std::reference_wrapper<const BugType>> BT) const {
130   const auto Ptr = PtrVal.getAs<DefinedSVal>();
131   if (!Ptr || !PtrExpr->getType()->isPointerType())
132     return State;
133 
134   const auto [PtrNotNull, PtrNull] = State->assume(*Ptr);
135   if (!PtrNotNull && PtrNull) {
136     if (ExplodedNode *N = C.generateErrorNode(PtrNull)) {
137       auto R = std::make_unique<PathSensitiveBugReport>(
138           BT.value_or(std::cref(BT_ArgumentNull)),
139           (PtrDescr + " pointer might be NULL.").str(), N);
140       if (PtrExpr)
141         bugreporter::trackExpressionValue(N, PtrExpr, *R);
142       C.emitReport(std::move(R));
143     }
144     return nullptr;
145   }
146 
147   return PtrNotNull;
148 }
149 
150 //===----------------------------------------------------------------------===//
151 // "open" (man 2 open)
152 //===----------------------------------------------------------------------===/
153 
checkPreCall(const CallEvent & Call,CheckerContext & C) const154 void UnixAPIMisuseChecker::checkPreCall(const CallEvent &Call,
155                                         CheckerContext &C) const {
156   const FunctionDecl *FD = dyn_cast_if_present<FunctionDecl>(Call.getDecl());
157   if (!FD || FD->getKind() != Decl::Function)
158     return;
159 
160   // Don't treat functions in namespaces with the same name a Unix function
161   // as a call to the Unix function.
162   const DeclContext *NamespaceCtx = FD->getEnclosingNamespaceContext();
163   if (isa_and_nonnull<NamespaceDecl>(NamespaceCtx))
164     return;
165 
166   StringRef FName = C.getCalleeName(FD);
167   if (FName.empty())
168     return;
169 
170   if (FName == "open")
171     CheckOpen(C, Call);
172 
173   else if (FName == "openat")
174     CheckOpenAt(C, Call);
175 
176   else if (FName == "pthread_once")
177     CheckPthreadOnce(C, Call);
178 
179   else if (is_contained({"getdelim", "getline"}, FName))
180     CheckGetDelimOrGetline(C, Call);
181 }
ReportOpenBug(CheckerContext & C,ProgramStateRef State,const char * Msg,SourceRange SR) const182 void UnixAPIMisuseChecker::ReportOpenBug(CheckerContext &C,
183                                          ProgramStateRef State,
184                                          const char *Msg,
185                                          SourceRange SR) const {
186   ExplodedNode *N = C.generateErrorNode(State);
187   if (!N)
188     return;
189 
190   auto Report = std::make_unique<PathSensitiveBugReport>(BT_open, Msg, N);
191   Report->addRange(SR);
192   C.emitReport(std::move(Report));
193 }
194 
CheckOpen(CheckerContext & C,const CallEvent & Call) const195 void UnixAPIMisuseChecker::CheckOpen(CheckerContext &C,
196                                      const CallEvent &Call) const {
197   CheckOpenVariant(C, Call, OpenVariant::Open);
198 }
199 
CheckOpenAt(CheckerContext & C,const CallEvent & Call) const200 void UnixAPIMisuseChecker::CheckOpenAt(CheckerContext &C,
201                                        const CallEvent &Call) const {
202   CheckOpenVariant(C, Call, OpenVariant::OpenAt);
203 }
204 
CheckOpenVariant(CheckerContext & C,const CallEvent & Call,OpenVariant Variant) const205 void UnixAPIMisuseChecker::CheckOpenVariant(CheckerContext &C,
206                                             const CallEvent &Call,
207                                             OpenVariant Variant) const {
208   // The index of the argument taking the flags open flags (O_RDONLY,
209   // O_WRONLY, O_CREAT, etc.),
210   unsigned int FlagsArgIndex;
211   const char *VariantName;
212   switch (Variant) {
213   case OpenVariant::Open:
214     FlagsArgIndex = 1;
215     VariantName = "open";
216     break;
217   case OpenVariant::OpenAt:
218     FlagsArgIndex = 2;
219     VariantName = "openat";
220     break;
221   };
222 
223   // All calls should at least provide arguments up to the 'flags' parameter.
224   unsigned int MinArgCount = FlagsArgIndex + 1;
225 
226   // The frontend should issue a warning for this case. Just return.
227   if (Call.getNumArgs() < MinArgCount)
228     return;
229 
230   // If the flags has O_CREAT set then open/openat() require an additional
231   // argument specifying the file mode (permission bits) for the created file.
232   unsigned int CreateModeArgIndex = FlagsArgIndex + 1;
233 
234   // The create mode argument should be the last argument.
235   unsigned int MaxArgCount = CreateModeArgIndex + 1;
236 
237   ProgramStateRef state = C.getState();
238   if (Call.getNumArgs() == MaxArgCount) {
239     const Expr *Arg = Call.getArgExpr(CreateModeArgIndex);
240     QualType QT = Arg->getType();
241     if (!QT->isIntegerType()) {
242       SmallString<256> SBuf;
243       llvm::raw_svector_ostream OS(SBuf);
244       OS << "The " << CreateModeArgIndex + 1
245          << llvm::getOrdinalSuffix(CreateModeArgIndex + 1)
246          << " argument to '" << VariantName << "' is not an integer";
247 
248       ReportOpenBug(C, state,
249                     SBuf.c_str(),
250                     Arg->getSourceRange());
251       return;
252     }
253   } else if (Call.getNumArgs() > MaxArgCount) {
254     SmallString<256> SBuf;
255     llvm::raw_svector_ostream OS(SBuf);
256     OS << "Call to '" << VariantName << "' with more than " << MaxArgCount
257        << " arguments";
258 
259     ReportOpenBug(C, state, SBuf.c_str(),
260                   Call.getArgExpr(MaxArgCount)->getSourceRange());
261     return;
262   }
263 
264   if (!Val_O_CREAT.has_value()) {
265     return;
266   }
267 
268   // Now check if oflags has O_CREAT set.
269   const Expr *oflagsEx = Call.getArgExpr(FlagsArgIndex);
270   const SVal V = Call.getArgSVal(FlagsArgIndex);
271   if (!isa<NonLoc>(V)) {
272     // The case where 'V' can be a location can only be due to a bad header,
273     // so in this case bail out.
274     return;
275   }
276   NonLoc oflags = V.castAs<NonLoc>();
277   NonLoc ocreateFlag = C.getSValBuilder()
278                            .makeIntVal(Val_O_CREAT.value(), oflagsEx->getType())
279                            .castAs<NonLoc>();
280   SVal maskedFlagsUC = C.getSValBuilder().evalBinOpNN(state, BO_And,
281                                                       oflags, ocreateFlag,
282                                                       oflagsEx->getType());
283   if (maskedFlagsUC.isUnknownOrUndef())
284     return;
285   DefinedSVal maskedFlags = maskedFlagsUC.castAs<DefinedSVal>();
286 
287   // Check if maskedFlags is non-zero.
288   ProgramStateRef trueState, falseState;
289   std::tie(trueState, falseState) = state->assume(maskedFlags);
290 
291   // Only emit an error if the value of 'maskedFlags' is properly
292   // constrained;
293   if (!(trueState && !falseState))
294     return;
295 
296   if (Call.getNumArgs() < MaxArgCount) {
297     SmallString<256> SBuf;
298     llvm::raw_svector_ostream OS(SBuf);
299     OS << "Call to '" << VariantName << "' requires a "
300        << CreateModeArgIndex + 1
301        << llvm::getOrdinalSuffix(CreateModeArgIndex + 1)
302        << " argument when the 'O_CREAT' flag is set";
303     ReportOpenBug(C, trueState,
304                   SBuf.c_str(),
305                   oflagsEx->getSourceRange());
306   }
307 }
308 
309 //===----------------------------------------------------------------------===//
310 // getdelim and getline
311 //===----------------------------------------------------------------------===//
312 
EnsureGetdelimBufferAndSizeCorrect(SVal LinePtrPtrSVal,SVal SizePtrSVal,const Expr * LinePtrPtrExpr,const Expr * SizePtrExpr,CheckerContext & C,ProgramStateRef State) const313 ProgramStateRef UnixAPIMisuseChecker::EnsureGetdelimBufferAndSizeCorrect(
314     SVal LinePtrPtrSVal, SVal SizePtrSVal, const Expr *LinePtrPtrExpr,
315     const Expr *SizePtrExpr, CheckerContext &C, ProgramStateRef State) const {
316   static constexpr llvm::StringLiteral SizeGreaterThanBufferSize =
317       "The buffer from the first argument is smaller than the size "
318       "specified by the second parameter";
319   static constexpr llvm::StringLiteral SizeUndef =
320       "The buffer from the first argument is not NULL, but the size specified "
321       "by the second parameter is undefined.";
322 
323   auto EmitBugReport = [this, &C, SizePtrExpr, LinePtrPtrExpr](
324                            ProgramStateRef BugState, StringRef ErrMsg) {
325     if (ExplodedNode *N = C.generateErrorNode(BugState)) {
326       auto R = std::make_unique<PathSensitiveBugReport>(BT_getline, ErrMsg, N);
327       bugreporter::trackExpressionValue(N, SizePtrExpr, *R);
328       bugreporter::trackExpressionValue(N, LinePtrPtrExpr, *R);
329       C.emitReport(std::move(R));
330     }
331   };
332 
333   // We have a pointer to a pointer to the buffer, and a pointer to the size.
334   // We want what they point at.
335   const auto LinePtrValOpt = getPointeeVal(LinePtrPtrSVal, State);
336   if (!LinePtrValOpt)
337     return nullptr;
338 
339   const auto LinePtrSVal = LinePtrValOpt->getAs<DefinedSVal>();
340   const auto NSVal = getPointeeVal(SizePtrSVal, State);
341   if (!LinePtrSVal || !NSVal || NSVal->isUnknown())
342     return nullptr;
343 
344   assert(LinePtrPtrExpr && SizePtrExpr);
345 
346   const auto [LinePtrNotNull, LinePtrNull] = State->assume(*LinePtrSVal);
347   if (LinePtrNotNull && !LinePtrNull) {
348     // If `*lineptr` is not null, but `*n` is undefined, there is UB.
349     if (NSVal->isUndef()) {
350       EmitBugReport(LinePtrNotNull, SizeUndef);
351       return nullptr;
352     }
353 
354     // If it is defined, and known, its size must be less than or equal to
355     // the buffer size.
356     auto NDefSVal = NSVal->getAs<DefinedSVal>();
357     if (!NDefSVal)
358       return LinePtrNotNull;
359 
360     auto &SVB = C.getSValBuilder();
361 
362     const MemRegion *LinePtrRegion = LinePtrSVal->getAsRegion();
363     if (!LinePtrRegion)
364       return LinePtrNotNull;
365 
366     auto LineBufSize = getDynamicExtent(LinePtrNotNull, LinePtrRegion, SVB);
367     auto LineBufSizeGtN = SVB.evalBinOp(LinePtrNotNull, BO_GE, LineBufSize,
368                                         *NDefSVal, SVB.getConditionType())
369                               .getAs<DefinedOrUnknownSVal>();
370     if (!LineBufSizeGtN)
371       return LinePtrNotNull;
372     if (auto LineBufSizeOk = LinePtrNotNull->assume(*LineBufSizeGtN, true))
373       return LineBufSizeOk;
374 
375     EmitBugReport(LinePtrNotNull, SizeGreaterThanBufferSize);
376     return nullptr;
377   }
378   return State;
379 }
380 
CheckGetDelimOrGetline(CheckerContext & C,const CallEvent & Call) const381 void UnixAPIMisuseChecker::CheckGetDelimOrGetline(CheckerContext &C,
382                                                   const CallEvent &Call) const {
383   if (Call.getNumArgs() < 2)
384     return;
385 
386   ProgramStateRef State = C.getState();
387 
388   // The parameter `n` must not be NULL.
389   SVal SizePtrSval = Call.getArgSVal(1);
390   State = EnsurePtrNotNull(SizePtrSval, Call.getArgExpr(1), C, State, "Size");
391   if (!State)
392     return;
393 
394   // The parameter `lineptr` must not be NULL.
395   SVal LinePtrPtrSVal = Call.getArgSVal(0);
396   State =
397       EnsurePtrNotNull(LinePtrPtrSVal, Call.getArgExpr(0), C, State, "Line");
398   if (!State)
399     return;
400 
401   State = EnsureGetdelimBufferAndSizeCorrect(LinePtrPtrSVal, SizePtrSval,
402                                              Call.getArgExpr(0),
403                                              Call.getArgExpr(1), C, State);
404   if (!State)
405     return;
406 
407   C.addTransition(State);
408 }
409 
410 //===----------------------------------------------------------------------===//
411 // pthread_once
412 //===----------------------------------------------------------------------===//
413 
CheckPthreadOnce(CheckerContext & C,const CallEvent & Call) const414 void UnixAPIMisuseChecker::CheckPthreadOnce(CheckerContext &C,
415                                             const CallEvent &Call) const {
416 
417   // This is similar to 'CheckDispatchOnce' in the MacOSXAPIChecker.
418   // They can possibly be refactored.
419 
420   if (Call.getNumArgs() < 1)
421     return;
422 
423   // Check if the first argument is stack allocated.  If so, issue a warning
424   // because that's likely to be bad news.
425   ProgramStateRef state = C.getState();
426   const MemRegion *R = Call.getArgSVal(0).getAsRegion();
427   if (!R || !R->hasMemorySpace<StackSpaceRegion>(state))
428     return;
429 
430   ExplodedNode *N = C.generateErrorNode(state);
431   if (!N)
432     return;
433 
434   SmallString<256> S;
435   llvm::raw_svector_ostream os(S);
436   os << "Call to 'pthread_once' uses";
437   if (const VarRegion *VR = dyn_cast<VarRegion>(R))
438     os << " the local variable '" << VR->getDecl()->getName() << '\'';
439   else
440     os << " stack allocated memory";
441   os << " for the \"control\" value.  Using such transient memory for "
442   "the control value is potentially dangerous.";
443   if (isa<VarRegion>(R) && R->hasMemorySpace<StackLocalsSpaceRegion>(state))
444     os << "  Perhaps you intended to declare the variable as 'static'?";
445 
446   auto report =
447       std::make_unique<PathSensitiveBugReport>(BT_pthreadOnce, os.str(), N);
448   report->addRange(Call.getArgExpr(0)->getSourceRange());
449   C.emitReport(std::move(report));
450 }
451 
452 //===----------------------------------------------------------------------===//
453 // "calloc", "malloc", "realloc", "reallocf", "alloca" and "valloc"
454 // with allocation size 0
455 //===----------------------------------------------------------------------===//
456 
457 // FIXME: Eventually these should be rolled into the MallocChecker, but right now
458 // they're more basic and valuable for widespread use.
459 
460 // Returns true if we try to do a zero byte allocation, false otherwise.
461 // Fills in trueState and falseState.
IsZeroByteAllocation(ProgramStateRef state,const SVal argVal,ProgramStateRef * trueState,ProgramStateRef * falseState)462 static bool IsZeroByteAllocation(ProgramStateRef state,
463                                  const SVal argVal,
464                                  ProgramStateRef *trueState,
465                                  ProgramStateRef *falseState) {
466   std::tie(*trueState, *falseState) =
467     state->assume(argVal.castAs<DefinedSVal>());
468 
469   return (*falseState && !*trueState);
470 }
471 
472 // Generates an error report, indicating that the function whose name is given
473 // will perform a zero byte allocation.
474 // Returns false if an error occurred, true otherwise.
ReportZeroByteAllocation(CheckerContext & C,ProgramStateRef falseState,const Expr * arg,const char * fn_name) const475 bool UnixAPIPortabilityChecker::ReportZeroByteAllocation(
476                                                     CheckerContext &C,
477                                                     ProgramStateRef falseState,
478                                                     const Expr *arg,
479                                                     const char *fn_name) const {
480   ExplodedNode *N = C.generateErrorNode(falseState);
481   if (!N)
482     return false;
483 
484   SmallString<256> S;
485   llvm::raw_svector_ostream os(S);
486   os << "Call to '" << fn_name << "' has an allocation size of 0 bytes";
487   auto report =
488       std::make_unique<PathSensitiveBugReport>(BT_mallocZero, os.str(), N);
489 
490   report->addRange(arg->getSourceRange());
491   bugreporter::trackExpressionValue(N, arg, *report);
492   C.emitReport(std::move(report));
493 
494   return true;
495 }
496 
497 // Does a basic check for 0-sized allocations suitable for most of the below
498 // functions (modulo "calloc")
BasicAllocationCheck(CheckerContext & C,const CallExpr * CE,const unsigned numArgs,const unsigned sizeArg,const char * fn) const499 void UnixAPIPortabilityChecker::BasicAllocationCheck(CheckerContext &C,
500                                                      const CallExpr *CE,
501                                                      const unsigned numArgs,
502                                                      const unsigned sizeArg,
503                                                      const char *fn) const {
504   // Check for the correct number of arguments.
505   if (CE->getNumArgs() != numArgs)
506     return;
507 
508   // Check if the allocation size is 0.
509   ProgramStateRef state = C.getState();
510   ProgramStateRef trueState = nullptr, falseState = nullptr;
511   const Expr *arg = CE->getArg(sizeArg);
512   SVal argVal = C.getSVal(arg);
513 
514   if (argVal.isUnknownOrUndef())
515     return;
516 
517   // Is the value perfectly constrained to zero?
518   if (IsZeroByteAllocation(state, argVal, &trueState, &falseState)) {
519     (void) ReportZeroByteAllocation(C, falseState, arg, fn);
520     return;
521   }
522   // Assume the value is non-zero going forward.
523   assert(trueState);
524   if (trueState != state)
525     C.addTransition(trueState);
526 }
527 
CheckCallocZero(CheckerContext & C,const CallExpr * CE) const528 void UnixAPIPortabilityChecker::CheckCallocZero(CheckerContext &C,
529                                                 const CallExpr *CE) const {
530   unsigned int nArgs = CE->getNumArgs();
531   if (nArgs != 2)
532     return;
533 
534   ProgramStateRef state = C.getState();
535   ProgramStateRef trueState = nullptr, falseState = nullptr;
536 
537   unsigned int i;
538   for (i = 0; i < nArgs; i++) {
539     const Expr *arg = CE->getArg(i);
540     SVal argVal = C.getSVal(arg);
541     if (argVal.isUnknownOrUndef()) {
542       if (i == 0)
543         continue;
544       return;
545     }
546 
547     if (IsZeroByteAllocation(state, argVal, &trueState, &falseState)) {
548       if (ReportZeroByteAllocation(C, falseState, arg, "calloc"))
549         return;
550       if (i == 0)
551         continue;
552       return;
553     }
554   }
555 
556   // Assume the value is non-zero going forward.
557   assert(trueState);
558   if (trueState != state)
559     C.addTransition(trueState);
560 }
561 
CheckMallocZero(CheckerContext & C,const CallExpr * CE) const562 void UnixAPIPortabilityChecker::CheckMallocZero(CheckerContext &C,
563                                                 const CallExpr *CE) const {
564   BasicAllocationCheck(C, CE, 1, 0, "malloc");
565 }
566 
CheckReallocZero(CheckerContext & C,const CallExpr * CE) const567 void UnixAPIPortabilityChecker::CheckReallocZero(CheckerContext &C,
568                                                  const CallExpr *CE) const {
569   BasicAllocationCheck(C, CE, 2, 1, "realloc");
570 }
571 
CheckReallocfZero(CheckerContext & C,const CallExpr * CE) const572 void UnixAPIPortabilityChecker::CheckReallocfZero(CheckerContext &C,
573                                                   const CallExpr *CE) const {
574   BasicAllocationCheck(C, CE, 2, 1, "reallocf");
575 }
576 
CheckAllocaZero(CheckerContext & C,const CallExpr * CE) const577 void UnixAPIPortabilityChecker::CheckAllocaZero(CheckerContext &C,
578                                                 const CallExpr *CE) const {
579   BasicAllocationCheck(C, CE, 1, 0, "alloca");
580 }
581 
CheckAllocaWithAlignZero(CheckerContext & C,const CallExpr * CE) const582 void UnixAPIPortabilityChecker::CheckAllocaWithAlignZero(
583                                                      CheckerContext &C,
584                                                      const CallExpr *CE) const {
585   BasicAllocationCheck(C, CE, 2, 0, "__builtin_alloca_with_align");
586 }
587 
CheckVallocZero(CheckerContext & C,const CallExpr * CE) const588 void UnixAPIPortabilityChecker::CheckVallocZero(CheckerContext &C,
589                                                 const CallExpr *CE) const {
590   BasicAllocationCheck(C, CE, 1, 0, "valloc");
591 }
592 
checkPreStmt(const CallExpr * CE,CheckerContext & C) const593 void UnixAPIPortabilityChecker::checkPreStmt(const CallExpr *CE,
594                                              CheckerContext &C) const {
595   const FunctionDecl *FD = C.getCalleeDecl(CE);
596   if (!FD || FD->getKind() != Decl::Function)
597     return;
598 
599   // Don't treat functions in namespaces with the same name a Unix function
600   // as a call to the Unix function.
601   const DeclContext *NamespaceCtx = FD->getEnclosingNamespaceContext();
602   if (isa_and_nonnull<NamespaceDecl>(NamespaceCtx))
603     return;
604 
605   StringRef FName = C.getCalleeName(FD);
606   if (FName.empty())
607     return;
608 
609   if (FName == "calloc")
610     CheckCallocZero(C, CE);
611 
612   else if (FName == "malloc")
613     CheckMallocZero(C, CE);
614 
615   else if (FName == "realloc")
616     CheckReallocZero(C, CE);
617 
618   else if (FName == "reallocf")
619     CheckReallocfZero(C, CE);
620 
621   else if (FName == "alloca" || FName ==  "__builtin_alloca")
622     CheckAllocaZero(C, CE);
623 
624   else if (FName == "__builtin_alloca_with_align")
625     CheckAllocaWithAlignZero(C, CE);
626 
627   else if (FName == "valloc")
628     CheckVallocZero(C, CE);
629 }
630 
631 //===----------------------------------------------------------------------===//
632 // Registration.
633 //===----------------------------------------------------------------------===//
634 
registerUnixAPIMisuseChecker(CheckerManager & Mgr)635 void ento::registerUnixAPIMisuseChecker(CheckerManager &Mgr) {
636   Mgr.registerChecker<UnixAPIMisuseChecker>(Mgr.getASTContext(),
637                                             Mgr.getPreprocessor());
638 }
shouldRegisterUnixAPIMisuseChecker(const CheckerManager & Mgr)639 bool ento::shouldRegisterUnixAPIMisuseChecker(const CheckerManager &Mgr) {
640   return true;
641 }
642 
registerUnixAPIPortabilityChecker(CheckerManager & Mgr)643 void ento::registerUnixAPIPortabilityChecker(CheckerManager &Mgr) {
644   Mgr.registerChecker<UnixAPIPortabilityChecker>();
645 }
shouldRegisterUnixAPIPortabilityChecker(const CheckerManager & Mgr)646 bool ento::shouldRegisterUnixAPIPortabilityChecker(const CheckerManager &Mgr) {
647   return true;
648 }
649