| 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(Name: ErrnoVarName); |
| 82 | auto LookupRes = ACtx.getTranslationUnitDecl()->lookup(Name: &II); |
| 83 | auto Found = llvm::find_if(Range&: LookupRes, P: [&ACtx](const Decl *D) { |
| 84 | if (auto *VD = dyn_cast<VarDecl>(Val: D)) |
| 85 | return ACtx.getSourceManager().isInSystemHeader(Loc: VD->getLocation()) && |
| 86 | VD->hasExternalStorage() && |
| 87 | VD->getType().getCanonicalType() == ACtx.IntTy; |
| 88 | return false; |
| 89 | }); |
| 90 | if (Found != LookupRes.end()) |
| 91 | ErrnoDecl = cast<VarDecl>(Val: *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(D: ErrnoDecl, LC: 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(K: 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 | Elem: C.getCFGElementRef(), LCtx: C.getLocationContext(), |
| 126 | type: ACtx.getLValueReferenceType(T: ACtx.IntTy), visitCount: C.blockCount(), symbolTag: &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 | elementType: ACtx.IntTy, Idx: SVB.makeZeroArrayIndex(), |
| 132 | superRegion: RMgr.getSymbolicRegion(Sym, MemSpace: GlobalSystemSpace), Ctx: C.getASTContext()); |
| 133 | } |
| 134 | assert(ErrnoR); |
| 135 | State = State->set<ErrnoRegion>(ErrnoR); |
| 136 | State = |
| 137 | errno_modeling::setErrnoValue(State, C, Value: 0, EState: 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(S: Call.getOriginExpr(), LCtx: C.getLocationContext(), |
| 152 | V: 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(region: 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(R: ErrnoR, T: 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(location: loc::MemRegionVal{ErrnoR}, V: 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 | location: loc::MemRegionVal{ErrnoR}, |
| 198 | V: C.getSValBuilder().makeIntVal(integer: Value, type: C.getASTContext().IntTy), |
| 199 | LCtx: 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, EState: Irrelevant); |
| 220 | } |
| 221 | |
| 222 | bool isErrnoLocationCall(const CallEvent &CE) { |
| 223 | return ErrnoLocationCalls.contains(Call: CE); |
| 224 | } |
| 225 | |
| 226 | const NoteTag *getErrnoNoteTag(CheckerContext &C, const std::string &Message) { |
| 227 | return C.getNoteTag(Cb: [Message](PathSensitiveBugReport &BR) -> std::string { |
| 228 | const MemRegion *ErrnoR = BR.getErrorNode()->getState()->get<ErrnoRegion>(); |
| 229 | if (ErrnoR && BR.isInteresting(R: ErrnoR)) { |
| 230 | BR.markNotInteresting(R: ErrnoR); |
| 231 | return Message; |
| 232 | } |
| 233 | return "" ; |
| 234 | }); |
| 235 | } |
| 236 | |
| 237 | ProgramStateRef setErrnoForStdSuccess(ProgramStateRef State, |
| 238 | CheckerContext &C) { |
| 239 | return setErrnoState(State, EState: MustNotBeChecked); |
| 240 | } |
| 241 | |
| 242 | ProgramStateRef setErrnoForStdFailure(ProgramStateRef State, CheckerContext &C, |
| 243 | NonLoc ErrnoSym) { |
| 244 | SValBuilder &SVB = C.getSValBuilder(); |
| 245 | NonLoc ZeroVal = SVB.makeZeroVal(type: C.getASTContext().IntTy).castAs<NonLoc>(); |
| 246 | DefinedOrUnknownSVal Cond = |
| 247 | SVB.evalBinOp(state: State, op: BO_NE, lhs: ErrnoSym, rhs: ZeroVal, type: SVB.getConditionType()) |
| 248 | .castAs<DefinedOrUnknownSVal>(); |
| 249 | State = State->assume(Cond, Assumption: true); |
| 250 | if (!State) |
| 251 | return nullptr; |
| 252 | return setErrnoValue(State, LCtx: C.getLocationContext(), Value: ErrnoSym, EState: 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(Regions: ErrnoR, Elem, BlockCount: C.blockCount(), |
| 262 | LCtx: C.getLocationContext(), CausesPointerEscape: false); |
| 263 | if (!State) |
| 264 | return nullptr; |
| 265 | return setErrnoState(State, EState: 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 | |