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