xref: /freebsd-src/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ErrnoTesterChecker.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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 
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 
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 
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 
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 
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 
17181ad6265SDimitry 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 
18181ad6265SDimitry Andric void ento::registerErrnoTesterChecker(CheckerManager &Mgr) {
18281ad6265SDimitry Andric   Mgr.registerChecker<ErrnoTesterChecker>();
18381ad6265SDimitry Andric }
18481ad6265SDimitry Andric 
18581ad6265SDimitry Andric bool ento::shouldRegisterErrnoTesterChecker(const CheckerManager &Mgr) {
18681ad6265SDimitry Andric   return true;
18781ad6265SDimitry Andric }
188