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. 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 96 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 143 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 162 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 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 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 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 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 212 ErrnoCheckState getErrnoState(ProgramStateRef State) { 213 return State->get<ErrnoState>(); 214 } 215 216 ProgramStateRef setErrnoState(ProgramStateRef State, ErrnoCheckState EState) { 217 return State->set<ErrnoState>(EState); 218 } 219 220 ProgramStateRef clearErrnoState(ProgramStateRef State) { 221 return setErrnoState(State, Irrelevant); 222 } 223 224 bool isErrnoLocationCall(const CallEvent &CE) { 225 return ErrnoLocationCalls.contains(CE); 226 } 227 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 239 ProgramStateRef setErrnoForStdSuccess(ProgramStateRef State, 240 CheckerContext &C) { 241 return setErrnoState(State, MustNotBeChecked); 242 } 243 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 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 274 void ento::registerErrnoModeling(CheckerManager &mgr) { 275 mgr.registerChecker<ErrnoModeling>(); 276 } 277 278 bool ento::shouldRegisterErrnoModeling(const CheckerManager &mgr) { 279 return true; 280 } 281