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