xref: /llvm-project/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.cpp (revision 29b512ba322cb6dd2c45d5e07645e20db47fad0d)
1 //=== ErrnoModeling.cpp -----------------------------------------*- 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 a checker `ErrnoModeling`, which is used to make the system
10 // value 'errno' available to other checkers.
11 // The 'errno' value is stored at a special memory region that is accessible
12 // through the `errno_modeling` namespace. The memory region is either the
13 // region of `errno` itself if it is a variable, otherwise an artifically
14 // created region (in the system memory space). If `errno` is defined by using
15 // a function which returns the address of it (this is always the case if it is
16 // not a variable) this function is recognized and evaluated. In this way
17 // `errno` becomes visible to the analysis and checkers can change its value.
18 //
19 //===----------------------------------------------------------------------===//
20 
21 #include "Errno.h"
22 #include "clang/AST/ParentMapContext.h"
23 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
24 #include "clang/StaticAnalyzer/Core/Checker.h"
25 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
26 #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
27 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
28 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
29 #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
30 #include "llvm/ADT/STLExtras.h"
31 
32 using namespace clang;
33 using namespace ento;
34 
35 namespace {
36 
37 // Name of the "errno" variable.
38 // FIXME: Is there a system where it is not called "errno" but is a variable?
39 const char *ErrnoVarName = "errno";
40 // Names of functions that return a location of the "errno" value.
41 // FIXME: Are there other similar function names?
42 const char *ErrnoLocationFuncNames[] = {"__errno_location", "___errno",
43                                         "__errno", "_errno", "__error"};
44 
45 class ErrnoModeling
46     : public Checker<check::ASTDecl<TranslationUnitDecl>, check::BeginFunction,
47                      check::LiveSymbols, eval::Call> {
48 public:
49   void checkASTDecl(const TranslationUnitDecl *D, AnalysisManager &Mgr,
50                     BugReporter &BR) const;
51   void checkBeginFunction(CheckerContext &C) const;
52   void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const;
53   bool evalCall(const CallEvent &Call, CheckerContext &C) const;
54 
55   // The declaration of an "errno" variable or "errno location" function.
56   static const Decl *ErrnoDecl;
57 
58 private:
59   // FIXME: Names from `ErrnoLocationFuncNames` are used to build this set.
60   CallDescriptionSet ErrnoLocationCalls{{"__errno_location", 0, 0},
61                                         {"___errno", 0, 0},
62                                         {"__errno", 0, 0},
63                                         {"_errno", 0, 0},
64                                         {"__error", 0, 0}};
65 };
66 
67 } // namespace
68 
69 /// Store a MemRegion that contains the 'errno' integer value.
70 /// The value is null if the 'errno' value was not recognized in the AST.
71 REGISTER_TRAIT_WITH_PROGRAMSTATE(ErrnoRegion, const void *)
72 
73 /// An internal function accessing the errno region.
74 /// Returns null if there isn't any associated memory region.
75 static const MemRegion *getErrnoRegion(ProgramStateRef State) {
76   return reinterpret_cast<const MemRegion *>(State->get<ErrnoRegion>());
77 }
78 
79 /// Search for a variable called "errno" in the AST.
80 /// Return nullptr if not found.
81 static const VarDecl *getErrnoVar(ASTContext &ACtx) {
82   IdentifierInfo &II = ACtx.Idents.get(ErrnoVarName);
83   auto LookupRes = ACtx.getTranslationUnitDecl()->lookup(&II);
84   auto Found = llvm::find_if(LookupRes, [&ACtx](const Decl *D) {
85     if (auto *VD = dyn_cast<VarDecl>(D))
86       return ACtx.getSourceManager().isInSystemHeader(VD->getLocation()) &&
87              VD->hasExternalStorage() &&
88              VD->getType().getCanonicalType() == ACtx.IntTy;
89     return false;
90   });
91   if (Found == LookupRes.end())
92     return nullptr;
93 
94   return cast<VarDecl>(*Found);
95 }
96 
97 /// Search for a function with a specific name that is used to return a pointer
98 /// to "errno".
99 /// Return nullptr if no such function was found.
100 static const FunctionDecl *getErrnoFunc(ASTContext &ACtx) {
101   SmallVector<const Decl *> LookupRes;
102   for (StringRef ErrnoName : ErrnoLocationFuncNames) {
103     IdentifierInfo &II = ACtx.Idents.get(ErrnoName);
104     llvm::append_range(LookupRes, ACtx.getTranslationUnitDecl()->lookup(&II));
105   }
106 
107   auto Found = llvm::find_if(LookupRes, [&ACtx](const Decl *D) {
108     if (auto *FD = dyn_cast<FunctionDecl>(D))
109       return ACtx.getSourceManager().isInSystemHeader(FD->getLocation()) &&
110              FD->isExternC() && FD->getNumParams() == 0 &&
111              FD->getReturnType().getCanonicalType() ==
112                  ACtx.getPointerType(ACtx.IntTy);
113     return false;
114   });
115   if (Found == LookupRes.end())
116     return nullptr;
117 
118   return cast<FunctionDecl>(*Found);
119 }
120 
121 const Decl *ErrnoModeling::ErrnoDecl = nullptr;
122 
123 void ErrnoModeling::checkASTDecl(const TranslationUnitDecl *D,
124                                  AnalysisManager &Mgr, BugReporter &BR) const {
125   // Try to find an usable `errno` value.
126   // It can be an external variable called "errno" or a function that returns a
127   // pointer to the "errno" value. This function can have different names.
128   // The actual case is dependent on the C library implementation, we
129   // can only search for a match in one of these variations.
130   // We assume that exactly one of these cases might be true.
131   assert(!ErrnoDecl && "Checker should be used for one translation unit.");
132 
133   ErrnoDecl = getErrnoVar(Mgr.getASTContext());
134   if (!ErrnoDecl)
135     ErrnoDecl = getErrnoFunc(Mgr.getASTContext());
136 }
137 
138 void ErrnoModeling::checkBeginFunction(CheckerContext &C) const {
139   if (!C.inTopFrame())
140     return;
141 
142   ASTContext &ACtx = C.getASTContext();
143   ProgramStateRef State = C.getState();
144 
145   if (const auto *ErrnoVar = dyn_cast_or_null<VarDecl>(ErrnoDecl)) {
146     // There is an external 'errno' variable.
147     // Use its memory region.
148     // The memory region for an 'errno'-like variable is allocated in system
149     // space by MemRegionManager.
150     const MemRegion *ErrnoR =
151         State->getRegion(ErrnoVar, C.getLocationContext());
152     assert(ErrnoR && "Memory region should exist for the 'errno' variable.");
153     State = State->set<ErrnoRegion>(ErrnoR);
154     State = errno_modeling::setErrnoValue(State, C, 0);
155     C.addTransition(State);
156   } else if (ErrnoDecl) {
157     assert(isa<FunctionDecl>(ErrnoDecl) && "Invalid errno location function.");
158     // There is a function that returns the location of 'errno'.
159     // We must create a memory region for it in system space.
160     // Currently a symbolic region is used with an artifical symbol.
161     // FIXME: It is better to have a custom (new) kind of MemRegion for such
162     // cases.
163     SValBuilder &SVB = C.getSValBuilder();
164     MemRegionManager &RMgr = C.getStateManager().getRegionManager();
165 
166     const MemSpaceRegion *GlobalSystemSpace =
167         RMgr.getGlobalsRegion(MemRegion::GlobalSystemSpaceRegionKind);
168 
169     // Create an artifical symbol for the region.
170     // It is not possible to associate a statement or expression in this case.
171     const SymbolConjured *Sym = SVB.conjureSymbol(
172         nullptr, C.getLocationContext(),
173         ACtx.getLValueReferenceType(ACtx.IntTy), C.blockCount(), &ErrnoDecl);
174 
175     // The symbolic region is untyped, create a typed sub-region in it.
176     // The ElementRegion is used to make the errno region a typed region.
177     const MemRegion *ErrnoR = RMgr.getElementRegion(
178         ACtx.IntTy, SVB.makeZeroArrayIndex(),
179         RMgr.getSymbolicRegion(Sym, GlobalSystemSpace), C.getASTContext());
180     State = State->set<ErrnoRegion>(ErrnoR);
181     State = errno_modeling::setErrnoValue(State, C, 0);
182     C.addTransition(State);
183   }
184 }
185 
186 bool ErrnoModeling::evalCall(const CallEvent &Call, CheckerContext &C) const {
187   // Return location of "errno" at a call to an "errno address returning"
188   // function.
189   if (ErrnoLocationCalls.contains(Call)) {
190     ProgramStateRef State = C.getState();
191 
192     const MemRegion *ErrnoR = getErrnoRegion(State);
193     if (!ErrnoR)
194       return false;
195 
196     State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(),
197                             loc::MemRegionVal{ErrnoR});
198     C.addTransition(State);
199     return true;
200   }
201 
202   return false;
203 }
204 
205 void ErrnoModeling::checkLiveSymbols(ProgramStateRef State,
206                                      SymbolReaper &SR) const {
207   // The special errno region should never garbage collected.
208   if (const auto *ErrnoR = getErrnoRegion(State))
209     SR.markLive(ErrnoR);
210 }
211 
212 namespace clang {
213 namespace ento {
214 namespace errno_modeling {
215 
216 Optional<SVal> getErrnoValue(ProgramStateRef State) {
217   const MemRegion *ErrnoR = getErrnoRegion(State);
218   if (!ErrnoR)
219     return {};
220   QualType IntTy = State->getAnalysisManager().getASTContext().IntTy;
221   return State->getSVal(ErrnoR, IntTy);
222 }
223 
224 ProgramStateRef setErrnoValue(ProgramStateRef State,
225                               const LocationContext *LCtx, SVal Value) {
226   const MemRegion *ErrnoR = getErrnoRegion(State);
227   if (!ErrnoR)
228     return State;
229   return State->bindLoc(loc::MemRegionVal{ErrnoR}, Value, LCtx);
230 }
231 
232 ProgramStateRef setErrnoValue(ProgramStateRef State, CheckerContext &C,
233                               uint64_t Value) {
234   const MemRegion *ErrnoR = getErrnoRegion(State);
235   if (!ErrnoR)
236     return State;
237   return State->bindLoc(
238       loc::MemRegionVal{ErrnoR},
239       C.getSValBuilder().makeIntVal(Value, C.getASTContext().IntTy),
240       C.getLocationContext());
241 }
242 
243 } // namespace errno_modeling
244 } // namespace ento
245 } // namespace clang
246 
247 void ento::registerErrnoModeling(CheckerManager &mgr) {
248   mgr.registerChecker<ErrnoModeling>();
249 }
250 
251 bool ento::shouldRegisterErrnoModeling(const CheckerManager &mgr) {
252   return true;
253 }
254