diff options
Diffstat (limited to 'contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ErrnoTesterChecker.cpp')
| -rw-r--r-- | contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ErrnoTesterChecker.cpp | 185 |
1 files changed, 185 insertions, 0 deletions
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ErrnoTesterChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ErrnoTesterChecker.cpp new file mode 100644 index 000000000000..c46ebee0c94f --- /dev/null +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ErrnoTesterChecker.cpp @@ -0,0 +1,185 @@ +//=== ErrnoTesterChecker.cpp ------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This defines ErrnoTesterChecker, which is used to test functionality of the +// errno_check API. +// +//===----------------------------------------------------------------------===// + +#include "ErrnoModeling.h" +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include <optional> + +using namespace clang; +using namespace ento; +using namespace errno_modeling; + +namespace { + +class ErrnoTesterChecker : public Checker<eval::Call> { +public: + bool evalCall(const CallEvent &Call, CheckerContext &C) const; + +private: + /// Evaluate function \code void ErrnoTesterChecker_setErrno(int) \endcode. + /// Set value of \c errno to the argument. + static void evalSetErrno(CheckerContext &C, const CallEvent &Call); + /// Evaluate function \code int ErrnoTesterChecker_getErrno() \endcode. + /// Return the value of \c errno. + static void evalGetErrno(CheckerContext &C, const CallEvent &Call); + /// Evaluate function \code int ErrnoTesterChecker_setErrnoIfError() \endcode. + /// Simulate a standard library function tha returns 0 on success and 1 on + /// failure. On the success case \c errno is not allowed to be used (may be + /// undefined). On the failure case \c errno is set to a fixed value 11 and + /// is not needed to be checked. + static void evalSetErrnoIfError(CheckerContext &C, const CallEvent &Call); + /// Evaluate function \code int ErrnoTesterChecker_setErrnoIfErrorRange() + /// \endcode. Same as \c ErrnoTesterChecker_setErrnoIfError but \c errno is + /// set to a range (to be nonzero) at the failure case. + static void evalSetErrnoIfErrorRange(CheckerContext &C, + const CallEvent &Call); + /// Evaluate function \code int ErrnoTesterChecker_setErrnoCheckState() + /// \endcode. This function simulates the following: + /// - Return 0 and leave \c errno with undefined value. + /// This is the case of a successful standard function call. + /// For example if \c ftell returns not -1. + /// - Return 1 and sets \c errno to a specific error code (1). + /// This is the case of a failed standard function call. + /// The function indicates the failure by a special return value + /// that is returned only at failure. + /// \c errno can be checked but it is not required. + /// For example if \c ftell returns -1. + /// - Return 2 and may set errno to a value (actually it does not set it). + /// This is the case of a standard function call where the failure can only + /// be checked by reading from \c errno. The value of \c errno is changed by + /// the function only at failure, the user should set \c errno to 0 before + /// the call (\c ErrnoChecker does not check for this rule). + /// \c strtol is an example of this case, if it returns \c LONG_MIN (or + /// \c LONG_MAX). This case applies only if \c LONG_MIN or \c LONG_MAX is + /// returned, otherwise the first case in this list applies. + static void evalSetErrnoCheckState(CheckerContext &C, const CallEvent &Call); + + using EvalFn = std::function<void(CheckerContext &, const CallEvent &)>; + const CallDescriptionMap<EvalFn> TestCalls{ + {{{"ErrnoTesterChecker_setErrno"}, 1}, &ErrnoTesterChecker::evalSetErrno}, + {{{"ErrnoTesterChecker_getErrno"}, 0}, &ErrnoTesterChecker::evalGetErrno}, + {{{"ErrnoTesterChecker_setErrnoIfError"}, 0}, + &ErrnoTesterChecker::evalSetErrnoIfError}, + {{{"ErrnoTesterChecker_setErrnoIfErrorRange"}, 0}, + &ErrnoTesterChecker::evalSetErrnoIfErrorRange}, + {{{"ErrnoTesterChecker_setErrnoCheckState"}, 0}, + &ErrnoTesterChecker::evalSetErrnoCheckState}}; +}; + +} // namespace + +void ErrnoTesterChecker::evalSetErrno(CheckerContext &C, + const CallEvent &Call) { + C.addTransition(setErrnoValue(C.getState(), C.getLocationContext(), + Call.getArgSVal(0), Irrelevant)); +} + +void ErrnoTesterChecker::evalGetErrno(CheckerContext &C, + const CallEvent &Call) { + ProgramStateRef State = C.getState(); + + std::optional<SVal> ErrnoVal = getErrnoValue(State); + assert(ErrnoVal && "Errno value should be available."); + State = + State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), *ErrnoVal); + + C.addTransition(State); +} + +void ErrnoTesterChecker::evalSetErrnoIfError(CheckerContext &C, + const CallEvent &Call) { + ProgramStateRef State = C.getState(); + SValBuilder &SVB = C.getSValBuilder(); + + ProgramStateRef StateSuccess = State->BindExpr( + Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(0, true)); + StateSuccess = setErrnoState(StateSuccess, MustNotBeChecked); + + ProgramStateRef StateFailure = State->BindExpr( + Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(1, true)); + StateFailure = setErrnoValue(StateFailure, C, 11, Irrelevant); + + C.addTransition(StateSuccess); + C.addTransition(StateFailure); +} + +void ErrnoTesterChecker::evalSetErrnoIfErrorRange(CheckerContext &C, + const CallEvent &Call) { + ProgramStateRef State = C.getState(); + SValBuilder &SVB = C.getSValBuilder(); + + ProgramStateRef StateSuccess = State->BindExpr( + Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(0, true)); + StateSuccess = setErrnoState(StateSuccess, MustNotBeChecked); + + ProgramStateRef StateFailure = State->BindExpr( + Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(1, true)); + DefinedOrUnknownSVal ErrnoVal = SVB.conjureSymbolVal( + nullptr, Call.getOriginExpr(), C.getLocationContext(), C.blockCount()); + StateFailure = StateFailure->assume(ErrnoVal, true); + assert(StateFailure && "Failed to assume on an initial value."); + StateFailure = + setErrnoValue(StateFailure, C.getLocationContext(), ErrnoVal, Irrelevant); + + C.addTransition(StateSuccess); + C.addTransition(StateFailure); +} + +void ErrnoTesterChecker::evalSetErrnoCheckState(CheckerContext &C, + const CallEvent &Call) { + ProgramStateRef State = C.getState(); + SValBuilder &SVB = C.getSValBuilder(); + + ProgramStateRef StateSuccess = State->BindExpr( + Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(0, true)); + StateSuccess = setErrnoState(StateSuccess, MustNotBeChecked); + + ProgramStateRef StateFailure1 = State->BindExpr( + Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(1, true)); + StateFailure1 = setErrnoValue(StateFailure1, C, 1, Irrelevant); + + ProgramStateRef StateFailure2 = State->BindExpr( + Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(2, true)); + StateFailure2 = setErrnoValue(StateFailure2, C, 2, MustBeChecked); + + C.addTransition(StateSuccess, + getErrnoNoteTag(C, "Assuming that this function succeeds but " + "sets 'errno' to an unspecified value.")); + C.addTransition(StateFailure1); + C.addTransition( + StateFailure2, + getErrnoNoteTag(C, "Assuming that this function returns 2. 'errno' " + "should be checked to test for failure.")); +} + +bool ErrnoTesterChecker::evalCall(const CallEvent &Call, + CheckerContext &C) const { + const EvalFn *Fn = TestCalls.lookup(Call); + if (Fn) { + (*Fn)(C, Call); + return C.isDifferent(); + } + return false; +} + +void ento::registerErrnoTesterChecker(CheckerManager &Mgr) { + Mgr.registerChecker<ErrnoTesterChecker>(); +} + +bool ento::shouldRegisterErrnoTesterChecker(const CheckerManager &Mgr) { + return true; +} |
