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