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 {{{"ErrnoTesterChecker_setErrno"}, 1}, &ErrnoTesterChecker::evalSetErrno}, 74 {{{"ErrnoTesterChecker_getErrno"}, 0}, &ErrnoTesterChecker::evalGetErrno}, 75 {{{"ErrnoTesterChecker_setErrnoIfError"}, 0}, 76 &ErrnoTesterChecker::evalSetErrnoIfError}, 77 {{{"ErrnoTesterChecker_setErrnoIfErrorRange"}, 0}, 78 &ErrnoTesterChecker::evalSetErrnoIfErrorRange}, 79 {{{"ErrnoTesterChecker_setErrnoCheckState"}, 0}, 80 &ErrnoTesterChecker::evalSetErrnoCheckState}}; 81 }; 82 83 } // namespace 84 85 void ErrnoTesterChecker::evalSetErrno(CheckerContext &C, 86 const CallEvent &Call) { 87 C.addTransition(setErrnoValue(C.getState(), C.getLocationContext(), 88 Call.getArgSVal(0), Irrelevant)); 89 } 90 91 void ErrnoTesterChecker::evalGetErrno(CheckerContext &C, 92 const CallEvent &Call) { 93 ProgramStateRef State = C.getState(); 94 95 std::optional<SVal> ErrnoVal = getErrnoValue(State); 96 assert(ErrnoVal && "Errno value should be available."); 97 State = 98 State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), *ErrnoVal); 99 100 C.addTransition(State); 101 } 102 103 void ErrnoTesterChecker::evalSetErrnoIfError(CheckerContext &C, 104 const CallEvent &Call) { 105 ProgramStateRef State = C.getState(); 106 SValBuilder &SVB = C.getSValBuilder(); 107 108 ProgramStateRef StateSuccess = State->BindExpr( 109 Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(0, true)); 110 StateSuccess = setErrnoState(StateSuccess, MustNotBeChecked); 111 112 ProgramStateRef StateFailure = State->BindExpr( 113 Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(1, true)); 114 StateFailure = setErrnoValue(StateFailure, C, 11, Irrelevant); 115 116 C.addTransition(StateSuccess); 117 C.addTransition(StateFailure); 118 } 119 120 void ErrnoTesterChecker::evalSetErrnoIfErrorRange(CheckerContext &C, 121 const CallEvent &Call) { 122 ProgramStateRef State = C.getState(); 123 SValBuilder &SVB = C.getSValBuilder(); 124 125 ProgramStateRef StateSuccess = State->BindExpr( 126 Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(0, true)); 127 StateSuccess = setErrnoState(StateSuccess, MustNotBeChecked); 128 129 ProgramStateRef StateFailure = State->BindExpr( 130 Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(1, true)); 131 DefinedOrUnknownSVal ErrnoVal = SVB.conjureSymbolVal( 132 nullptr, Call.getOriginExpr(), C.getLocationContext(), C.blockCount()); 133 StateFailure = StateFailure->assume(ErrnoVal, true); 134 assert(StateFailure && "Failed to assume on an initial value."); 135 StateFailure = 136 setErrnoValue(StateFailure, C.getLocationContext(), ErrnoVal, Irrelevant); 137 138 C.addTransition(StateSuccess); 139 C.addTransition(StateFailure); 140 } 141 142 void ErrnoTesterChecker::evalSetErrnoCheckState(CheckerContext &C, 143 const CallEvent &Call) { 144 ProgramStateRef State = C.getState(); 145 SValBuilder &SVB = C.getSValBuilder(); 146 147 ProgramStateRef StateSuccess = State->BindExpr( 148 Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(0, true)); 149 StateSuccess = setErrnoState(StateSuccess, MustNotBeChecked); 150 151 ProgramStateRef StateFailure1 = State->BindExpr( 152 Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(1, true)); 153 StateFailure1 = setErrnoValue(StateFailure1, C, 1, Irrelevant); 154 155 ProgramStateRef StateFailure2 = State->BindExpr( 156 Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(2, true)); 157 StateFailure2 = setErrnoValue(StateFailure2, C, 2, MustBeChecked); 158 159 C.addTransition(StateSuccess, 160 getErrnoNoteTag(C, "Assuming that this function succeeds but " 161 "sets 'errno' to an unspecified value.")); 162 C.addTransition(StateFailure1); 163 C.addTransition( 164 StateFailure2, 165 getErrnoNoteTag(C, "Assuming that this function returns 2. 'errno' " 166 "should be checked to test for failure.")); 167 } 168 169 bool ErrnoTesterChecker::evalCall(const CallEvent &Call, 170 CheckerContext &C) const { 171 const EvalFn *Fn = TestCalls.lookup(Call); 172 if (Fn) { 173 (*Fn)(C, Call); 174 return C.isDifferent(); 175 } 176 return false; 177 } 178 179 void ento::registerErrnoTesterChecker(CheckerManager &Mgr) { 180 Mgr.registerChecker<ErrnoTesterChecker>(); 181 } 182 183 bool ento::shouldRegisterErrnoTesterChecker(const CheckerManager &Mgr) { 184 return true; 185 } 186