xref: /freebsd/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.cpp (revision e64bea71c21eb42e97aa615188ba91f6cce0d36d)
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/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
23 #include "clang/StaticAnalyzer/Core/Checker.h"
24 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
25 #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
26 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
27 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
28 #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
29 #include "llvm/ADT/STLExtras.h"
30 #include <optional>
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 
41 // Names of functions that return a location of the "errno" value.
42 // FIXME: Are there other similar function names?
43 CallDescriptionSet ErrnoLocationCalls{
44     {CDM::CLibrary, {"__errno_location"}, 0, 0},
45     {CDM::CLibrary, {"___errno"}, 0, 0},
46     {CDM::CLibrary, {"__errno"}, 0, 0},
47     {CDM::CLibrary, {"_errno"}, 0, 0},
48     {CDM::CLibrary, {"__error"}, 0, 0}};
49 
50 class ErrnoModeling
51     : public Checker<check::ASTDecl<TranslationUnitDecl>, check::BeginFunction,
52                      check::LiveSymbols, eval::Call> {
53 public:
54   void checkASTDecl(const TranslationUnitDecl *D, AnalysisManager &Mgr,
55                     BugReporter &BR) const;
56   void checkBeginFunction(CheckerContext &C) const;
57   void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const;
58   bool evalCall(const CallEvent &Call, CheckerContext &C) const;
59 
60 private:
61   // The declaration of an "errno" variable on systems where errno is
62   // represented by a variable (and not a function that queries its location).
63   mutable const VarDecl *ErrnoDecl = nullptr;
64 };
65 
66 } // namespace
67 
68 /// Store a MemRegion that contains the 'errno' integer value.
69 /// The value is null if the 'errno' value was not recognized in the AST.
70 REGISTER_TRAIT_WITH_PROGRAMSTATE(ErrnoRegion, const MemRegion *)
71 
72 REGISTER_TRAIT_WITH_PROGRAMSTATE(ErrnoState, errno_modeling::ErrnoCheckState)
73 
74 void ErrnoModeling::checkASTDecl(const TranslationUnitDecl *D,
75                                  AnalysisManager &Mgr, BugReporter &BR) const {
76   // Try to find the declaration of the external variable `int errno;`.
77   // There are also C library implementations, where the `errno` location is
78   // accessed via a function that returns its address; in those environments
79   // this callback has no effect.
80   ASTContext &ACtx = Mgr.getASTContext();
81   IdentifierInfo &II = ACtx.Idents.get(ErrnoVarName);
82   auto LookupRes = ACtx.getTranslationUnitDecl()->lookup(&II);
83   auto Found = llvm::find_if(LookupRes, [&ACtx](const Decl *D) {
84     if (auto *VD = dyn_cast<VarDecl>(D))
85       return ACtx.getSourceManager().isInSystemHeader(VD->getLocation()) &&
86              VD->hasExternalStorage() &&
87              VD->getType().getCanonicalType() == ACtx.IntTy;
88     return false;
89   });
90   if (Found != LookupRes.end())
91     ErrnoDecl = cast<VarDecl>(*Found);
92 }
93 
94 void ErrnoModeling::checkBeginFunction(CheckerContext &C) const {
95   if (!C.inTopFrame())
96     return;
97 
98   ASTContext &ACtx = C.getASTContext();
99   ProgramStateRef State = C.getState();
100 
101   const MemRegion *ErrnoR = nullptr;
102 
103   if (ErrnoDecl) {
104     // There is an external 'errno' variable, so we can simply use the memory
105     // region that's associated with it.
106     ErrnoR = State->getRegion(ErrnoDecl, C.getLocationContext());
107     assert(ErrnoR && "Memory region should exist for the 'errno' variable.");
108   } else {
109     // There is no 'errno' variable, so create a new symbolic memory region
110     // that can be used to model the return value of the "get the location of
111     // errno" internal functions.
112     // NOTE: this `SVal` is created even if errno is not defined or used.
113     SValBuilder &SVB = C.getSValBuilder();
114     MemRegionManager &RMgr = C.getStateManager().getRegionManager();
115 
116     const MemSpaceRegion *GlobalSystemSpace =
117         RMgr.getGlobalsRegion(MemRegion::GlobalSystemSpaceRegionKind);
118 
119     // Create an artifical symbol for the region.
120     // Note that it is not possible to associate a statement or expression in
121     // this case and the `symbolTag` (opaque pointer tag) is just the address
122     // of the data member `ErrnoDecl` of the singleton `ErrnoModeling` checker
123     // object.
124     const SymbolConjured *Sym = SVB.conjureSymbol(
125         C.getCFGElementRef(), C.getLocationContext(),
126         ACtx.getLValueReferenceType(ACtx.IntTy), C.blockCount(), &ErrnoDecl);
127 
128     // The symbolic region is untyped, create a typed sub-region in it.
129     // The ElementRegion is used to make the errno region a typed region.
130     ErrnoR = RMgr.getElementRegion(
131         ACtx.IntTy, SVB.makeZeroArrayIndex(),
132         RMgr.getSymbolicRegion(Sym, GlobalSystemSpace), C.getASTContext());
133   }
134   assert(ErrnoR);
135   State = State->set<ErrnoRegion>(ErrnoR);
136   State =
137       errno_modeling::setErrnoValue(State, C, 0, errno_modeling::Irrelevant);
138   C.addTransition(State);
139 }
140 
141 bool ErrnoModeling::evalCall(const CallEvent &Call, CheckerContext &C) const {
142   // Return location of "errno" at a call to an "errno address returning"
143   // function.
144   if (errno_modeling::isErrnoLocationCall(Call)) {
145     ProgramStateRef State = C.getState();
146 
147     const MemRegion *ErrnoR = State->get<ErrnoRegion>();
148     if (!ErrnoR)
149       return false;
150 
151     State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(),
152                             loc::MemRegionVal{ErrnoR});
153     C.addTransition(State);
154     return true;
155   }
156 
157   return false;
158 }
159 
160 void ErrnoModeling::checkLiveSymbols(ProgramStateRef State,
161                                      SymbolReaper &SR) const {
162   // The special errno region should never garbage collected.
163   if (const auto *ErrnoR = State->get<ErrnoRegion>())
164     SR.markLive(ErrnoR);
165 }
166 
167 namespace clang {
168 namespace ento {
169 namespace errno_modeling {
170 
171 std::optional<SVal> getErrnoValue(ProgramStateRef State) {
172   const MemRegion *ErrnoR = State->get<ErrnoRegion>();
173   if (!ErrnoR)
174     return {};
175   QualType IntTy = State->getAnalysisManager().getASTContext().IntTy;
176   return State->getSVal(ErrnoR, IntTy);
177 }
178 
179 ProgramStateRef setErrnoValue(ProgramStateRef State,
180                               const LocationContext *LCtx, SVal Value,
181                               ErrnoCheckState EState) {
182   const MemRegion *ErrnoR = State->get<ErrnoRegion>();
183   if (!ErrnoR)
184     return State;
185   // First set the errno value, the old state is still available at 'checkBind'
186   // or 'checkLocation' for errno value.
187   State = State->bindLoc(loc::MemRegionVal{ErrnoR}, Value, LCtx);
188   return State->set<ErrnoState>(EState);
189 }
190 
191 ProgramStateRef setErrnoValue(ProgramStateRef State, CheckerContext &C,
192                               uint64_t Value, ErrnoCheckState EState) {
193   const MemRegion *ErrnoR = State->get<ErrnoRegion>();
194   if (!ErrnoR)
195     return State;
196   State = State->bindLoc(
197       loc::MemRegionVal{ErrnoR},
198       C.getSValBuilder().makeIntVal(Value, C.getASTContext().IntTy),
199       C.getLocationContext());
200   return State->set<ErrnoState>(EState);
201 }
202 
203 std::optional<Loc> getErrnoLoc(ProgramStateRef State) {
204   const MemRegion *ErrnoR = State->get<ErrnoRegion>();
205   if (!ErrnoR)
206     return {};
207   return loc::MemRegionVal{ErrnoR};
208 }
209 
210 ErrnoCheckState getErrnoState(ProgramStateRef State) {
211   return State->get<ErrnoState>();
212 }
213 
214 ProgramStateRef setErrnoState(ProgramStateRef State, ErrnoCheckState EState) {
215   return State->set<ErrnoState>(EState);
216 }
217 
218 ProgramStateRef clearErrnoState(ProgramStateRef State) {
219   return setErrnoState(State, Irrelevant);
220 }
221 
222 bool isErrnoLocationCall(const CallEvent &CE) {
223   return ErrnoLocationCalls.contains(CE);
224 }
225 
226 const NoteTag *getErrnoNoteTag(CheckerContext &C, const std::string &Message) {
227   return C.getNoteTag([Message](PathSensitiveBugReport &BR) -> std::string {
228     const MemRegion *ErrnoR = BR.getErrorNode()->getState()->get<ErrnoRegion>();
229     if (ErrnoR && BR.isInteresting(ErrnoR)) {
230       BR.markNotInteresting(ErrnoR);
231       return Message;
232     }
233     return "";
234   });
235 }
236 
237 ProgramStateRef setErrnoForStdSuccess(ProgramStateRef State,
238                                       CheckerContext &C) {
239   return setErrnoState(State, MustNotBeChecked);
240 }
241 
242 ProgramStateRef setErrnoForStdFailure(ProgramStateRef State, CheckerContext &C,
243                                       NonLoc ErrnoSym) {
244   SValBuilder &SVB = C.getSValBuilder();
245   NonLoc ZeroVal = SVB.makeZeroVal(C.getASTContext().IntTy).castAs<NonLoc>();
246   DefinedOrUnknownSVal Cond =
247       SVB.evalBinOp(State, BO_NE, ErrnoSym, ZeroVal, SVB.getConditionType())
248           .castAs<DefinedOrUnknownSVal>();
249   State = State->assume(Cond, true);
250   if (!State)
251     return nullptr;
252   return setErrnoValue(State, C.getLocationContext(), ErrnoSym, Irrelevant);
253 }
254 
255 ProgramStateRef setErrnoStdMustBeChecked(ProgramStateRef State,
256                                          CheckerContext &C,
257                                          ConstCFGElementRef Elem) {
258   const MemRegion *ErrnoR = State->get<ErrnoRegion>();
259   if (!ErrnoR)
260     return State;
261   State = State->invalidateRegions(ErrnoR, Elem, C.blockCount(),
262                                    C.getLocationContext(), false);
263   if (!State)
264     return nullptr;
265   return setErrnoState(State, MustBeChecked);
266 }
267 
268 } // namespace errno_modeling
269 } // namespace ento
270 } // namespace clang
271 
272 void ento::registerErrnoModeling(CheckerManager &mgr) {
273   mgr.registerChecker<ErrnoModeling>();
274 }
275 
276 bool ento::shouldRegisterErrnoModeling(const CheckerManager &mgr) {
277   return true;
278 }
279