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