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 "Errno.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 21 using namespace clang; 22 using namespace ento; 23 24 namespace { 25 26 class ErrnoTesterChecker : public Checker<eval::Call> { 27 public: 28 bool evalCall(const CallEvent &Call, CheckerContext &C) const; 29 30 private: 31 static void evalSetErrno(CheckerContext &C, const CallEvent &Call); 32 static void evalGetErrno(CheckerContext &C, const CallEvent &Call); 33 static void evalSetErrnoIfError(CheckerContext &C, const CallEvent &Call); 34 static void evalSetErrnoIfErrorRange(CheckerContext &C, 35 const CallEvent &Call); 36 37 using EvalFn = std::function<void(CheckerContext &, const CallEvent &)>; 38 const CallDescriptionMap<EvalFn> TestCalls{ 39 {{"ErrnoTesterChecker_setErrno", 1}, &ErrnoTesterChecker::evalSetErrno}, 40 {{"ErrnoTesterChecker_getErrno", 0}, &ErrnoTesterChecker::evalGetErrno}, 41 {{"ErrnoTesterChecker_setErrnoIfError", 0}, 42 &ErrnoTesterChecker::evalSetErrnoIfError}, 43 {{"ErrnoTesterChecker_setErrnoIfErrorRange", 0}, 44 &ErrnoTesterChecker::evalSetErrnoIfErrorRange}}; 45 }; 46 47 } // namespace 48 49 void ErrnoTesterChecker::evalSetErrno(CheckerContext &C, 50 const CallEvent &Call) { 51 C.addTransition(errno_modeling::setErrnoValue( 52 C.getState(), C.getLocationContext(), Call.getArgSVal(0))); 53 } 54 55 void ErrnoTesterChecker::evalGetErrno(CheckerContext &C, 56 const CallEvent &Call) { 57 ProgramStateRef State = C.getState(); 58 59 Optional<SVal> ErrnoVal = errno_modeling::getErrnoValue(State); 60 assert(ErrnoVal && "Errno value should be available."); 61 State = 62 State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), *ErrnoVal); 63 64 C.addTransition(State); 65 } 66 67 void ErrnoTesterChecker::evalSetErrnoIfError(CheckerContext &C, 68 const CallEvent &Call) { 69 ProgramStateRef State = C.getState(); 70 SValBuilder &SVB = C.getSValBuilder(); 71 72 ProgramStateRef StateSuccess = State->BindExpr( 73 Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(0, true)); 74 75 ProgramStateRef StateFailure = State->BindExpr( 76 Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(1, true)); 77 StateFailure = errno_modeling::setErrnoValue(StateFailure, C, 11); 78 79 C.addTransition(StateSuccess); 80 C.addTransition(StateFailure); 81 } 82 83 void ErrnoTesterChecker::evalSetErrnoIfErrorRange(CheckerContext &C, 84 const CallEvent &Call) { 85 ProgramStateRef State = C.getState(); 86 SValBuilder &SVB = C.getSValBuilder(); 87 88 ProgramStateRef StateSuccess = State->BindExpr( 89 Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(0, true)); 90 91 ProgramStateRef StateFailure = State->BindExpr( 92 Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(1, true)); 93 DefinedOrUnknownSVal ErrnoVal = SVB.conjureSymbolVal( 94 nullptr, Call.getOriginExpr(), C.getLocationContext(), C.blockCount()); 95 StateFailure = StateFailure->assume(ErrnoVal, true); 96 assert(StateFailure && "Failed to assume on an initial value."); 97 StateFailure = errno_modeling::setErrnoValue( 98 StateFailure, C.getLocationContext(), ErrnoVal); 99 100 C.addTransition(StateSuccess); 101 C.addTransition(StateFailure); 102 } 103 104 bool ErrnoTesterChecker::evalCall(const CallEvent &Call, 105 CheckerContext &C) const { 106 const EvalFn *Fn = TestCalls.lookup(Call); 107 if (Fn) { 108 (*Fn)(C, Call); 109 return C.isDifferent(); 110 } 111 return false; 112 } 113 114 void ento::registerErrnoTesterChecker(CheckerManager &Mgr) { 115 Mgr.registerChecker<ErrnoTesterChecker>(); 116 } 117 118 bool ento::shouldRegisterErrnoTesterChecker(const CheckerManager &Mgr) { 119 return true; 120 } 121