Path: blob/main/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ErrnoTesterChecker.cpp
35266 views
//=== ErrnoTesterChecker.cpp ------------------------------------*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This defines ErrnoTesterChecker, which is used to test functionality of the9// errno_check API.10//11//===----------------------------------------------------------------------===//1213#include "ErrnoModeling.h"14#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"15#include "clang/StaticAnalyzer/Core/Checker.h"16#include "clang/StaticAnalyzer/Core/CheckerManager.h"17#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"18#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"19#include <optional>2021using namespace clang;22using namespace ento;23using namespace errno_modeling;2425namespace {2627class ErrnoTesterChecker : public Checker<eval::Call> {28public:29bool evalCall(const CallEvent &Call, CheckerContext &C) const;3031private:32/// Evaluate function \code void ErrnoTesterChecker_setErrno(int) \endcode.33/// Set value of \c errno to the argument.34static void evalSetErrno(CheckerContext &C, const CallEvent &Call);35/// Evaluate function \code int ErrnoTesterChecker_getErrno() \endcode.36/// Return the value of \c errno.37static void evalGetErrno(CheckerContext &C, const CallEvent &Call);38/// Evaluate function \code int ErrnoTesterChecker_setErrnoIfError() \endcode.39/// Simulate a standard library function tha returns 0 on success and 1 on40/// failure. On the success case \c errno is not allowed to be used (may be41/// undefined). On the failure case \c errno is set to a fixed value 11 and42/// is not needed to be checked.43static void evalSetErrnoIfError(CheckerContext &C, const CallEvent &Call);44/// Evaluate function \code int ErrnoTesterChecker_setErrnoIfErrorRange()45/// \endcode. Same as \c ErrnoTesterChecker_setErrnoIfError but \c errno is46/// set to a range (to be nonzero) at the failure case.47static void evalSetErrnoIfErrorRange(CheckerContext &C,48const CallEvent &Call);49/// Evaluate function \code int ErrnoTesterChecker_setErrnoCheckState()50/// \endcode. This function simulates the following:51/// - Return 0 and leave \c errno with undefined value.52/// This is the case of a successful standard function call.53/// For example if \c ftell returns not -1.54/// - Return 1 and sets \c errno to a specific error code (1).55/// This is the case of a failed standard function call.56/// The function indicates the failure by a special return value57/// that is returned only at failure.58/// \c errno can be checked but it is not required.59/// For example if \c ftell returns -1.60/// - Return 2 and may set errno to a value (actually it does not set it).61/// This is the case of a standard function call where the failure can only62/// be checked by reading from \c errno. The value of \c errno is changed by63/// the function only at failure, the user should set \c errno to 0 before64/// the call (\c ErrnoChecker does not check for this rule).65/// \c strtol is an example of this case, if it returns \c LONG_MIN (or66/// \c LONG_MAX). This case applies only if \c LONG_MIN or \c LONG_MAX is67/// returned, otherwise the first case in this list applies.68static void evalSetErrnoCheckState(CheckerContext &C, const CallEvent &Call);6970using EvalFn = std::function<void(CheckerContext &, const CallEvent &)>;71const CallDescriptionMap<EvalFn> TestCalls{72{{CDM::SimpleFunc, {"ErrnoTesterChecker_setErrno"}, 1},73&ErrnoTesterChecker::evalSetErrno},74{{CDM::SimpleFunc, {"ErrnoTesterChecker_getErrno"}, 0},75&ErrnoTesterChecker::evalGetErrno},76{{CDM::SimpleFunc, {"ErrnoTesterChecker_setErrnoIfError"}, 0},77&ErrnoTesterChecker::evalSetErrnoIfError},78{{CDM::SimpleFunc, {"ErrnoTesterChecker_setErrnoIfErrorRange"}, 0},79&ErrnoTesterChecker::evalSetErrnoIfErrorRange},80{{CDM::SimpleFunc, {"ErrnoTesterChecker_setErrnoCheckState"}, 0},81&ErrnoTesterChecker::evalSetErrnoCheckState}};82};8384} // namespace8586void ErrnoTesterChecker::evalSetErrno(CheckerContext &C,87const CallEvent &Call) {88C.addTransition(setErrnoValue(C.getState(), C.getLocationContext(),89Call.getArgSVal(0), Irrelevant));90}9192void ErrnoTesterChecker::evalGetErrno(CheckerContext &C,93const CallEvent &Call) {94ProgramStateRef State = C.getState();9596std::optional<SVal> ErrnoVal = getErrnoValue(State);97assert(ErrnoVal && "Errno value should be available.");98State =99State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), *ErrnoVal);100101C.addTransition(State);102}103104void ErrnoTesterChecker::evalSetErrnoIfError(CheckerContext &C,105const CallEvent &Call) {106ProgramStateRef State = C.getState();107SValBuilder &SVB = C.getSValBuilder();108109ProgramStateRef StateSuccess = State->BindExpr(110Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(0, true));111StateSuccess = setErrnoState(StateSuccess, MustNotBeChecked);112113ProgramStateRef StateFailure = State->BindExpr(114Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(1, true));115StateFailure = setErrnoValue(StateFailure, C, 11, Irrelevant);116117C.addTransition(StateSuccess);118C.addTransition(StateFailure);119}120121void ErrnoTesterChecker::evalSetErrnoIfErrorRange(CheckerContext &C,122const CallEvent &Call) {123ProgramStateRef State = C.getState();124SValBuilder &SVB = C.getSValBuilder();125126ProgramStateRef StateSuccess = State->BindExpr(127Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(0, true));128StateSuccess = setErrnoState(StateSuccess, MustNotBeChecked);129130ProgramStateRef StateFailure = State->BindExpr(131Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(1, true));132DefinedOrUnknownSVal ErrnoVal = SVB.conjureSymbolVal(133nullptr, Call.getOriginExpr(), C.getLocationContext(), C.blockCount());134StateFailure = StateFailure->assume(ErrnoVal, true);135assert(StateFailure && "Failed to assume on an initial value.");136StateFailure =137setErrnoValue(StateFailure, C.getLocationContext(), ErrnoVal, Irrelevant);138139C.addTransition(StateSuccess);140C.addTransition(StateFailure);141}142143void ErrnoTesterChecker::evalSetErrnoCheckState(CheckerContext &C,144const CallEvent &Call) {145ProgramStateRef State = C.getState();146SValBuilder &SVB = C.getSValBuilder();147148ProgramStateRef StateSuccess = State->BindExpr(149Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(0, true));150StateSuccess = setErrnoState(StateSuccess, MustNotBeChecked);151152ProgramStateRef StateFailure1 = State->BindExpr(153Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(1, true));154StateFailure1 = setErrnoValue(StateFailure1, C, 1, Irrelevant);155156ProgramStateRef StateFailure2 = State->BindExpr(157Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(2, true));158StateFailure2 = setErrnoValue(StateFailure2, C, 2, MustBeChecked);159160C.addTransition(StateSuccess,161getErrnoNoteTag(C, "Assuming that this function succeeds but "162"sets 'errno' to an unspecified value."));163C.addTransition(StateFailure1);164C.addTransition(165StateFailure2,166getErrnoNoteTag(C, "Assuming that this function returns 2. 'errno' "167"should be checked to test for failure."));168}169170bool ErrnoTesterChecker::evalCall(const CallEvent &Call,171CheckerContext &C) const {172const EvalFn *Fn = TestCalls.lookup(Call);173if (Fn) {174(*Fn)(C, Call);175return C.isDifferent();176}177return false;178}179180void ento::registerErrnoTesterChecker(CheckerManager &Mgr) {181Mgr.registerChecker<ErrnoTesterChecker>();182}183184bool ento::shouldRegisterErrnoTesterChecker(const CheckerManager &Mgr) {185return true;186}187188189