181ad6265SDimitry Andric //=== ErrnoTesterChecker.cpp ------------------------------------*- C++ -*-===//
281ad6265SDimitry Andric //
381ad6265SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
481ad6265SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
581ad6265SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
681ad6265SDimitry Andric //
781ad6265SDimitry Andric //===----------------------------------------------------------------------===//
881ad6265SDimitry Andric //
981ad6265SDimitry Andric // This defines ErrnoTesterChecker, which is used to test functionality of the
1081ad6265SDimitry Andric // errno_check API.
1181ad6265SDimitry Andric //
1281ad6265SDimitry Andric //===----------------------------------------------------------------------===//
1381ad6265SDimitry Andric
1481ad6265SDimitry Andric #include "ErrnoModeling.h"
1581ad6265SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
1681ad6265SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
1781ad6265SDimitry Andric #include "clang/StaticAnalyzer/Core/CheckerManager.h"
1881ad6265SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
1981ad6265SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
20bdd1243dSDimitry Andric #include <optional>
2181ad6265SDimitry Andric
2281ad6265SDimitry Andric using namespace clang;
2381ad6265SDimitry Andric using namespace ento;
2481ad6265SDimitry Andric using namespace errno_modeling;
2581ad6265SDimitry Andric
2681ad6265SDimitry Andric namespace {
2781ad6265SDimitry Andric
2881ad6265SDimitry Andric class ErrnoTesterChecker : public Checker<eval::Call> {
2981ad6265SDimitry Andric public:
3081ad6265SDimitry Andric bool evalCall(const CallEvent &Call, CheckerContext &C) const;
3181ad6265SDimitry Andric
3281ad6265SDimitry Andric private:
3381ad6265SDimitry Andric /// Evaluate function \code void ErrnoTesterChecker_setErrno(int) \endcode.
3481ad6265SDimitry Andric /// Set value of \c errno to the argument.
3581ad6265SDimitry Andric static void evalSetErrno(CheckerContext &C, const CallEvent &Call);
3681ad6265SDimitry Andric /// Evaluate function \code int ErrnoTesterChecker_getErrno() \endcode.
3781ad6265SDimitry Andric /// Return the value of \c errno.
3881ad6265SDimitry Andric static void evalGetErrno(CheckerContext &C, const CallEvent &Call);
3981ad6265SDimitry Andric /// Evaluate function \code int ErrnoTesterChecker_setErrnoIfError() \endcode.
4081ad6265SDimitry Andric /// Simulate a standard library function tha returns 0 on success and 1 on
4181ad6265SDimitry Andric /// failure. On the success case \c errno is not allowed to be used (may be
4281ad6265SDimitry Andric /// undefined). On the failure case \c errno is set to a fixed value 11 and
4381ad6265SDimitry Andric /// is not needed to be checked.
4481ad6265SDimitry Andric static void evalSetErrnoIfError(CheckerContext &C, const CallEvent &Call);
4581ad6265SDimitry Andric /// Evaluate function \code int ErrnoTesterChecker_setErrnoIfErrorRange()
4681ad6265SDimitry Andric /// \endcode. Same as \c ErrnoTesterChecker_setErrnoIfError but \c errno is
4781ad6265SDimitry Andric /// set to a range (to be nonzero) at the failure case.
4881ad6265SDimitry Andric static void evalSetErrnoIfErrorRange(CheckerContext &C,
4981ad6265SDimitry Andric const CallEvent &Call);
5081ad6265SDimitry Andric /// Evaluate function \code int ErrnoTesterChecker_setErrnoCheckState()
5181ad6265SDimitry Andric /// \endcode. This function simulates the following:
5281ad6265SDimitry Andric /// - Return 0 and leave \c errno with undefined value.
5381ad6265SDimitry Andric /// This is the case of a successful standard function call.
5481ad6265SDimitry Andric /// For example if \c ftell returns not -1.
5581ad6265SDimitry Andric /// - Return 1 and sets \c errno to a specific error code (1).
5681ad6265SDimitry Andric /// This is the case of a failed standard function call.
5781ad6265SDimitry Andric /// The function indicates the failure by a special return value
5881ad6265SDimitry Andric /// that is returned only at failure.
5981ad6265SDimitry Andric /// \c errno can be checked but it is not required.
6081ad6265SDimitry Andric /// For example if \c ftell returns -1.
6181ad6265SDimitry Andric /// - Return 2 and may set errno to a value (actually it does not set it).
6281ad6265SDimitry Andric /// This is the case of a standard function call where the failure can only
6381ad6265SDimitry Andric /// be checked by reading from \c errno. The value of \c errno is changed by
6481ad6265SDimitry Andric /// the function only at failure, the user should set \c errno to 0 before
6581ad6265SDimitry Andric /// the call (\c ErrnoChecker does not check for this rule).
6681ad6265SDimitry Andric /// \c strtol is an example of this case, if it returns \c LONG_MIN (or
6781ad6265SDimitry Andric /// \c LONG_MAX). This case applies only if \c LONG_MIN or \c LONG_MAX is
6881ad6265SDimitry Andric /// returned, otherwise the first case in this list applies.
6981ad6265SDimitry Andric static void evalSetErrnoCheckState(CheckerContext &C, const CallEvent &Call);
7081ad6265SDimitry Andric
7181ad6265SDimitry Andric using EvalFn = std::function<void(CheckerContext &, const CallEvent &)>;
7281ad6265SDimitry Andric const CallDescriptionMap<EvalFn> TestCalls{
73*0fca6ea1SDimitry Andric {{CDM::SimpleFunc, {"ErrnoTesterChecker_setErrno"}, 1},
74*0fca6ea1SDimitry Andric &ErrnoTesterChecker::evalSetErrno},
75*0fca6ea1SDimitry Andric {{CDM::SimpleFunc, {"ErrnoTesterChecker_getErrno"}, 0},
76*0fca6ea1SDimitry Andric &ErrnoTesterChecker::evalGetErrno},
77*0fca6ea1SDimitry Andric {{CDM::SimpleFunc, {"ErrnoTesterChecker_setErrnoIfError"}, 0},
7881ad6265SDimitry Andric &ErrnoTesterChecker::evalSetErrnoIfError},
79*0fca6ea1SDimitry Andric {{CDM::SimpleFunc, {"ErrnoTesterChecker_setErrnoIfErrorRange"}, 0},
8081ad6265SDimitry Andric &ErrnoTesterChecker::evalSetErrnoIfErrorRange},
81*0fca6ea1SDimitry Andric {{CDM::SimpleFunc, {"ErrnoTesterChecker_setErrnoCheckState"}, 0},
8281ad6265SDimitry Andric &ErrnoTesterChecker::evalSetErrnoCheckState}};
8381ad6265SDimitry Andric };
8481ad6265SDimitry Andric
8581ad6265SDimitry Andric } // namespace
8681ad6265SDimitry Andric
evalSetErrno(CheckerContext & C,const CallEvent & Call)8781ad6265SDimitry Andric void ErrnoTesterChecker::evalSetErrno(CheckerContext &C,
8881ad6265SDimitry Andric const CallEvent &Call) {
8981ad6265SDimitry Andric C.addTransition(setErrnoValue(C.getState(), C.getLocationContext(),
9081ad6265SDimitry Andric Call.getArgSVal(0), Irrelevant));
9181ad6265SDimitry Andric }
9281ad6265SDimitry Andric
evalGetErrno(CheckerContext & C,const CallEvent & Call)9381ad6265SDimitry Andric void ErrnoTesterChecker::evalGetErrno(CheckerContext &C,
9481ad6265SDimitry Andric const CallEvent &Call) {
9581ad6265SDimitry Andric ProgramStateRef State = C.getState();
9681ad6265SDimitry Andric
97bdd1243dSDimitry Andric std::optional<SVal> ErrnoVal = getErrnoValue(State);
9881ad6265SDimitry Andric assert(ErrnoVal && "Errno value should be available.");
9981ad6265SDimitry Andric State =
10081ad6265SDimitry Andric State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), *ErrnoVal);
10181ad6265SDimitry Andric
10281ad6265SDimitry Andric C.addTransition(State);
10381ad6265SDimitry Andric }
10481ad6265SDimitry Andric
evalSetErrnoIfError(CheckerContext & C,const CallEvent & Call)10581ad6265SDimitry Andric void ErrnoTesterChecker::evalSetErrnoIfError(CheckerContext &C,
10681ad6265SDimitry Andric const CallEvent &Call) {
10781ad6265SDimitry Andric ProgramStateRef State = C.getState();
10881ad6265SDimitry Andric SValBuilder &SVB = C.getSValBuilder();
10981ad6265SDimitry Andric
11081ad6265SDimitry Andric ProgramStateRef StateSuccess = State->BindExpr(
11181ad6265SDimitry Andric Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(0, true));
11281ad6265SDimitry Andric StateSuccess = setErrnoState(StateSuccess, MustNotBeChecked);
11381ad6265SDimitry Andric
11481ad6265SDimitry Andric ProgramStateRef StateFailure = State->BindExpr(
11581ad6265SDimitry Andric Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(1, true));
11681ad6265SDimitry Andric StateFailure = setErrnoValue(StateFailure, C, 11, Irrelevant);
11781ad6265SDimitry Andric
11881ad6265SDimitry Andric C.addTransition(StateSuccess);
11981ad6265SDimitry Andric C.addTransition(StateFailure);
12081ad6265SDimitry Andric }
12181ad6265SDimitry Andric
evalSetErrnoIfErrorRange(CheckerContext & C,const CallEvent & Call)12281ad6265SDimitry Andric void ErrnoTesterChecker::evalSetErrnoIfErrorRange(CheckerContext &C,
12381ad6265SDimitry Andric const CallEvent &Call) {
12481ad6265SDimitry Andric ProgramStateRef State = C.getState();
12581ad6265SDimitry Andric SValBuilder &SVB = C.getSValBuilder();
12681ad6265SDimitry Andric
12781ad6265SDimitry Andric ProgramStateRef StateSuccess = State->BindExpr(
12881ad6265SDimitry Andric Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(0, true));
12981ad6265SDimitry Andric StateSuccess = setErrnoState(StateSuccess, MustNotBeChecked);
13081ad6265SDimitry Andric
13181ad6265SDimitry Andric ProgramStateRef StateFailure = State->BindExpr(
13281ad6265SDimitry Andric Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(1, true));
13381ad6265SDimitry Andric DefinedOrUnknownSVal ErrnoVal = SVB.conjureSymbolVal(
13481ad6265SDimitry Andric nullptr, Call.getOriginExpr(), C.getLocationContext(), C.blockCount());
13581ad6265SDimitry Andric StateFailure = StateFailure->assume(ErrnoVal, true);
13681ad6265SDimitry Andric assert(StateFailure && "Failed to assume on an initial value.");
13781ad6265SDimitry Andric StateFailure =
13881ad6265SDimitry Andric setErrnoValue(StateFailure, C.getLocationContext(), ErrnoVal, Irrelevant);
13981ad6265SDimitry Andric
14081ad6265SDimitry Andric C.addTransition(StateSuccess);
14181ad6265SDimitry Andric C.addTransition(StateFailure);
14281ad6265SDimitry Andric }
14381ad6265SDimitry Andric
evalSetErrnoCheckState(CheckerContext & C,const CallEvent & Call)14481ad6265SDimitry Andric void ErrnoTesterChecker::evalSetErrnoCheckState(CheckerContext &C,
14581ad6265SDimitry Andric const CallEvent &Call) {
14681ad6265SDimitry Andric ProgramStateRef State = C.getState();
14781ad6265SDimitry Andric SValBuilder &SVB = C.getSValBuilder();
14881ad6265SDimitry Andric
14981ad6265SDimitry Andric ProgramStateRef StateSuccess = State->BindExpr(
15081ad6265SDimitry Andric Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(0, true));
15181ad6265SDimitry Andric StateSuccess = setErrnoState(StateSuccess, MustNotBeChecked);
15281ad6265SDimitry Andric
15381ad6265SDimitry Andric ProgramStateRef StateFailure1 = State->BindExpr(
15481ad6265SDimitry Andric Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(1, true));
15581ad6265SDimitry Andric StateFailure1 = setErrnoValue(StateFailure1, C, 1, Irrelevant);
15681ad6265SDimitry Andric
15781ad6265SDimitry Andric ProgramStateRef StateFailure2 = State->BindExpr(
15881ad6265SDimitry Andric Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(2, true));
15981ad6265SDimitry Andric StateFailure2 = setErrnoValue(StateFailure2, C, 2, MustBeChecked);
16081ad6265SDimitry Andric
16181ad6265SDimitry Andric C.addTransition(StateSuccess,
16281ad6265SDimitry Andric getErrnoNoteTag(C, "Assuming that this function succeeds but "
16381ad6265SDimitry Andric "sets 'errno' to an unspecified value."));
16481ad6265SDimitry Andric C.addTransition(StateFailure1);
16581ad6265SDimitry Andric C.addTransition(
16681ad6265SDimitry Andric StateFailure2,
16781ad6265SDimitry Andric getErrnoNoteTag(C, "Assuming that this function returns 2. 'errno' "
16881ad6265SDimitry Andric "should be checked to test for failure."));
16981ad6265SDimitry Andric }
17081ad6265SDimitry Andric
evalCall(const CallEvent & Call,CheckerContext & C) const17181ad6265SDimitry Andric bool ErrnoTesterChecker::evalCall(const CallEvent &Call,
17281ad6265SDimitry Andric CheckerContext &C) const {
17381ad6265SDimitry Andric const EvalFn *Fn = TestCalls.lookup(Call);
17481ad6265SDimitry Andric if (Fn) {
17581ad6265SDimitry Andric (*Fn)(C, Call);
17681ad6265SDimitry Andric return C.isDifferent();
17781ad6265SDimitry Andric }
17881ad6265SDimitry Andric return false;
17981ad6265SDimitry Andric }
18081ad6265SDimitry Andric
registerErrnoTesterChecker(CheckerManager & Mgr)18181ad6265SDimitry Andric void ento::registerErrnoTesterChecker(CheckerManager &Mgr) {
18281ad6265SDimitry Andric Mgr.registerChecker<ErrnoTesterChecker>();
18381ad6265SDimitry Andric }
18481ad6265SDimitry Andric
shouldRegisterErrnoTesterChecker(const CheckerManager & Mgr)18581ad6265SDimitry Andric bool ento::shouldRegisterErrnoTesterChecker(const CheckerManager &Mgr) {
18681ad6265SDimitry Andric return true;
18781ad6265SDimitry Andric }
188