1 1.1 joerg //===-- StreamChecker.cpp -----------------------------------------*- C++ -*--// 2 1.1 joerg // 3 1.1 joerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 1.1 joerg // See https://llvm.org/LICENSE.txt for license information. 5 1.1 joerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 1.1 joerg // 7 1.1 joerg //===----------------------------------------------------------------------===// 8 1.1 joerg // 9 1.1 joerg // This file defines checkers that model and check stream handling functions. 10 1.1 joerg // 11 1.1 joerg //===----------------------------------------------------------------------===// 12 1.1 joerg 13 1.1 joerg #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 14 1.1 joerg #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 15 1.1 joerg #include "clang/StaticAnalyzer/Core/Checker.h" 16 1.1 joerg #include "clang/StaticAnalyzer/Core/CheckerManager.h" 17 1.1 joerg #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 18 1.1 joerg #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 19 1.1 joerg #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" 20 1.1 joerg #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" 21 1.1 joerg #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" 22 1.1.1.2 joerg #include <functional> 23 1.1 joerg 24 1.1 joerg using namespace clang; 25 1.1 joerg using namespace ento; 26 1.1.1.2 joerg using namespace std::placeholders; 27 1.1 joerg 28 1.1 joerg namespace { 29 1.1 joerg 30 1.1.1.2 joerg struct FnDescription; 31 1.1 joerg 32 1.1.1.2 joerg /// State of the stream error flags. 33 1.1.1.2 joerg /// Sometimes it is not known to the checker what error flags are set. 34 1.1.1.2 joerg /// This is indicated by setting more than one flag to true. 35 1.1.1.2 joerg /// This is an optimization to avoid state splits. 36 1.1.1.2 joerg /// A stream can either be in FEOF or FERROR but not both at the same time. 37 1.1.1.2 joerg /// Multiple flags are set to handle the corresponding states together. 38 1.1.1.2 joerg struct StreamErrorState { 39 1.1.1.2 joerg /// The stream can be in state where none of the error flags set. 40 1.1.1.2 joerg bool NoError = true; 41 1.1.1.2 joerg /// The stream can be in state where the EOF indicator is set. 42 1.1.1.2 joerg bool FEof = false; 43 1.1.1.2 joerg /// The stream can be in state where the error indicator is set. 44 1.1.1.2 joerg bool FError = false; 45 1.1 joerg 46 1.1.1.2 joerg bool isNoError() const { return NoError && !FEof && !FError; } 47 1.1.1.2 joerg bool isFEof() const { return !NoError && FEof && !FError; } 48 1.1.1.2 joerg bool isFError() const { return !NoError && !FEof && FError; } 49 1.1 joerg 50 1.1.1.2 joerg bool operator==(const StreamErrorState &ES) const { 51 1.1.1.2 joerg return NoError == ES.NoError && FEof == ES.FEof && FError == ES.FError; 52 1.1 joerg } 53 1.1 joerg 54 1.1.1.2 joerg bool operator!=(const StreamErrorState &ES) const { return !(*this == ES); } 55 1.1.1.2 joerg 56 1.1.1.2 joerg StreamErrorState operator|(const StreamErrorState &E) const { 57 1.1.1.2 joerg return {NoError || E.NoError, FEof || E.FEof, FError || E.FError}; 58 1.1 joerg } 59 1.1.1.2 joerg 60 1.1.1.2 joerg StreamErrorState operator&(const StreamErrorState &E) const { 61 1.1.1.2 joerg return {NoError && E.NoError, FEof && E.FEof, FError && E.FError}; 62 1.1 joerg } 63 1.1 joerg 64 1.1.1.2 joerg StreamErrorState operator~() const { return {!NoError, !FEof, !FError}; } 65 1.1.1.2 joerg 66 1.1.1.2 joerg /// Returns if the StreamErrorState is a valid object. 67 1.1.1.2 joerg operator bool() const { return NoError || FEof || FError; } 68 1.1.1.2 joerg 69 1.1 joerg void Profile(llvm::FoldingSetNodeID &ID) const { 70 1.1.1.2 joerg ID.AddBoolean(NoError); 71 1.1.1.2 joerg ID.AddBoolean(FEof); 72 1.1.1.2 joerg ID.AddBoolean(FError); 73 1.1 joerg } 74 1.1 joerg }; 75 1.1 joerg 76 1.1.1.2 joerg const StreamErrorState ErrorNone{true, false, false}; 77 1.1.1.2 joerg const StreamErrorState ErrorFEof{false, true, false}; 78 1.1.1.2 joerg const StreamErrorState ErrorFError{false, false, true}; 79 1.1 joerg 80 1.1.1.2 joerg /// Full state information about a stream pointer. 81 1.1.1.2 joerg struct StreamState { 82 1.1.1.2 joerg /// The last file operation called in the stream. 83 1.1.1.2 joerg const FnDescription *LastOperation; 84 1.1.1.2 joerg 85 1.1.1.2 joerg /// State of a stream symbol. 86 1.1.1.2 joerg /// FIXME: We need maybe an "escaped" state later. 87 1.1.1.2 joerg enum KindTy { 88 1.1.1.2 joerg Opened, /// Stream is opened. 89 1.1.1.2 joerg Closed, /// Closed stream (an invalid stream pointer after it was closed). 90 1.1.1.2 joerg OpenFailed /// The last open operation has failed. 91 1.1.1.2 joerg } State; 92 1.1.1.2 joerg 93 1.1.1.2 joerg /// State of the error flags. 94 1.1.1.2 joerg /// Ignored in non-opened stream state but must be NoError. 95 1.1.1.2 joerg StreamErrorState const ErrorState; 96 1.1.1.2 joerg 97 1.1.1.2 joerg /// Indicate if the file has an "indeterminate file position indicator". 98 1.1.1.2 joerg /// This can be set at a failing read or write or seek operation. 99 1.1.1.2 joerg /// If it is set no more read or write is allowed. 100 1.1.1.2 joerg /// This value is not dependent on the stream error flags: 101 1.1.1.2 joerg /// The error flag may be cleared with `clearerr` but the file position 102 1.1.1.2 joerg /// remains still indeterminate. 103 1.1.1.2 joerg /// This value applies to all error states in ErrorState except FEOF. 104 1.1.1.2 joerg /// An EOF+indeterminate state is the same as EOF state. 105 1.1.1.2 joerg bool const FilePositionIndeterminate = false; 106 1.1.1.2 joerg 107 1.1.1.2 joerg StreamState(const FnDescription *L, KindTy S, const StreamErrorState &ES, 108 1.1.1.2 joerg bool IsFilePositionIndeterminate) 109 1.1.1.2 joerg : LastOperation(L), State(S), ErrorState(ES), 110 1.1.1.2 joerg FilePositionIndeterminate(IsFilePositionIndeterminate) { 111 1.1.1.2 joerg assert((!ES.isFEof() || !IsFilePositionIndeterminate) && 112 1.1.1.2 joerg "FilePositionIndeterminate should be false in FEof case."); 113 1.1.1.2 joerg assert((State == Opened || ErrorState.isNoError()) && 114 1.1.1.2 joerg "ErrorState should be None in non-opened stream state."); 115 1.1.1.2 joerg } 116 1.1.1.2 joerg 117 1.1.1.2 joerg bool isOpened() const { return State == Opened; } 118 1.1.1.2 joerg bool isClosed() const { return State == Closed; } 119 1.1.1.2 joerg bool isOpenFailed() const { return State == OpenFailed; } 120 1.1.1.2 joerg 121 1.1.1.2 joerg bool operator==(const StreamState &X) const { 122 1.1.1.2 joerg // In not opened state error state should always NoError, so comparison 123 1.1.1.2 joerg // here is no problem. 124 1.1.1.2 joerg return LastOperation == X.LastOperation && State == X.State && 125 1.1.1.2 joerg ErrorState == X.ErrorState && 126 1.1.1.2 joerg FilePositionIndeterminate == X.FilePositionIndeterminate; 127 1.1.1.2 joerg } 128 1.1.1.2 joerg 129 1.1.1.2 joerg static StreamState getOpened(const FnDescription *L, 130 1.1.1.2 joerg const StreamErrorState &ES = ErrorNone, 131 1.1.1.2 joerg bool IsFilePositionIndeterminate = false) { 132 1.1.1.2 joerg return StreamState{L, Opened, ES, IsFilePositionIndeterminate}; 133 1.1.1.2 joerg } 134 1.1.1.2 joerg static StreamState getClosed(const FnDescription *L) { 135 1.1.1.2 joerg return StreamState{L, Closed, {}, false}; 136 1.1.1.2 joerg } 137 1.1.1.2 joerg static StreamState getOpenFailed(const FnDescription *L) { 138 1.1.1.2 joerg return StreamState{L, OpenFailed, {}, false}; 139 1.1.1.2 joerg } 140 1.1.1.2 joerg 141 1.1.1.2 joerg void Profile(llvm::FoldingSetNodeID &ID) const { 142 1.1.1.2 joerg ID.AddPointer(LastOperation); 143 1.1.1.2 joerg ID.AddInteger(State); 144 1.1.1.2 joerg ID.AddInteger(ErrorState); 145 1.1.1.2 joerg ID.AddBoolean(FilePositionIndeterminate); 146 1.1.1.2 joerg } 147 1.1.1.2 joerg }; 148 1.1.1.2 joerg 149 1.1.1.2 joerg class StreamChecker; 150 1.1.1.2 joerg using FnCheck = std::function<void(const StreamChecker *, const FnDescription *, 151 1.1.1.2 joerg const CallEvent &, CheckerContext &)>; 152 1.1.1.2 joerg 153 1.1.1.2 joerg using ArgNoTy = unsigned int; 154 1.1.1.2 joerg static const ArgNoTy ArgNone = std::numeric_limits<ArgNoTy>::max(); 155 1.1.1.2 joerg 156 1.1.1.2 joerg struct FnDescription { 157 1.1.1.2 joerg FnCheck PreFn; 158 1.1.1.2 joerg FnCheck EvalFn; 159 1.1.1.2 joerg ArgNoTy StreamArgNo; 160 1.1.1.2 joerg }; 161 1.1.1.2 joerg 162 1.1.1.2 joerg /// Get the value of the stream argument out of the passed call event. 163 1.1.1.2 joerg /// The call should contain a function that is described by Desc. 164 1.1.1.2 joerg SVal getStreamArg(const FnDescription *Desc, const CallEvent &Call) { 165 1.1.1.2 joerg assert(Desc && Desc->StreamArgNo != ArgNone && 166 1.1.1.2 joerg "Try to get a non-existing stream argument."); 167 1.1.1.2 joerg return Call.getArgSVal(Desc->StreamArgNo); 168 1.1.1.2 joerg } 169 1.1 joerg 170 1.1.1.2 joerg /// Create a conjured symbol return value for a call expression. 171 1.1.1.2 joerg DefinedSVal makeRetVal(CheckerContext &C, const CallExpr *CE) { 172 1.1.1.2 joerg assert(CE && "Expecting a call expression."); 173 1.1.1.2 joerg 174 1.1.1.2 joerg const LocationContext *LCtx = C.getLocationContext(); 175 1.1.1.2 joerg return C.getSValBuilder() 176 1.1.1.2 joerg .conjureSymbolVal(nullptr, CE, LCtx, C.blockCount()) 177 1.1.1.2 joerg .castAs<DefinedSVal>(); 178 1.1.1.2 joerg } 179 1.1.1.2 joerg 180 1.1.1.2 joerg ProgramStateRef bindAndAssumeTrue(ProgramStateRef State, CheckerContext &C, 181 1.1.1.2 joerg const CallExpr *CE) { 182 1.1.1.2 joerg DefinedSVal RetVal = makeRetVal(C, CE); 183 1.1.1.2 joerg State = State->BindExpr(CE, C.getLocationContext(), RetVal); 184 1.1.1.2 joerg State = State->assume(RetVal, true); 185 1.1.1.2 joerg assert(State && "Assumption on new value should not fail."); 186 1.1.1.2 joerg return State; 187 1.1.1.2 joerg } 188 1.1.1.2 joerg 189 1.1.1.2 joerg ProgramStateRef bindInt(uint64_t Value, ProgramStateRef State, 190 1.1.1.2 joerg CheckerContext &C, const CallExpr *CE) { 191 1.1.1.2 joerg State = State->BindExpr(CE, C.getLocationContext(), 192 1.1.1.2 joerg C.getSValBuilder().makeIntVal(Value, false)); 193 1.1.1.2 joerg return State; 194 1.1.1.2 joerg } 195 1.1.1.2 joerg 196 1.1.1.2 joerg class StreamChecker : public Checker<check::PreCall, eval::Call, 197 1.1.1.2 joerg check::DeadSymbols, check::PointerEscape> { 198 1.1.1.2 joerg BugType BT_FileNull{this, "NULL stream pointer", "Stream handling error"}; 199 1.1.1.2 joerg BugType BT_UseAfterClose{this, "Closed stream", "Stream handling error"}; 200 1.1.1.2 joerg BugType BT_UseAfterOpenFailed{this, "Invalid stream", 201 1.1.1.2 joerg "Stream handling error"}; 202 1.1.1.2 joerg BugType BT_IndeterminatePosition{this, "Invalid stream state", 203 1.1.1.2 joerg "Stream handling error"}; 204 1.1.1.2 joerg BugType BT_IllegalWhence{this, "Illegal whence argument", 205 1.1.1.2 joerg "Stream handling error"}; 206 1.1.1.2 joerg BugType BT_StreamEof{this, "Stream already in EOF", "Stream handling error"}; 207 1.1.1.2 joerg BugType BT_ResourceLeak{this, "Resource leak", "Stream handling error", 208 1.1.1.2 joerg /*SuppressOnSink =*/true}; 209 1.1.1.2 joerg 210 1.1.1.2 joerg public: 211 1.1.1.2 joerg void checkPreCall(const CallEvent &Call, CheckerContext &C) const; 212 1.1 joerg bool evalCall(const CallEvent &Call, CheckerContext &C) const; 213 1.1 joerg void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; 214 1.1.1.2 joerg ProgramStateRef checkPointerEscape(ProgramStateRef State, 215 1.1.1.2 joerg const InvalidatedSymbols &Escaped, 216 1.1.1.2 joerg const CallEvent *Call, 217 1.1.1.2 joerg PointerEscapeKind Kind) const; 218 1.1.1.2 joerg 219 1.1.1.2 joerg /// If true, evaluate special testing stream functions. 220 1.1.1.2 joerg bool TestMode = false; 221 1.1 joerg 222 1.1 joerg private: 223 1.1.1.2 joerg CallDescriptionMap<FnDescription> FnDescriptions = { 224 1.1.1.2 joerg {{"fopen"}, {nullptr, &StreamChecker::evalFopen, ArgNone}}, 225 1.1.1.2 joerg {{"freopen", 3}, 226 1.1.1.2 joerg {&StreamChecker::preFreopen, &StreamChecker::evalFreopen, 2}}, 227 1.1.1.2 joerg {{"tmpfile"}, {nullptr, &StreamChecker::evalFopen, ArgNone}}, 228 1.1.1.2 joerg {{"fclose", 1}, 229 1.1.1.2 joerg {&StreamChecker::preDefault, &StreamChecker::evalFclose, 0}}, 230 1.1.1.2 joerg {{"fread", 4}, 231 1.1.1.2 joerg {&StreamChecker::preFread, 232 1.1.1.2 joerg std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, true), 3}}, 233 1.1.1.2 joerg {{"fwrite", 4}, 234 1.1.1.2 joerg {&StreamChecker::preFwrite, 235 1.1.1.2 joerg std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, false), 3}}, 236 1.1.1.2 joerg {{"fseek", 3}, {&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}}, 237 1.1.1.2 joerg {{"ftell", 1}, {&StreamChecker::preDefault, nullptr, 0}}, 238 1.1.1.2 joerg {{"rewind", 1}, {&StreamChecker::preDefault, nullptr, 0}}, 239 1.1.1.2 joerg {{"fgetpos", 2}, {&StreamChecker::preDefault, nullptr, 0}}, 240 1.1.1.2 joerg {{"fsetpos", 2}, {&StreamChecker::preDefault, nullptr, 0}}, 241 1.1.1.2 joerg {{"clearerr", 1}, 242 1.1.1.2 joerg {&StreamChecker::preDefault, &StreamChecker::evalClearerr, 0}}, 243 1.1.1.2 joerg {{"feof", 1}, 244 1.1.1.2 joerg {&StreamChecker::preDefault, 245 1.1.1.2 joerg std::bind(&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFEof), 246 1.1.1.2 joerg 0}}, 247 1.1.1.2 joerg {{"ferror", 1}, 248 1.1.1.2 joerg {&StreamChecker::preDefault, 249 1.1.1.2 joerg std::bind(&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFError), 250 1.1.1.2 joerg 0}}, 251 1.1.1.2 joerg {{"fileno", 1}, {&StreamChecker::preDefault, nullptr, 0}}, 252 1.1.1.2 joerg }; 253 1.1.1.2 joerg 254 1.1.1.2 joerg CallDescriptionMap<FnDescription> FnTestDescriptions = { 255 1.1.1.2 joerg {{"StreamTesterChecker_make_feof_stream", 1}, 256 1.1.1.2 joerg {nullptr, 257 1.1.1.2 joerg std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4, ErrorFEof), 258 1.1.1.2 joerg 0}}, 259 1.1.1.2 joerg {{"StreamTesterChecker_make_ferror_stream", 1}, 260 1.1.1.2 joerg {nullptr, 261 1.1.1.2 joerg std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4, 262 1.1.1.2 joerg ErrorFError), 263 1.1.1.2 joerg 0}}, 264 1.1.1.2 joerg }; 265 1.1.1.2 joerg 266 1.1.1.2 joerg void evalFopen(const FnDescription *Desc, const CallEvent &Call, 267 1.1.1.2 joerg CheckerContext &C) const; 268 1.1.1.2 joerg 269 1.1.1.2 joerg void preFreopen(const FnDescription *Desc, const CallEvent &Call, 270 1.1.1.2 joerg CheckerContext &C) const; 271 1.1.1.2 joerg void evalFreopen(const FnDescription *Desc, const CallEvent &Call, 272 1.1.1.2 joerg CheckerContext &C) const; 273 1.1.1.2 joerg 274 1.1.1.2 joerg void evalFclose(const FnDescription *Desc, const CallEvent &Call, 275 1.1.1.2 joerg CheckerContext &C) const; 276 1.1.1.2 joerg 277 1.1.1.2 joerg void preFread(const FnDescription *Desc, const CallEvent &Call, 278 1.1.1.2 joerg CheckerContext &C) const; 279 1.1.1.2 joerg 280 1.1.1.2 joerg void preFwrite(const FnDescription *Desc, const CallEvent &Call, 281 1.1.1.2 joerg CheckerContext &C) const; 282 1.1.1.2 joerg 283 1.1.1.2 joerg void evalFreadFwrite(const FnDescription *Desc, const CallEvent &Call, 284 1.1.1.2 joerg CheckerContext &C, bool IsFread) const; 285 1.1.1.2 joerg 286 1.1.1.2 joerg void preFseek(const FnDescription *Desc, const CallEvent &Call, 287 1.1.1.2 joerg CheckerContext &C) const; 288 1.1.1.2 joerg void evalFseek(const FnDescription *Desc, const CallEvent &Call, 289 1.1.1.2 joerg CheckerContext &C) const; 290 1.1.1.2 joerg 291 1.1.1.2 joerg void preDefault(const FnDescription *Desc, const CallEvent &Call, 292 1.1.1.2 joerg CheckerContext &C) const; 293 1.1.1.2 joerg 294 1.1.1.2 joerg void evalClearerr(const FnDescription *Desc, const CallEvent &Call, 295 1.1.1.2 joerg CheckerContext &C) const; 296 1.1.1.2 joerg 297 1.1.1.2 joerg void evalFeofFerror(const FnDescription *Desc, const CallEvent &Call, 298 1.1.1.2 joerg CheckerContext &C, 299 1.1.1.2 joerg const StreamErrorState &ErrorKind) const; 300 1.1.1.2 joerg 301 1.1.1.2 joerg void evalSetFeofFerror(const FnDescription *Desc, const CallEvent &Call, 302 1.1.1.2 joerg CheckerContext &C, 303 1.1.1.2 joerg const StreamErrorState &ErrorKind) const; 304 1.1.1.2 joerg 305 1.1.1.2 joerg /// Check that the stream (in StreamVal) is not NULL. 306 1.1.1.2 joerg /// If it can only be NULL a fatal error is emitted and nullptr returned. 307 1.1.1.2 joerg /// Otherwise the return value is a new state where the stream is constrained 308 1.1.1.2 joerg /// to be non-null. 309 1.1.1.2 joerg ProgramStateRef ensureStreamNonNull(SVal StreamVal, CheckerContext &C, 310 1.1.1.2 joerg ProgramStateRef State) const; 311 1.1.1.2 joerg 312 1.1.1.2 joerg /// Check that the stream is the opened state. 313 1.1.1.2 joerg /// If the stream is known to be not opened an error is generated 314 1.1.1.2 joerg /// and nullptr returned, otherwise the original state is returned. 315 1.1.1.2 joerg ProgramStateRef ensureStreamOpened(SVal StreamVal, CheckerContext &C, 316 1.1.1.2 joerg ProgramStateRef State) const; 317 1.1.1.2 joerg 318 1.1.1.2 joerg /// Check that the stream has not an invalid ("indeterminate") file position, 319 1.1.1.2 joerg /// generate warning for it. 320 1.1.1.2 joerg /// (EOF is not an invalid position.) 321 1.1.1.2 joerg /// The returned state can be nullptr if a fatal error was generated. 322 1.1.1.2 joerg /// It can return non-null state if the stream has not an invalid position or 323 1.1.1.2 joerg /// there is execution path with non-invalid position. 324 1.1.1.2 joerg ProgramStateRef 325 1.1.1.2 joerg ensureNoFilePositionIndeterminate(SVal StreamVal, CheckerContext &C, 326 1.1.1.2 joerg ProgramStateRef State) const; 327 1.1.1.2 joerg 328 1.1.1.2 joerg /// Check the legality of the 'whence' argument of 'fseek'. 329 1.1.1.2 joerg /// Generate error and return nullptr if it is found to be illegal. 330 1.1.1.2 joerg /// Otherwise returns the state. 331 1.1.1.2 joerg /// (State is not changed here because the "whence" value is already known.) 332 1.1.1.2 joerg ProgramStateRef ensureFseekWhenceCorrect(SVal WhenceVal, CheckerContext &C, 333 1.1.1.2 joerg ProgramStateRef State) const; 334 1.1.1.2 joerg 335 1.1.1.2 joerg /// Generate warning about stream in EOF state. 336 1.1.1.2 joerg /// There will be always a state transition into the passed State, 337 1.1.1.2 joerg /// by the new non-fatal error node or (if failed) a normal transition, 338 1.1.1.2 joerg /// to ensure uniform handling. 339 1.1.1.2 joerg void reportFEofWarning(CheckerContext &C, ProgramStateRef State) const; 340 1.1.1.2 joerg 341 1.1.1.2 joerg /// Emit resource leak warnings for the given symbols. 342 1.1.1.2 joerg /// Createn a non-fatal error node for these, and returns it (if any warnings 343 1.1.1.2 joerg /// were generated). Return value is non-null. 344 1.1.1.2 joerg ExplodedNode *reportLeaks(const SmallVector<SymbolRef, 2> &LeakedSyms, 345 1.1.1.2 joerg CheckerContext &C, ExplodedNode *Pred) const; 346 1.1.1.2 joerg 347 1.1.1.2 joerg /// Find the description data of the function called by a call event. 348 1.1.1.2 joerg /// Returns nullptr if no function is recognized. 349 1.1.1.2 joerg const FnDescription *lookupFn(const CallEvent &Call) const { 350 1.1.1.2 joerg // Recognize "global C functions" with only integral or pointer arguments 351 1.1.1.2 joerg // (and matching name) as stream functions. 352 1.1.1.2 joerg if (!Call.isGlobalCFunction()) 353 1.1.1.2 joerg return nullptr; 354 1.1.1.2 joerg for (auto P : Call.parameters()) { 355 1.1.1.2 joerg QualType T = P->getType(); 356 1.1.1.2 joerg if (!T->isIntegralOrEnumerationType() && !T->isPointerType()) 357 1.1.1.2 joerg return nullptr; 358 1.1.1.2 joerg } 359 1.1.1.2 joerg 360 1.1.1.2 joerg return FnDescriptions.lookup(Call); 361 1.1.1.2 joerg } 362 1.1.1.2 joerg 363 1.1.1.2 joerg /// Generate a message for BugReporterVisitor if the stored symbol is 364 1.1.1.2 joerg /// marked as interesting by the actual bug report. 365 1.1.1.2 joerg struct NoteFn { 366 1.1.1.2 joerg const CheckerNameRef CheckerName; 367 1.1.1.2 joerg SymbolRef StreamSym; 368 1.1.1.2 joerg std::string Message; 369 1.1.1.2 joerg 370 1.1.1.2 joerg std::string operator()(PathSensitiveBugReport &BR) const { 371 1.1.1.2 joerg if (BR.isInteresting(StreamSym) && 372 1.1.1.2 joerg CheckerName == BR.getBugType().getCheckerName()) 373 1.1.1.2 joerg return Message; 374 1.1.1.2 joerg 375 1.1.1.2 joerg return ""; 376 1.1.1.2 joerg } 377 1.1.1.2 joerg }; 378 1.1.1.2 joerg 379 1.1.1.2 joerg const NoteTag *constructNoteTag(CheckerContext &C, SymbolRef StreamSym, 380 1.1.1.2 joerg const std::string &Message) const { 381 1.1.1.2 joerg return C.getNoteTag(NoteFn{getCheckerName(), StreamSym, Message}); 382 1.1.1.2 joerg } 383 1.1.1.2 joerg 384 1.1.1.2 joerg /// Searches for the ExplodedNode where the file descriptor was acquired for 385 1.1.1.2 joerg /// StreamSym. 386 1.1.1.2 joerg static const ExplodedNode *getAcquisitionSite(const ExplodedNode *N, 387 1.1.1.2 joerg SymbolRef StreamSym, 388 1.1.1.2 joerg CheckerContext &C); 389 1.1 joerg }; 390 1.1 joerg 391 1.1 joerg } // end anonymous namespace 392 1.1 joerg 393 1.1 joerg REGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState) 394 1.1 joerg 395 1.1.1.2 joerg inline void assertStreamStateOpened(const StreamState *SS) { 396 1.1.1.2 joerg assert(SS->isOpened() && 397 1.1.1.2 joerg "Previous create of error node for non-opened stream failed?"); 398 1.1.1.2 joerg } 399 1.1.1.2 joerg 400 1.1.1.2 joerg const ExplodedNode *StreamChecker::getAcquisitionSite(const ExplodedNode *N, 401 1.1.1.2 joerg SymbolRef StreamSym, 402 1.1.1.2 joerg CheckerContext &C) { 403 1.1.1.2 joerg ProgramStateRef State = N->getState(); 404 1.1.1.2 joerg // When bug type is resource leak, exploded node N may not have state info 405 1.1.1.2 joerg // for leaked file descriptor, but predecessor should have it. 406 1.1.1.2 joerg if (!State->get<StreamMap>(StreamSym)) 407 1.1.1.2 joerg N = N->getFirstPred(); 408 1.1.1.2 joerg 409 1.1.1.2 joerg const ExplodedNode *Pred = N; 410 1.1.1.2 joerg while (N) { 411 1.1.1.2 joerg State = N->getState(); 412 1.1.1.2 joerg if (!State->get<StreamMap>(StreamSym)) 413 1.1.1.2 joerg return Pred; 414 1.1.1.2 joerg Pred = N; 415 1.1.1.2 joerg N = N->getFirstPred(); 416 1.1.1.2 joerg } 417 1.1.1.2 joerg 418 1.1.1.2 joerg return nullptr; 419 1.1.1.2 joerg } 420 1.1.1.2 joerg 421 1.1.1.2 joerg void StreamChecker::checkPreCall(const CallEvent &Call, 422 1.1.1.2 joerg CheckerContext &C) const { 423 1.1.1.2 joerg const FnDescription *Desc = lookupFn(Call); 424 1.1.1.2 joerg if (!Desc || !Desc->PreFn) 425 1.1.1.2 joerg return; 426 1.1.1.2 joerg 427 1.1.1.2 joerg Desc->PreFn(this, Desc, Call, C); 428 1.1.1.2 joerg } 429 1.1 joerg 430 1.1 joerg bool StreamChecker::evalCall(const CallEvent &Call, CheckerContext &C) const { 431 1.1.1.2 joerg const FnDescription *Desc = lookupFn(Call); 432 1.1.1.2 joerg if (!Desc && TestMode) 433 1.1.1.2 joerg Desc = FnTestDescriptions.lookup(Call); 434 1.1.1.2 joerg if (!Desc || !Desc->EvalFn) 435 1.1 joerg return false; 436 1.1 joerg 437 1.1.1.2 joerg Desc->EvalFn(this, Desc, Call, C); 438 1.1.1.2 joerg 439 1.1.1.2 joerg return C.isDifferent(); 440 1.1.1.2 joerg } 441 1.1.1.2 joerg 442 1.1.1.2 joerg void StreamChecker::evalFopen(const FnDescription *Desc, const CallEvent &Call, 443 1.1.1.2 joerg CheckerContext &C) const { 444 1.1.1.2 joerg ProgramStateRef State = C.getState(); 445 1.1.1.2 joerg const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 446 1.1 joerg if (!CE) 447 1.1.1.2 joerg return; 448 1.1 joerg 449 1.1.1.2 joerg DefinedSVal RetVal = makeRetVal(C, CE); 450 1.1.1.2 joerg SymbolRef RetSym = RetVal.getAsSymbol(); 451 1.1.1.2 joerg assert(RetSym && "RetVal must be a symbol here."); 452 1.1.1.2 joerg 453 1.1.1.2 joerg State = State->BindExpr(CE, C.getLocationContext(), RetVal); 454 1.1 joerg 455 1.1 joerg // Bifurcate the state into two: one with a valid FILE* pointer, the other 456 1.1 joerg // with a NULL. 457 1.1.1.2 joerg ProgramStateRef StateNotNull, StateNull; 458 1.1.1.2 joerg std::tie(StateNotNull, StateNull) = 459 1.1.1.2 joerg C.getConstraintManager().assumeDual(State, RetVal); 460 1.1.1.2 joerg 461 1.1.1.2 joerg StateNotNull = 462 1.1.1.2 joerg StateNotNull->set<StreamMap>(RetSym, StreamState::getOpened(Desc)); 463 1.1.1.2 joerg StateNull = 464 1.1.1.2 joerg StateNull->set<StreamMap>(RetSym, StreamState::getOpenFailed(Desc)); 465 1.1.1.2 joerg 466 1.1.1.2 joerg C.addTransition(StateNotNull, 467 1.1.1.2 joerg constructNoteTag(C, RetSym, "Stream opened here")); 468 1.1.1.2 joerg C.addTransition(StateNull); 469 1.1.1.2 joerg } 470 1.1.1.2 joerg 471 1.1.1.2 joerg void StreamChecker::preFreopen(const FnDescription *Desc, const CallEvent &Call, 472 1.1.1.2 joerg CheckerContext &C) const { 473 1.1.1.2 joerg // Do not allow NULL as passed stream pointer but allow a closed stream. 474 1.1.1.2 joerg ProgramStateRef State = C.getState(); 475 1.1.1.2 joerg State = ensureStreamNonNull(getStreamArg(Desc, Call), C, State); 476 1.1.1.2 joerg if (!State) 477 1.1.1.2 joerg return; 478 1.1 joerg 479 1.1.1.2 joerg C.addTransition(State); 480 1.1 joerg } 481 1.1 joerg 482 1.1.1.2 joerg void StreamChecker::evalFreopen(const FnDescription *Desc, 483 1.1.1.2 joerg const CallEvent &Call, 484 1.1.1.2 joerg CheckerContext &C) const { 485 1.1.1.2 joerg ProgramStateRef State = C.getState(); 486 1.1.1.2 joerg 487 1.1.1.2 joerg auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 488 1.1.1.2 joerg if (!CE) 489 1.1.1.2 joerg return; 490 1.1.1.2 joerg 491 1.1.1.2 joerg Optional<DefinedSVal> StreamVal = 492 1.1.1.2 joerg getStreamArg(Desc, Call).getAs<DefinedSVal>(); 493 1.1.1.2 joerg if (!StreamVal) 494 1.1.1.2 joerg return; 495 1.1.1.2 joerg 496 1.1.1.2 joerg SymbolRef StreamSym = StreamVal->getAsSymbol(); 497 1.1.1.2 joerg // Do not care about concrete values for stream ("(FILE *)0x12345"?). 498 1.1.1.2 joerg // FIXME: Can be stdin, stdout, stderr such values? 499 1.1.1.2 joerg if (!StreamSym) 500 1.1.1.2 joerg return; 501 1.1.1.2 joerg 502 1.1.1.2 joerg // Do not handle untracked stream. It is probably escaped. 503 1.1.1.2 joerg if (!State->get<StreamMap>(StreamSym)) 504 1.1.1.2 joerg return; 505 1.1.1.2 joerg 506 1.1.1.2 joerg // Generate state for non-failed case. 507 1.1.1.2 joerg // Return value is the passed stream pointer. 508 1.1.1.2 joerg // According to the documentations, the stream is closed first 509 1.1.1.2 joerg // but any close error is ignored. The state changes to (or remains) opened. 510 1.1.1.2 joerg ProgramStateRef StateRetNotNull = 511 1.1.1.2 joerg State->BindExpr(CE, C.getLocationContext(), *StreamVal); 512 1.1.1.2 joerg // Generate state for NULL return value. 513 1.1.1.2 joerg // Stream switches to OpenFailed state. 514 1.1.1.2 joerg ProgramStateRef StateRetNull = State->BindExpr(CE, C.getLocationContext(), 515 1.1.1.2 joerg C.getSValBuilder().makeNull()); 516 1.1.1.2 joerg 517 1.1.1.2 joerg StateRetNotNull = 518 1.1.1.2 joerg StateRetNotNull->set<StreamMap>(StreamSym, StreamState::getOpened(Desc)); 519 1.1.1.2 joerg StateRetNull = 520 1.1.1.2 joerg StateRetNull->set<StreamMap>(StreamSym, StreamState::getOpenFailed(Desc)); 521 1.1.1.2 joerg 522 1.1.1.2 joerg C.addTransition(StateRetNotNull, 523 1.1.1.2 joerg constructNoteTag(C, StreamSym, "Stream reopened here")); 524 1.1.1.2 joerg C.addTransition(StateRetNull); 525 1.1.1.2 joerg } 526 1.1.1.2 joerg 527 1.1.1.2 joerg void StreamChecker::evalFclose(const FnDescription *Desc, const CallEvent &Call, 528 1.1.1.2 joerg CheckerContext &C) const { 529 1.1.1.2 joerg ProgramStateRef State = C.getState(); 530 1.1.1.2 joerg SymbolRef Sym = getStreamArg(Desc, Call).getAsSymbol(); 531 1.1.1.2 joerg if (!Sym) 532 1.1.1.2 joerg return; 533 1.1.1.2 joerg 534 1.1.1.2 joerg const StreamState *SS = State->get<StreamMap>(Sym); 535 1.1.1.2 joerg if (!SS) 536 1.1.1.2 joerg return; 537 1.1.1.2 joerg 538 1.1.1.2 joerg assertStreamStateOpened(SS); 539 1.1.1.2 joerg 540 1.1.1.2 joerg // Close the File Descriptor. 541 1.1.1.2 joerg // Regardless if the close fails or not, stream becomes "closed" 542 1.1.1.2 joerg // and can not be used any more. 543 1.1.1.2 joerg State = State->set<StreamMap>(Sym, StreamState::getClosed(Desc)); 544 1.1.1.2 joerg 545 1.1.1.2 joerg C.addTransition(State); 546 1.1 joerg } 547 1.1 joerg 548 1.1.1.2 joerg void StreamChecker::preFread(const FnDescription *Desc, const CallEvent &Call, 549 1.1.1.2 joerg CheckerContext &C) const { 550 1.1.1.2 joerg ProgramStateRef State = C.getState(); 551 1.1.1.2 joerg SVal StreamVal = getStreamArg(Desc, Call); 552 1.1.1.2 joerg State = ensureStreamNonNull(StreamVal, C, State); 553 1.1.1.2 joerg if (!State) 554 1.1 joerg return; 555 1.1.1.2 joerg State = ensureStreamOpened(StreamVal, C, State); 556 1.1.1.2 joerg if (!State) 557 1.1.1.2 joerg return; 558 1.1.1.2 joerg State = ensureNoFilePositionIndeterminate(StreamVal, C, State); 559 1.1.1.2 joerg if (!State) 560 1.1.1.2 joerg return; 561 1.1.1.2 joerg 562 1.1.1.2 joerg SymbolRef Sym = StreamVal.getAsSymbol(); 563 1.1.1.2 joerg if (Sym && State->get<StreamMap>(Sym)) { 564 1.1.1.2 joerg const StreamState *SS = State->get<StreamMap>(Sym); 565 1.1.1.2 joerg if (SS->ErrorState & ErrorFEof) 566 1.1.1.2 joerg reportFEofWarning(C, State); 567 1.1.1.2 joerg } else { 568 1.1.1.2 joerg C.addTransition(State); 569 1.1.1.2 joerg } 570 1.1 joerg } 571 1.1 joerg 572 1.1.1.2 joerg void StreamChecker::preFwrite(const FnDescription *Desc, const CallEvent &Call, 573 1.1.1.2 joerg CheckerContext &C) const { 574 1.1.1.2 joerg ProgramStateRef State = C.getState(); 575 1.1.1.2 joerg SVal StreamVal = getStreamArg(Desc, Call); 576 1.1.1.2 joerg State = ensureStreamNonNull(StreamVal, C, State); 577 1.1.1.2 joerg if (!State) 578 1.1.1.2 joerg return; 579 1.1.1.2 joerg State = ensureStreamOpened(StreamVal, C, State); 580 1.1.1.2 joerg if (!State) 581 1.1.1.2 joerg return; 582 1.1.1.2 joerg State = ensureNoFilePositionIndeterminate(StreamVal, C, State); 583 1.1.1.2 joerg if (!State) 584 1.1 joerg return; 585 1.1.1.2 joerg 586 1.1.1.2 joerg C.addTransition(State); 587 1.1 joerg } 588 1.1 joerg 589 1.1.1.2 joerg void StreamChecker::evalFreadFwrite(const FnDescription *Desc, 590 1.1.1.2 joerg const CallEvent &Call, CheckerContext &C, 591 1.1.1.2 joerg bool IsFread) const { 592 1.1.1.2 joerg ProgramStateRef State = C.getState(); 593 1.1.1.2 joerg SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); 594 1.1.1.2 joerg if (!StreamSym) 595 1.1 joerg return; 596 1.1 joerg 597 1.1.1.2 joerg const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 598 1.1.1.2 joerg if (!CE) 599 1.1 joerg return; 600 1.1 joerg 601 1.1.1.2 joerg Optional<NonLoc> SizeVal = Call.getArgSVal(1).getAs<NonLoc>(); 602 1.1.1.2 joerg if (!SizeVal) 603 1.1.1.2 joerg return; 604 1.1.1.2 joerg Optional<NonLoc> NMembVal = Call.getArgSVal(2).getAs<NonLoc>(); 605 1.1.1.2 joerg if (!NMembVal) 606 1.1 joerg return; 607 1.1 joerg 608 1.1.1.2 joerg const StreamState *SS = State->get<StreamMap>(StreamSym); 609 1.1.1.2 joerg if (!SS) 610 1.1.1.2 joerg return; 611 1.1.1.2 joerg 612 1.1.1.2 joerg assertStreamStateOpened(SS); 613 1.1.1.2 joerg 614 1.1.1.2 joerg // C'99 standard, 7.19.8.1.3, the return value of fread: 615 1.1.1.2 joerg // The fread function returns the number of elements successfully read, which 616 1.1.1.2 joerg // may be less than nmemb if a read error or end-of-file is encountered. If 617 1.1.1.2 joerg // size or nmemb is zero, fread returns zero and the contents of the array and 618 1.1.1.2 joerg // the state of the stream remain unchanged. 619 1.1.1.2 joerg 620 1.1.1.2 joerg if (State->isNull(*SizeVal).isConstrainedTrue() || 621 1.1.1.2 joerg State->isNull(*NMembVal).isConstrainedTrue()) { 622 1.1.1.2 joerg // This is the "size or nmemb is zero" case. 623 1.1.1.2 joerg // Just return 0, do nothing more (not clear the error flags). 624 1.1.1.2 joerg State = bindInt(0, State, C, CE); 625 1.1.1.2 joerg C.addTransition(State); 626 1.1.1.2 joerg return; 627 1.1 joerg } 628 1.1 joerg 629 1.1.1.2 joerg // Generate a transition for the success state. 630 1.1.1.2 joerg // If we know the state to be FEOF at fread, do not add a success state. 631 1.1.1.2 joerg if (!IsFread || (SS->ErrorState != ErrorFEof)) { 632 1.1.1.2 joerg ProgramStateRef StateNotFailed = 633 1.1.1.2 joerg State->BindExpr(CE, C.getLocationContext(), *NMembVal); 634 1.1.1.2 joerg if (StateNotFailed) { 635 1.1.1.2 joerg StateNotFailed = StateNotFailed->set<StreamMap>( 636 1.1.1.2 joerg StreamSym, StreamState::getOpened(Desc)); 637 1.1.1.2 joerg C.addTransition(StateNotFailed); 638 1.1.1.2 joerg } 639 1.1.1.2 joerg } 640 1.1.1.2 joerg 641 1.1.1.2 joerg // Add transition for the failed state. 642 1.1.1.2 joerg Optional<NonLoc> RetVal = makeRetVal(C, CE).castAs<NonLoc>(); 643 1.1.1.2 joerg assert(RetVal && "Value should be NonLoc."); 644 1.1.1.2 joerg ProgramStateRef StateFailed = 645 1.1.1.2 joerg State->BindExpr(CE, C.getLocationContext(), *RetVal); 646 1.1.1.2 joerg if (!StateFailed) 647 1.1.1.2 joerg return; 648 1.1.1.2 joerg auto Cond = C.getSValBuilder() 649 1.1.1.2 joerg .evalBinOpNN(State, BO_LT, *RetVal, *NMembVal, 650 1.1.1.2 joerg C.getASTContext().IntTy) 651 1.1.1.2 joerg .getAs<DefinedOrUnknownSVal>(); 652 1.1.1.2 joerg if (!Cond) 653 1.1.1.2 joerg return; 654 1.1.1.2 joerg StateFailed = StateFailed->assume(*Cond, true); 655 1.1.1.2 joerg if (!StateFailed) 656 1.1 joerg return; 657 1.1 joerg 658 1.1.1.2 joerg StreamErrorState NewES; 659 1.1.1.2 joerg if (IsFread) 660 1.1.1.2 joerg NewES = (SS->ErrorState == ErrorFEof) ? ErrorFEof : ErrorFEof | ErrorFError; 661 1.1.1.2 joerg else 662 1.1.1.2 joerg NewES = ErrorFError; 663 1.1.1.2 joerg // If a (non-EOF) error occurs, the resulting value of the file position 664 1.1.1.2 joerg // indicator for the stream is indeterminate. 665 1.1.1.2 joerg StreamState NewState = StreamState::getOpened(Desc, NewES, !NewES.isFEof()); 666 1.1.1.2 joerg StateFailed = StateFailed->set<StreamMap>(StreamSym, NewState); 667 1.1.1.2 joerg C.addTransition(StateFailed); 668 1.1.1.2 joerg } 669 1.1.1.2 joerg 670 1.1.1.2 joerg void StreamChecker::preFseek(const FnDescription *Desc, const CallEvent &Call, 671 1.1.1.2 joerg CheckerContext &C) const { 672 1.1.1.2 joerg ProgramStateRef State = C.getState(); 673 1.1.1.2 joerg SVal StreamVal = getStreamArg(Desc, Call); 674 1.1.1.2 joerg State = ensureStreamNonNull(StreamVal, C, State); 675 1.1.1.2 joerg if (!State) 676 1.1.1.2 joerg return; 677 1.1.1.2 joerg State = ensureStreamOpened(StreamVal, C, State); 678 1.1.1.2 joerg if (!State) 679 1.1.1.2 joerg return; 680 1.1.1.2 joerg State = ensureFseekWhenceCorrect(Call.getArgSVal(2), C, State); 681 1.1.1.2 joerg if (!State) 682 1.1 joerg return; 683 1.1.1.2 joerg 684 1.1.1.2 joerg C.addTransition(State); 685 1.1 joerg } 686 1.1 joerg 687 1.1.1.2 joerg void StreamChecker::evalFseek(const FnDescription *Desc, const CallEvent &Call, 688 1.1.1.2 joerg CheckerContext &C) const { 689 1.1.1.2 joerg ProgramStateRef State = C.getState(); 690 1.1.1.2 joerg SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); 691 1.1.1.2 joerg if (!StreamSym) 692 1.1 joerg return; 693 1.1 joerg 694 1.1.1.2 joerg const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 695 1.1.1.2 joerg if (!CE) 696 1.1 joerg return; 697 1.1 joerg 698 1.1.1.2 joerg // Ignore the call if the stream is not tracked. 699 1.1.1.2 joerg if (!State->get<StreamMap>(StreamSym)) 700 1.1 joerg return; 701 1.1 joerg 702 1.1.1.2 joerg DefinedSVal RetVal = makeRetVal(C, CE); 703 1.1.1.2 joerg 704 1.1.1.2 joerg // Make expression result. 705 1.1.1.2 joerg State = State->BindExpr(CE, C.getLocationContext(), RetVal); 706 1.1.1.2 joerg 707 1.1.1.2 joerg // Bifurcate the state into failed and non-failed. 708 1.1.1.2 joerg // Return zero on success, nonzero on error. 709 1.1.1.2 joerg ProgramStateRef StateNotFailed, StateFailed; 710 1.1.1.2 joerg std::tie(StateFailed, StateNotFailed) = 711 1.1.1.2 joerg C.getConstraintManager().assumeDual(State, RetVal); 712 1.1.1.2 joerg 713 1.1.1.2 joerg // Reset the state to opened with no error. 714 1.1.1.2 joerg StateNotFailed = 715 1.1.1.2 joerg StateNotFailed->set<StreamMap>(StreamSym, StreamState::getOpened(Desc)); 716 1.1.1.2 joerg // We get error. 717 1.1.1.2 joerg // It is possible that fseek fails but sets none of the error flags. 718 1.1.1.2 joerg // If fseek failed, assume that the file position becomes indeterminate in any 719 1.1.1.2 joerg // case. 720 1.1.1.2 joerg StateFailed = StateFailed->set<StreamMap>( 721 1.1.1.2 joerg StreamSym, 722 1.1.1.2 joerg StreamState::getOpened(Desc, ErrorNone | ErrorFEof | ErrorFError, true)); 723 1.1.1.2 joerg 724 1.1.1.2 joerg C.addTransition(StateNotFailed); 725 1.1.1.2 joerg C.addTransition(StateFailed); 726 1.1.1.2 joerg } 727 1.1.1.2 joerg 728 1.1.1.2 joerg void StreamChecker::evalClearerr(const FnDescription *Desc, 729 1.1.1.2 joerg const CallEvent &Call, 730 1.1.1.2 joerg CheckerContext &C) const { 731 1.1.1.2 joerg ProgramStateRef State = C.getState(); 732 1.1.1.2 joerg SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); 733 1.1.1.2 joerg if (!StreamSym) 734 1.1.1.2 joerg return; 735 1.1.1.2 joerg 736 1.1.1.2 joerg const StreamState *SS = State->get<StreamMap>(StreamSym); 737 1.1.1.2 joerg if (!SS) 738 1.1 joerg return; 739 1.1.1.2 joerg 740 1.1.1.2 joerg assertStreamStateOpened(SS); 741 1.1.1.2 joerg 742 1.1.1.2 joerg // FilePositionIndeterminate is not cleared. 743 1.1.1.2 joerg State = State->set<StreamMap>( 744 1.1.1.2 joerg StreamSym, 745 1.1.1.2 joerg StreamState::getOpened(Desc, ErrorNone, SS->FilePositionIndeterminate)); 746 1.1.1.2 joerg C.addTransition(State); 747 1.1 joerg } 748 1.1 joerg 749 1.1.1.2 joerg void StreamChecker::evalFeofFerror(const FnDescription *Desc, 750 1.1.1.2 joerg const CallEvent &Call, CheckerContext &C, 751 1.1.1.2 joerg const StreamErrorState &ErrorKind) const { 752 1.1.1.2 joerg ProgramStateRef State = C.getState(); 753 1.1.1.2 joerg SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); 754 1.1.1.2 joerg if (!StreamSym) 755 1.1.1.2 joerg return; 756 1.1.1.2 joerg 757 1.1.1.2 joerg const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 758 1.1.1.2 joerg if (!CE) 759 1.1.1.2 joerg return; 760 1.1.1.2 joerg 761 1.1.1.2 joerg const StreamState *SS = State->get<StreamMap>(StreamSym); 762 1.1.1.2 joerg if (!SS) 763 1.1 joerg return; 764 1.1.1.2 joerg 765 1.1.1.2 joerg assertStreamStateOpened(SS); 766 1.1.1.2 joerg 767 1.1.1.2 joerg if (SS->ErrorState & ErrorKind) { 768 1.1.1.2 joerg // Execution path with error of ErrorKind. 769 1.1.1.2 joerg // Function returns true. 770 1.1.1.2 joerg // From now on it is the only one error state. 771 1.1.1.2 joerg ProgramStateRef TrueState = bindAndAssumeTrue(State, C, CE); 772 1.1.1.2 joerg C.addTransition(TrueState->set<StreamMap>( 773 1.1.1.2 joerg StreamSym, StreamState::getOpened(Desc, ErrorKind, 774 1.1.1.2 joerg SS->FilePositionIndeterminate && 775 1.1.1.2 joerg !ErrorKind.isFEof()))); 776 1.1.1.2 joerg } 777 1.1.1.2 joerg if (StreamErrorState NewES = SS->ErrorState & (~ErrorKind)) { 778 1.1.1.2 joerg // Execution path(s) with ErrorKind not set. 779 1.1.1.2 joerg // Function returns false. 780 1.1.1.2 joerg // New error state is everything before minus ErrorKind. 781 1.1.1.2 joerg ProgramStateRef FalseState = bindInt(0, State, C, CE); 782 1.1.1.2 joerg C.addTransition(FalseState->set<StreamMap>( 783 1.1.1.2 joerg StreamSym, 784 1.1.1.2 joerg StreamState::getOpened( 785 1.1.1.2 joerg Desc, NewES, SS->FilePositionIndeterminate && !NewES.isFEof()))); 786 1.1.1.2 joerg } 787 1.1 joerg } 788 1.1 joerg 789 1.1.1.2 joerg void StreamChecker::preDefault(const FnDescription *Desc, const CallEvent &Call, 790 1.1.1.2 joerg CheckerContext &C) const { 791 1.1.1.2 joerg ProgramStateRef State = C.getState(); 792 1.1.1.2 joerg SVal StreamVal = getStreamArg(Desc, Call); 793 1.1.1.2 joerg State = ensureStreamNonNull(StreamVal, C, State); 794 1.1.1.2 joerg if (!State) 795 1.1.1.2 joerg return; 796 1.1.1.2 joerg State = ensureStreamOpened(StreamVal, C, State); 797 1.1.1.2 joerg if (!State) 798 1.1 joerg return; 799 1.1.1.2 joerg 800 1.1.1.2 joerg C.addTransition(State); 801 1.1 joerg } 802 1.1 joerg 803 1.1.1.2 joerg void StreamChecker::evalSetFeofFerror(const FnDescription *Desc, 804 1.1.1.2 joerg const CallEvent &Call, CheckerContext &C, 805 1.1.1.2 joerg const StreamErrorState &ErrorKind) const { 806 1.1.1.2 joerg ProgramStateRef State = C.getState(); 807 1.1.1.2 joerg SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); 808 1.1.1.2 joerg assert(StreamSym && "Operation not permitted on non-symbolic stream value."); 809 1.1.1.2 joerg const StreamState *SS = State->get<StreamMap>(StreamSym); 810 1.1.1.2 joerg assert(SS && "Stream should be tracked by the checker."); 811 1.1.1.2 joerg State = State->set<StreamMap>( 812 1.1.1.2 joerg StreamSym, StreamState::getOpened(SS->LastOperation, ErrorKind)); 813 1.1.1.2 joerg C.addTransition(State); 814 1.1.1.2 joerg } 815 1.1.1.2 joerg 816 1.1.1.2 joerg ProgramStateRef 817 1.1.1.2 joerg StreamChecker::ensureStreamNonNull(SVal StreamVal, CheckerContext &C, 818 1.1.1.2 joerg ProgramStateRef State) const { 819 1.1.1.2 joerg auto Stream = StreamVal.getAs<DefinedSVal>(); 820 1.1.1.2 joerg if (!Stream) 821 1.1.1.2 joerg return State; 822 1.1 joerg 823 1.1 joerg ConstraintManager &CM = C.getConstraintManager(); 824 1.1 joerg 825 1.1.1.2 joerg ProgramStateRef StateNotNull, StateNull; 826 1.1.1.2 joerg std::tie(StateNotNull, StateNull) = CM.assumeDual(C.getState(), *Stream); 827 1.1.1.2 joerg 828 1.1.1.2 joerg if (!StateNotNull && StateNull) { 829 1.1.1.2 joerg if (ExplodedNode *N = C.generateErrorNode(StateNull)) { 830 1.1 joerg C.emitReport(std::make_unique<PathSensitiveBugReport>( 831 1.1.1.2 joerg BT_FileNull, "Stream pointer might be NULL.", N)); 832 1.1 joerg } 833 1.1 joerg return nullptr; 834 1.1 joerg } 835 1.1.1.2 joerg 836 1.1.1.2 joerg return StateNotNull; 837 1.1 joerg } 838 1.1 joerg 839 1.1.1.2 joerg ProgramStateRef StreamChecker::ensureStreamOpened(SVal StreamVal, 840 1.1.1.2 joerg CheckerContext &C, 841 1.1.1.2 joerg ProgramStateRef State) const { 842 1.1.1.2 joerg SymbolRef Sym = StreamVal.getAsSymbol(); 843 1.1 joerg if (!Sym) 844 1.1.1.2 joerg return State; 845 1.1 joerg 846 1.1.1.2 joerg const StreamState *SS = State->get<StreamMap>(Sym); 847 1.1 joerg if (!SS) 848 1.1.1.2 joerg return State; 849 1.1 joerg 850 1.1 joerg if (SS->isClosed()) { 851 1.1.1.2 joerg // Using a stream pointer after 'fclose' causes undefined behavior 852 1.1.1.2 joerg // according to cppreference.com . 853 1.1 joerg ExplodedNode *N = C.generateErrorNode(); 854 1.1 joerg if (N) { 855 1.1 joerg C.emitReport(std::make_unique<PathSensitiveBugReport>( 856 1.1.1.2 joerg BT_UseAfterClose, 857 1.1.1.2 joerg "Stream might be already closed. Causes undefined behaviour.", N)); 858 1.1.1.2 joerg return nullptr; 859 1.1 joerg } 860 1.1.1.2 joerg 861 1.1.1.2 joerg return State; 862 1.1.1.2 joerg } 863 1.1.1.2 joerg 864 1.1.1.2 joerg if (SS->isOpenFailed()) { 865 1.1.1.2 joerg // Using a stream that has failed to open is likely to cause problems. 866 1.1.1.2 joerg // This should usually not occur because stream pointer is NULL. 867 1.1.1.2 joerg // But freopen can cause a state when stream pointer remains non-null but 868 1.1.1.2 joerg // failed to open. 869 1.1.1.2 joerg ExplodedNode *N = C.generateErrorNode(); 870 1.1.1.2 joerg if (N) { 871 1.1.1.2 joerg C.emitReport(std::make_unique<PathSensitiveBugReport>( 872 1.1.1.2 joerg BT_UseAfterOpenFailed, 873 1.1.1.2 joerg "Stream might be invalid after " 874 1.1.1.2 joerg "(re-)opening it has failed. " 875 1.1.1.2 joerg "Can cause undefined behaviour.", 876 1.1.1.2 joerg N)); 877 1.1.1.2 joerg return nullptr; 878 1.1.1.2 joerg } 879 1.1.1.2 joerg return State; 880 1.1.1.2 joerg } 881 1.1.1.2 joerg 882 1.1.1.2 joerg return State; 883 1.1.1.2 joerg } 884 1.1.1.2 joerg 885 1.1.1.2 joerg ProgramStateRef StreamChecker::ensureNoFilePositionIndeterminate( 886 1.1.1.2 joerg SVal StreamVal, CheckerContext &C, ProgramStateRef State) const { 887 1.1.1.2 joerg static const char *BugMessage = 888 1.1.1.2 joerg "File position of the stream might be 'indeterminate' " 889 1.1.1.2 joerg "after a failed operation. " 890 1.1.1.2 joerg "Can cause undefined behavior."; 891 1.1.1.2 joerg 892 1.1.1.2 joerg SymbolRef Sym = StreamVal.getAsSymbol(); 893 1.1.1.2 joerg if (!Sym) 894 1.1.1.2 joerg return State; 895 1.1.1.2 joerg 896 1.1.1.2 joerg const StreamState *SS = State->get<StreamMap>(Sym); 897 1.1.1.2 joerg if (!SS) 898 1.1.1.2 joerg return State; 899 1.1.1.2 joerg 900 1.1.1.2 joerg assert(SS->isOpened() && "First ensure that stream is opened."); 901 1.1.1.2 joerg 902 1.1.1.2 joerg if (SS->FilePositionIndeterminate) { 903 1.1.1.2 joerg if (SS->ErrorState & ErrorFEof) { 904 1.1.1.2 joerg // The error is unknown but may be FEOF. 905 1.1.1.2 joerg // Continue analysis with the FEOF error state. 906 1.1.1.2 joerg // Report warning because the other possible error states. 907 1.1.1.2 joerg ExplodedNode *N = C.generateNonFatalErrorNode(State); 908 1.1.1.2 joerg if (!N) 909 1.1.1.2 joerg return nullptr; 910 1.1.1.2 joerg 911 1.1.1.2 joerg C.emitReport(std::make_unique<PathSensitiveBugReport>( 912 1.1.1.2 joerg BT_IndeterminatePosition, BugMessage, N)); 913 1.1.1.2 joerg return State->set<StreamMap>( 914 1.1.1.2 joerg Sym, StreamState::getOpened(SS->LastOperation, ErrorFEof, false)); 915 1.1.1.2 joerg } 916 1.1.1.2 joerg 917 1.1.1.2 joerg // Known or unknown error state without FEOF possible. 918 1.1.1.2 joerg // Stop analysis, report error. 919 1.1.1.2 joerg ExplodedNode *N = C.generateErrorNode(State); 920 1.1.1.2 joerg if (N) 921 1.1.1.2 joerg C.emitReport(std::make_unique<PathSensitiveBugReport>( 922 1.1.1.2 joerg BT_IndeterminatePosition, BugMessage, N)); 923 1.1.1.2 joerg 924 1.1 joerg return nullptr; 925 1.1 joerg } 926 1.1 joerg 927 1.1.1.2 joerg return State; 928 1.1.1.2 joerg } 929 1.1.1.2 joerg 930 1.1.1.2 joerg ProgramStateRef 931 1.1.1.2 joerg StreamChecker::ensureFseekWhenceCorrect(SVal WhenceVal, CheckerContext &C, 932 1.1.1.2 joerg ProgramStateRef State) const { 933 1.1.1.2 joerg Optional<nonloc::ConcreteInt> CI = WhenceVal.getAs<nonloc::ConcreteInt>(); 934 1.1.1.2 joerg if (!CI) 935 1.1.1.2 joerg return State; 936 1.1.1.2 joerg 937 1.1.1.2 joerg int64_t X = CI->getValue().getSExtValue(); 938 1.1.1.2 joerg if (X >= 0 && X <= 2) 939 1.1.1.2 joerg return State; 940 1.1.1.2 joerg 941 1.1.1.2 joerg if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) { 942 1.1.1.2 joerg C.emitReport(std::make_unique<PathSensitiveBugReport>( 943 1.1.1.2 joerg BT_IllegalWhence, 944 1.1.1.2 joerg "The whence argument to fseek() should be " 945 1.1.1.2 joerg "SEEK_SET, SEEK_END, or SEEK_CUR.", 946 1.1.1.2 joerg N)); 947 1.1.1.2 joerg return nullptr; 948 1.1.1.2 joerg } 949 1.1.1.2 joerg 950 1.1.1.2 joerg return State; 951 1.1.1.2 joerg } 952 1.1.1.2 joerg 953 1.1.1.2 joerg void StreamChecker::reportFEofWarning(CheckerContext &C, 954 1.1.1.2 joerg ProgramStateRef State) const { 955 1.1.1.2 joerg if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) { 956 1.1.1.2 joerg C.emitReport(std::make_unique<PathSensitiveBugReport>( 957 1.1.1.2 joerg BT_StreamEof, 958 1.1.1.2 joerg "Read function called when stream is in EOF state. " 959 1.1.1.2 joerg "Function has no effect.", 960 1.1.1.2 joerg N)); 961 1.1.1.2 joerg return; 962 1.1.1.2 joerg } 963 1.1.1.2 joerg C.addTransition(State); 964 1.1.1.2 joerg } 965 1.1.1.2 joerg 966 1.1.1.2 joerg ExplodedNode * 967 1.1.1.2 joerg StreamChecker::reportLeaks(const SmallVector<SymbolRef, 2> &LeakedSyms, 968 1.1.1.2 joerg CheckerContext &C, ExplodedNode *Pred) const { 969 1.1.1.2 joerg ExplodedNode *Err = C.generateNonFatalErrorNode(C.getState(), Pred); 970 1.1.1.2 joerg if (!Err) 971 1.1.1.2 joerg return Pred; 972 1.1.1.2 joerg 973 1.1.1.2 joerg for (SymbolRef LeakSym : LeakedSyms) { 974 1.1.1.2 joerg // Resource leaks can result in multiple warning that describe the same kind 975 1.1.1.2 joerg // of programming error: 976 1.1.1.2 joerg // void f() { 977 1.1.1.2 joerg // FILE *F = fopen("a.txt"); 978 1.1.1.2 joerg // if (rand()) // state split 979 1.1.1.2 joerg // return; // warning 980 1.1.1.2 joerg // } // warning 981 1.1.1.2 joerg // While this isn't necessarily true (leaking the same stream could result 982 1.1.1.2 joerg // from a different kinds of errors), the reduction in redundant reports 983 1.1.1.2 joerg // makes this a worthwhile heuristic. 984 1.1.1.2 joerg // FIXME: Add a checker option to turn this uniqueing feature off. 985 1.1.1.2 joerg const ExplodedNode *StreamOpenNode = getAcquisitionSite(Err, LeakSym, C); 986 1.1.1.2 joerg assert(StreamOpenNode && "Could not find place of stream opening."); 987 1.1.1.2 joerg PathDiagnosticLocation LocUsedForUniqueing = 988 1.1.1.2 joerg PathDiagnosticLocation::createBegin( 989 1.1.1.2 joerg StreamOpenNode->getStmtForDiagnostics(), C.getSourceManager(), 990 1.1.1.2 joerg StreamOpenNode->getLocationContext()); 991 1.1.1.2 joerg 992 1.1.1.2 joerg std::unique_ptr<PathSensitiveBugReport> R = 993 1.1.1.2 joerg std::make_unique<PathSensitiveBugReport>( 994 1.1.1.2 joerg BT_ResourceLeak, 995 1.1.1.2 joerg "Opened stream never closed. Potential resource leak.", Err, 996 1.1.1.2 joerg LocUsedForUniqueing, 997 1.1.1.2 joerg StreamOpenNode->getLocationContext()->getDecl()); 998 1.1.1.2 joerg R->markInteresting(LeakSym); 999 1.1.1.2 joerg C.emitReport(std::move(R)); 1000 1.1.1.2 joerg } 1001 1.1.1.2 joerg 1002 1.1.1.2 joerg return Err; 1003 1.1 joerg } 1004 1.1 joerg 1005 1.1 joerg void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper, 1006 1.1 joerg CheckerContext &C) const { 1007 1.1.1.2 joerg ProgramStateRef State = C.getState(); 1008 1.1 joerg 1009 1.1.1.2 joerg llvm::SmallVector<SymbolRef, 2> LeakedSyms; 1010 1.1.1.2 joerg 1011 1.1.1.2 joerg const StreamMapTy &Map = State->get<StreamMap>(); 1012 1.1.1.2 joerg for (const auto &I : Map) { 1013 1.1 joerg SymbolRef Sym = I.first; 1014 1.1 joerg const StreamState &SS = I.second; 1015 1.1.1.2 joerg if (!SymReaper.isDead(Sym)) 1016 1.1 joerg continue; 1017 1.1.1.2 joerg if (SS.isOpened()) 1018 1.1.1.2 joerg LeakedSyms.push_back(Sym); 1019 1.1.1.2 joerg State = State->remove<StreamMap>(Sym); 1020 1.1.1.2 joerg } 1021 1.1 joerg 1022 1.1.1.2 joerg ExplodedNode *N = C.getPredecessor(); 1023 1.1.1.2 joerg if (!LeakedSyms.empty()) 1024 1.1.1.2 joerg N = reportLeaks(LeakedSyms, C, N); 1025 1.1 joerg 1026 1.1.1.2 joerg C.addTransition(State, N); 1027 1.1.1.2 joerg } 1028 1.1.1.2 joerg 1029 1.1.1.2 joerg ProgramStateRef StreamChecker::checkPointerEscape( 1030 1.1.1.2 joerg ProgramStateRef State, const InvalidatedSymbols &Escaped, 1031 1.1.1.2 joerg const CallEvent *Call, PointerEscapeKind Kind) const { 1032 1.1.1.2 joerg // Check for file-handling system call that is not handled by the checker. 1033 1.1.1.2 joerg // FIXME: The checker should be updated to handle all system calls that take 1034 1.1.1.2 joerg // 'FILE*' argument. These are now ignored. 1035 1.1.1.2 joerg if (Kind == PSK_DirectEscapeOnCall && Call->isInSystemHeader()) 1036 1.1.1.2 joerg return State; 1037 1.1.1.2 joerg 1038 1.1.1.2 joerg for (SymbolRef Sym : Escaped) { 1039 1.1.1.2 joerg // The symbol escaped. 1040 1.1.1.2 joerg // From now the stream can be manipulated in unknown way to the checker, 1041 1.1.1.2 joerg // it is not possible to handle it any more. 1042 1.1.1.2 joerg // Optimistically, assume that the corresponding file handle will be closed 1043 1.1.1.2 joerg // somewhere else. 1044 1.1.1.2 joerg // Remove symbol from state so the following stream calls on this symbol are 1045 1.1.1.2 joerg // not handled by the checker. 1046 1.1.1.2 joerg State = State->remove<StreamMap>(Sym); 1047 1.1 joerg } 1048 1.1.1.2 joerg return State; 1049 1.1.1.2 joerg } 1050 1.1.1.2 joerg 1051 1.1.1.2 joerg void ento::registerStreamChecker(CheckerManager &Mgr) { 1052 1.1.1.2 joerg Mgr.registerChecker<StreamChecker>(); 1053 1.1.1.2 joerg } 1054 1.1.1.2 joerg 1055 1.1.1.2 joerg bool ento::shouldRegisterStreamChecker(const CheckerManager &Mgr) { 1056 1.1.1.2 joerg return true; 1057 1.1 joerg } 1058 1.1 joerg 1059 1.1.1.2 joerg void ento::registerStreamTesterChecker(CheckerManager &Mgr) { 1060 1.1.1.2 joerg auto *Checker = Mgr.getChecker<StreamChecker>(); 1061 1.1.1.2 joerg Checker->TestMode = true; 1062 1.1 joerg } 1063 1.1 joerg 1064 1.1.1.2 joerg bool ento::shouldRegisterStreamTesterChecker(const CheckerManager &Mgr) { 1065 1.1 joerg return true; 1066 1.1 joerg } 1067