1 //=== ErrnoTesterChecker.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 ErrnoTesterChecker, which is used to test functionality of the 10 // errno_check API. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #include "ErrnoModeling.h" 15 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 16 #include "clang/StaticAnalyzer/Core/Checker.h" 17 #include "clang/StaticAnalyzer/Core/CheckerManager.h" 18 #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h" 19 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 20 #include <optional> 21 22 using namespace clang; 23 using namespace ento; 24 using namespace errno_modeling; 25 26 namespace { 27 28 class ErrnoTesterChecker : public Checker<eval::Call> { 29 public: 30 bool evalCall(const CallEvent &Call, CheckerContext &C) const; 31 32 private: 33 /// Evaluate function \code void ErrnoTesterChecker_setErrno(int) \endcode. 34 /// Set value of \c errno to the argument. 35 static void evalSetErrno(CheckerContext &C, const CallEvent &Call); 36 /// Evaluate function \code int ErrnoTesterChecker_getErrno() \endcode. 37 /// Return the value of \c errno. 38 static void evalGetErrno(CheckerContext &C, const CallEvent &Call); 39 /// Evaluate function \code int ErrnoTesterChecker_setErrnoIfError() \endcode. 40 /// Simulate a standard library function tha returns 0 on success and 1 on 41 /// failure. On the success case \c errno is not allowed to be used (may be 42 /// undefined). On the failure case \c errno is set to a fixed value 11 and 43 /// is not needed to be checked. 44 static void evalSetErrnoIfError(CheckerContext &C, const CallEvent &Call); 45 /// Evaluate function \code int ErrnoTesterChecker_setErrnoIfErrorRange() 46 /// \endcode. Same as \c ErrnoTesterChecker_setErrnoIfError but \c errno is 47 /// set to a range (to be nonzero) at the failure case. 48 static void evalSetErrnoIfErrorRange(CheckerContext &C, 49 const CallEvent &Call); 50 /// Evaluate function \code int ErrnoTesterChecker_setErrnoCheckState() 51 /// \endcode. This function simulates the following: 52 /// - Return 0 and leave \c errno with undefined value. 53 /// This is the case of a successful standard function call. 54 /// For example if \c ftell returns not -1. 55 /// - Return 1 and sets \c errno to a specific error code (1). 56 /// This is the case of a failed standard function call. 57 /// The function indicates the failure by a special return value 58 /// that is returned only at failure. 59 /// \c errno can be checked but it is not required. 60 /// For example if \c ftell returns -1. 61 /// - Return 2 and may set errno to a value (actually it does not set it). 62 /// This is the case of a standard function call where the failure can only 63 /// be checked by reading from \c errno. The value of \c errno is changed by 64 /// the function only at failure, the user should set \c errno to 0 before 65 /// the call (\c ErrnoChecker does not check for this rule). 66 /// \c strtol is an example of this case, if it returns \c LONG_MIN (or 67 /// \c LONG_MAX). This case applies only if \c LONG_MIN or \c LONG_MAX is 68 /// returned, otherwise the first case in this list applies. 69 static void evalSetErrnoCheckState(CheckerContext &C, const CallEvent &Call); 70 71 using EvalFn = std::function<void(CheckerContext &, const CallEvent &)>; 72 const CallDescriptionMap<EvalFn> TestCalls{ 73 {{CDM::SimpleFunc, {"ErrnoTesterChecker_setErrno"}, 1}, 74 &ErrnoTesterChecker::evalSetErrno}, 75 {{CDM::SimpleFunc, {"ErrnoTesterChecker_getErrno"}, 0}, 76 &ErrnoTesterChecker::evalGetErrno}, 77 {{CDM::SimpleFunc, {"ErrnoTesterChecker_setErrnoIfError"}, 0}, 78 &ErrnoTesterChecker::evalSetErrnoIfError}, 79 {{CDM::SimpleFunc, {"ErrnoTesterChecker_setErrnoIfErrorRange"}, 0}, 80 &ErrnoTesterChecker::evalSetErrnoIfErrorRange}, 81 {{CDM::SimpleFunc, {"ErrnoTesterChecker_setErrnoCheckState"}, 0}, 82 &ErrnoTesterChecker::evalSetErrnoCheckState}}; 83 }; 84 85 } // namespace 86 87 void ErrnoTesterChecker::evalSetErrno(CheckerContext &C, 88 const CallEvent &Call) { 89 C.addTransition(setErrnoValue(C.getState(), C.getLocationContext(), 90 Call.getArgSVal(0), Irrelevant)); 91 } 92 93 void ErrnoTesterChecker::evalGetErrno(CheckerContext &C, 94 const CallEvent &Call) { 95 ProgramStateRef State = C.getState(); 96 97 std::optional<SVal> ErrnoVal = getErrnoValue(State); 98 assert(ErrnoVal && "Errno value should be available."); 99 State = 100 State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), *ErrnoVal); 101 102 C.addTransition(State); 103 } 104 105 void ErrnoTesterChecker::evalSetErrnoIfError(CheckerContext &C, 106 const CallEvent &Call) { 107 ProgramStateRef State = C.getState(); 108 SValBuilder &SVB = C.getSValBuilder(); 109 110 ProgramStateRef StateSuccess = State->BindExpr( 111 Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(0, true)); 112 StateSuccess = setErrnoState(StateSuccess, MustNotBeChecked); 113 114 ProgramStateRef StateFailure = State->BindExpr( 115 Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(1, true)); 116 StateFailure = setErrnoValue(StateFailure, C, 11, Irrelevant); 117 118 C.addTransition(StateSuccess); 119 C.addTransition(StateFailure); 120 } 121 122 void ErrnoTesterChecker::evalSetErrnoIfErrorRange(CheckerContext &C, 123 const CallEvent &Call) { 124 ProgramStateRef State = C.getState(); 125 SValBuilder &SVB = C.getSValBuilder(); 126 127 ProgramStateRef StateSuccess = State->BindExpr( 128 Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(0, true)); 129 StateSuccess = setErrnoState(StateSuccess, MustNotBeChecked); 130 131 ProgramStateRef StateFailure = State->BindExpr( 132 Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(1, true)); 133 DefinedOrUnknownSVal ErrnoVal = SVB.conjureSymbolVal( 134 nullptr, Call.getOriginExpr(), C.getLocationContext(), C.blockCount()); 135 StateFailure = StateFailure->assume(ErrnoVal, true); 136 assert(StateFailure && "Failed to assume on an initial value."); 137 StateFailure = 138 setErrnoValue(StateFailure, C.getLocationContext(), ErrnoVal, Irrelevant); 139 140 C.addTransition(StateSuccess); 141 C.addTransition(StateFailure); 142 } 143 144 void ErrnoTesterChecker::evalSetErrnoCheckState(CheckerContext &C, 145 const CallEvent &Call) { 146 ProgramStateRef State = C.getState(); 147 SValBuilder &SVB = C.getSValBuilder(); 148 149 ProgramStateRef StateSuccess = State->BindExpr( 150 Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(0, true)); 151 StateSuccess = setErrnoState(StateSuccess, MustNotBeChecked); 152 153 ProgramStateRef StateFailure1 = State->BindExpr( 154 Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(1, true)); 155 StateFailure1 = setErrnoValue(StateFailure1, C, 1, Irrelevant); 156 157 ProgramStateRef StateFailure2 = State->BindExpr( 158 Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(2, true)); 159 StateFailure2 = setErrnoValue(StateFailure2, C, 2, MustBeChecked); 160 161 C.addTransition(StateSuccess, 162 getErrnoNoteTag(C, "Assuming that this function succeeds but " 163 "sets 'errno' to an unspecified value.")); 164 C.addTransition(StateFailure1); 165 C.addTransition( 166 StateFailure2, 167 getErrnoNoteTag(C, "Assuming that this function returns 2. 'errno' " 168 "should be checked to test for failure.")); 169 } 170 171 bool ErrnoTesterChecker::evalCall(const CallEvent &Call, 172 CheckerContext &C) const { 173 const EvalFn *Fn = TestCalls.lookup(Call); 174 if (Fn) { 175 (*Fn)(C, Call); 176 return C.isDifferent(); 177 } 178 return false; 179 } 180 181 void ento::registerErrnoTesterChecker(CheckerManager &Mgr) { 182 Mgr.registerChecker<ErrnoTesterChecker>(); 183 } 184 185 bool ento::shouldRegisterErrnoTesterChecker(const CheckerManager &Mgr) { 186 return true; 187 } 188