xref: /freebsd/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.cpp (revision a03411e84728e9b267056fd31c7d1d9d1dc1b01e)
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 "ErrnoModeling.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 #include "llvm/Support/FormatVariadic.h"
32 #include <optional>
33 
34 using namespace clang;
35 using namespace ento;
36 
37 namespace {
38 
39 // Name of the "errno" variable.
40 // FIXME: Is there a system where it is not called "errno" but is a variable?
41 const char *ErrnoVarName = "errno";
42 // Names of functions that return a location of the "errno" value.
43 // FIXME: Are there other similar function names?
44 const char *ErrnoLocationFuncNames[] = {"__errno_location", "___errno",
45                                         "__errno", "_errno", "__error"};
46 
47 class ErrnoModeling
48     : public Checker<check::ASTDecl<TranslationUnitDecl>, check::BeginFunction,
49                      check::LiveSymbols, eval::Call> {
50 public:
51   void checkASTDecl(const TranslationUnitDecl *D, AnalysisManager &Mgr,
52                     BugReporter &BR) const;
53   void checkBeginFunction(CheckerContext &C) const;
54   void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const;
55   bool evalCall(const CallEvent &Call, CheckerContext &C) const;
56 
57   // The declaration of an "errno" variable or "errno location" function.
58   mutable const Decl *ErrnoDecl = nullptr;
59 
60 private:
61   // FIXME: Names from `ErrnoLocationFuncNames` are used to build this set.
62   CallDescriptionSet ErrnoLocationCalls{{{"__errno_location"}, 0, 0},
63                                         {{"___errno"}, 0, 0},
64                                         {{"__errno"}, 0, 0},
65                                         {{"_errno"}, 0, 0},
66                                         {{"__error"}, 0, 0}};
67 };
68 
69 } // namespace
70 
71 /// Store a MemRegion that contains the 'errno' integer value.
72 /// The value is null if the 'errno' value was not recognized in the AST.
73 REGISTER_TRAIT_WITH_PROGRAMSTATE(ErrnoRegion, const MemRegion *)
74 
75 REGISTER_TRAIT_WITH_PROGRAMSTATE(ErrnoState, errno_modeling::ErrnoCheckState)
76 
77 /// Search for a variable called "errno" in the AST.
78 /// Return nullptr if not found.
79 static const VarDecl *getErrnoVar(ASTContext &ACtx) {
80   IdentifierInfo &II = ACtx.Idents.get(ErrnoVarName);
81   auto LookupRes = ACtx.getTranslationUnitDecl()->lookup(&II);
82   auto Found = llvm::find_if(LookupRes, [&ACtx](const Decl *D) {
83     if (auto *VD = dyn_cast<VarDecl>(D))
84       return ACtx.getSourceManager().isInSystemHeader(VD->getLocation()) &&
85              VD->hasExternalStorage() &&
86              VD->getType().getCanonicalType() == ACtx.IntTy;
87     return false;
88   });
89   if (Found == LookupRes.end())
90     return nullptr;
91 
92   return cast<VarDecl>(*Found);
93 }
94 
95 /// Search for a function with a specific name that is used to return a pointer
96 /// to "errno".
97 /// Return nullptr if no such function was found.
98 static const FunctionDecl *getErrnoFunc(ASTContext &ACtx) {
99   SmallVector<const Decl *> LookupRes;
100   for (StringRef ErrnoName : ErrnoLocationFuncNames) {
101     IdentifierInfo &II = ACtx.Idents.get(ErrnoName);
102     llvm::append_range(LookupRes, ACtx.getTranslationUnitDecl()->lookup(&II));
103   }
104 
105   auto Found = llvm::find_if(LookupRes, [&ACtx](const Decl *D) {
106     if (auto *FD = dyn_cast<FunctionDecl>(D))
107       return ACtx.getSourceManager().isInSystemHeader(FD->getLocation()) &&
108              FD->isExternC() && FD->getNumParams() == 0 &&
109              FD->getReturnType().getCanonicalType() ==
110                  ACtx.getPointerType(ACtx.IntTy);
111     return false;
112   });
113   if (Found == LookupRes.end())
114     return nullptr;
115 
116   return cast<FunctionDecl>(*Found);
117 }
118 
119 void ErrnoModeling::checkASTDecl(const TranslationUnitDecl *D,
120                                  AnalysisManager &Mgr, BugReporter &BR) const {
121   // Try to find an usable `errno` value.
122   // It can be an external variable called "errno" or a function that returns a
123   // pointer to the "errno" value. This function can have different names.
124   // The actual case is dependent on the C library implementation, we
125   // can only search for a match in one of these variations.
126   // We assume that exactly one of these cases might be true.
127   ErrnoDecl = getErrnoVar(Mgr.getASTContext());
128   if (!ErrnoDecl)
129     ErrnoDecl = getErrnoFunc(Mgr.getASTContext());
130 }
131 
132 void ErrnoModeling::checkBeginFunction(CheckerContext &C) const {
133   if (!C.inTopFrame())
134     return;
135 
136   ASTContext &ACtx = C.getASTContext();
137   ProgramStateRef State = C.getState();
138 
139   if (const auto *ErrnoVar = dyn_cast_or_null<VarDecl>(ErrnoDecl)) {
140     // There is an external 'errno' variable.
141     // Use its memory region.
142     // The memory region for an 'errno'-like variable is allocated in system
143     // space by MemRegionManager.
144     const MemRegion *ErrnoR =
145         State->getRegion(ErrnoVar, C.getLocationContext());
146     assert(ErrnoR && "Memory region should exist for the 'errno' variable.");
147     State = State->set<ErrnoRegion>(ErrnoR);
148     State =
149         errno_modeling::setErrnoValue(State, C, 0, errno_modeling::Irrelevant);
150     C.addTransition(State);
151   } else if (ErrnoDecl) {
152     assert(isa<FunctionDecl>(ErrnoDecl) && "Invalid errno location function.");
153     // There is a function that returns the location of 'errno'.
154     // We must create a memory region for it in system space.
155     // Currently a symbolic region is used with an artifical symbol.
156     // FIXME: It is better to have a custom (new) kind of MemRegion for such
157     // cases.
158     SValBuilder &SVB = C.getSValBuilder();
159     MemRegionManager &RMgr = C.getStateManager().getRegionManager();
160 
161     const MemSpaceRegion *GlobalSystemSpace =
162         RMgr.getGlobalsRegion(MemRegion::GlobalSystemSpaceRegionKind);
163 
164     // Create an artifical symbol for the region.
165     // It is not possible to associate a statement or expression in this case.
166     const SymbolConjured *Sym = SVB.conjureSymbol(
167         nullptr, C.getLocationContext(),
168         ACtx.getLValueReferenceType(ACtx.IntTy), C.blockCount(), &ErrnoDecl);
169 
170     // The symbolic region is untyped, create a typed sub-region in it.
171     // The ElementRegion is used to make the errno region a typed region.
172     const MemRegion *ErrnoR = RMgr.getElementRegion(
173         ACtx.IntTy, SVB.makeZeroArrayIndex(),
174         RMgr.getSymbolicRegion(Sym, GlobalSystemSpace), C.getASTContext());
175     State = State->set<ErrnoRegion>(ErrnoR);
176     State =
177         errno_modeling::setErrnoValue(State, C, 0, errno_modeling::Irrelevant);
178     C.addTransition(State);
179   }
180 }
181 
182 bool ErrnoModeling::evalCall(const CallEvent &Call, CheckerContext &C) const {
183   // Return location of "errno" at a call to an "errno address returning"
184   // function.
185   if (ErrnoLocationCalls.contains(Call)) {
186     ProgramStateRef State = C.getState();
187 
188     const MemRegion *ErrnoR = State->get<ErrnoRegion>();
189     if (!ErrnoR)
190       return false;
191 
192     State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(),
193                             loc::MemRegionVal{ErrnoR});
194     C.addTransition(State);
195     return true;
196   }
197 
198   return false;
199 }
200 
201 void ErrnoModeling::checkLiveSymbols(ProgramStateRef State,
202                                      SymbolReaper &SR) const {
203   // The special errno region should never garbage collected.
204   if (const auto *ErrnoR = State->get<ErrnoRegion>())
205     SR.markLive(ErrnoR);
206 }
207 
208 namespace clang {
209 namespace ento {
210 namespace errno_modeling {
211 
212 std::optional<SVal> getErrnoValue(ProgramStateRef State) {
213   const MemRegion *ErrnoR = State->get<ErrnoRegion>();
214   if (!ErrnoR)
215     return {};
216   QualType IntTy = State->getAnalysisManager().getASTContext().IntTy;
217   return State->getSVal(ErrnoR, IntTy);
218 }
219 
220 ProgramStateRef setErrnoValue(ProgramStateRef State,
221                               const LocationContext *LCtx, SVal Value,
222                               ErrnoCheckState EState) {
223   const MemRegion *ErrnoR = State->get<ErrnoRegion>();
224   if (!ErrnoR)
225     return State;
226   // First set the errno value, the old state is still available at 'checkBind'
227   // or 'checkLocation' for errno value.
228   State = State->bindLoc(loc::MemRegionVal{ErrnoR}, Value, LCtx);
229   return State->set<ErrnoState>(EState);
230 }
231 
232 ProgramStateRef setErrnoValue(ProgramStateRef State, CheckerContext &C,
233                               uint64_t Value, ErrnoCheckState EState) {
234   const MemRegion *ErrnoR = State->get<ErrnoRegion>();
235   if (!ErrnoR)
236     return State;
237   State = State->bindLoc(
238       loc::MemRegionVal{ErrnoR},
239       C.getSValBuilder().makeIntVal(Value, C.getASTContext().IntTy),
240       C.getLocationContext());
241   return State->set<ErrnoState>(EState);
242 }
243 
244 std::optional<Loc> getErrnoLoc(ProgramStateRef State) {
245   const MemRegion *ErrnoR = State->get<ErrnoRegion>();
246   if (!ErrnoR)
247     return {};
248   return loc::MemRegionVal{ErrnoR};
249 }
250 
251 ErrnoCheckState getErrnoState(ProgramStateRef State) {
252   return State->get<ErrnoState>();
253 }
254 
255 ProgramStateRef setErrnoState(ProgramStateRef State, ErrnoCheckState EState) {
256   return State->set<ErrnoState>(EState);
257 }
258 
259 ProgramStateRef clearErrnoState(ProgramStateRef State) {
260   return setErrnoState(State, Irrelevant);
261 }
262 
263 bool isErrno(const Decl *D) {
264   if (const auto *VD = dyn_cast_or_null<VarDecl>(D))
265     if (const IdentifierInfo *II = VD->getIdentifier())
266       return II->getName() == ErrnoVarName;
267   if (const auto *FD = dyn_cast_or_null<FunctionDecl>(D))
268     if (const IdentifierInfo *II = FD->getIdentifier())
269       return llvm::is_contained(ErrnoLocationFuncNames, II->getName());
270   return false;
271 }
272 
273 const NoteTag *getErrnoNoteTag(CheckerContext &C, const std::string &Message) {
274   return C.getNoteTag([Message](PathSensitiveBugReport &BR) -> std::string {
275     const MemRegion *ErrnoR = BR.getErrorNode()->getState()->get<ErrnoRegion>();
276     if (ErrnoR && BR.isInteresting(ErrnoR)) {
277       BR.markNotInteresting(ErrnoR);
278       return Message;
279     }
280     return "";
281   });
282 }
283 
284 ProgramStateRef setErrnoForStdSuccess(ProgramStateRef State,
285                                       CheckerContext &C) {
286   return setErrnoState(State, MustNotBeChecked);
287 }
288 
289 ProgramStateRef setErrnoForStdFailure(ProgramStateRef State, CheckerContext &C,
290                                       NonLoc ErrnoSym) {
291   SValBuilder &SVB = C.getSValBuilder();
292   NonLoc ZeroVal = SVB.makeZeroVal(C.getASTContext().IntTy).castAs<NonLoc>();
293   DefinedOrUnknownSVal Cond =
294       SVB.evalBinOp(State, BO_NE, ErrnoSym, ZeroVal, SVB.getConditionType())
295           .castAs<DefinedOrUnknownSVal>();
296   State = State->assume(Cond, true);
297   if (!State)
298     return nullptr;
299   return setErrnoValue(State, C.getLocationContext(), ErrnoSym, Irrelevant);
300 }
301 
302 ProgramStateRef setErrnoStdMustBeChecked(ProgramStateRef State,
303                                          CheckerContext &C,
304                                          const Expr *InvalE) {
305   const MemRegion *ErrnoR = State->get<ErrnoRegion>();
306   if (!ErrnoR)
307     return State;
308   State = State->invalidateRegions(ErrnoR, InvalE, C.blockCount(),
309                                    C.getLocationContext(), false);
310   if (!State)
311     return nullptr;
312   return setErrnoState(State, MustBeChecked);
313 }
314 
315 const NoteTag *getNoteTagForStdSuccess(CheckerContext &C, llvm::StringRef Fn) {
316   return getErrnoNoteTag(
317       C, llvm::formatv(
318              "'errno' may be undefined after successful call to '{0}'", Fn));
319 }
320 
321 const NoteTag *getNoteTagForStdMustBeChecked(CheckerContext &C,
322                                              llvm::StringRef Fn) {
323   return getErrnoNoteTag(
324       C, llvm::formatv("'{0}' indicates failure only by setting 'errno'", Fn));
325 }
326 
327 } // namespace errno_modeling
328 } // namespace ento
329 } // namespace clang
330 
331 void ento::registerErrnoModeling(CheckerManager &mgr) {
332   mgr.registerChecker<ErrnoModeling>();
333 }
334 
335 bool ento::shouldRegisterErrnoModeling(const CheckerManager &mgr) {
336   return true;
337 }
338