xref: /freebsd/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ErrnoTesterChecker.cpp (revision 7fdf597e96a02165cfe22ff357b857d5fa15ed8a)
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 "ErrnoModeling.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 #include <optional>
21 
22 using namespace clang;
23 using namespace ento;
24 using namespace errno_modeling;
25 
26 namespace {
27 
28 class ErrnoTesterChecker : public Checker<eval::Call> {
29 public:
30   bool evalCall(const CallEvent &Call, CheckerContext &C) const;
31 
32 private:
33   /// Evaluate function \code void ErrnoTesterChecker_setErrno(int) \endcode.
34   /// Set value of \c errno to the argument.
35   static void evalSetErrno(CheckerContext &C, const CallEvent &Call);
36   /// Evaluate function \code int ErrnoTesterChecker_getErrno() \endcode.
37   /// Return the value of \c errno.
38   static void evalGetErrno(CheckerContext &C, const CallEvent &Call);
39   /// Evaluate function \code int ErrnoTesterChecker_setErrnoIfError() \endcode.
40   /// Simulate a standard library function tha returns 0 on success and 1 on
41   /// failure. On the success case \c errno is not allowed to be used (may be
42   /// undefined). On the failure case \c errno is set to a fixed value 11 and
43   /// is not needed to be checked.
44   static void evalSetErrnoIfError(CheckerContext &C, const CallEvent &Call);
45   /// Evaluate function \code int ErrnoTesterChecker_setErrnoIfErrorRange()
46   /// \endcode. Same as \c ErrnoTesterChecker_setErrnoIfError but \c errno is
47   /// set to a range (to be nonzero) at the failure case.
48   static void evalSetErrnoIfErrorRange(CheckerContext &C,
49                                        const CallEvent &Call);
50   /// Evaluate function \code int ErrnoTesterChecker_setErrnoCheckState()
51   /// \endcode. This function simulates the following:
52   /// - Return 0 and leave \c errno with undefined value.
53   ///   This is the case of a successful standard function call.
54   ///   For example if \c ftell returns not -1.
55   /// - Return 1 and sets \c errno to a specific error code (1).
56   ///   This is the case of a failed standard function call.
57   ///   The function indicates the failure by a special return value
58   ///   that is returned only at failure.
59   ///   \c errno can be checked but it is not required.
60   ///   For example if \c ftell returns -1.
61   /// - Return 2 and may set errno to a value (actually it does not set it).
62   ///   This is the case of a standard function call where the failure can only
63   ///   be checked by reading from \c errno. The value of \c errno is changed by
64   ///   the function only at failure, the user should set \c errno to 0 before
65   ///   the call (\c ErrnoChecker does not check for this rule).
66   ///   \c strtol is an example of this case, if it returns \c LONG_MIN (or
67   ///   \c LONG_MAX). This case applies only if \c LONG_MIN or \c LONG_MAX is
68   ///   returned, otherwise the first case in this list applies.
69   static void evalSetErrnoCheckState(CheckerContext &C, const CallEvent &Call);
70 
71   using EvalFn = std::function<void(CheckerContext &, const CallEvent &)>;
72   const CallDescriptionMap<EvalFn> TestCalls{
73       {{CDM::SimpleFunc, {"ErrnoTesterChecker_setErrno"}, 1},
74        &ErrnoTesterChecker::evalSetErrno},
75       {{CDM::SimpleFunc, {"ErrnoTesterChecker_getErrno"}, 0},
76        &ErrnoTesterChecker::evalGetErrno},
77       {{CDM::SimpleFunc, {"ErrnoTesterChecker_setErrnoIfError"}, 0},
78        &ErrnoTesterChecker::evalSetErrnoIfError},
79       {{CDM::SimpleFunc, {"ErrnoTesterChecker_setErrnoIfErrorRange"}, 0},
80        &ErrnoTesterChecker::evalSetErrnoIfErrorRange},
81       {{CDM::SimpleFunc, {"ErrnoTesterChecker_setErrnoCheckState"}, 0},
82        &ErrnoTesterChecker::evalSetErrnoCheckState}};
83 };
84 
85 } // namespace
86 
87 void ErrnoTesterChecker::evalSetErrno(CheckerContext &C,
88                                       const CallEvent &Call) {
89   C.addTransition(setErrnoValue(C.getState(), C.getLocationContext(),
90                                 Call.getArgSVal(0), Irrelevant));
91 }
92 
93 void ErrnoTesterChecker::evalGetErrno(CheckerContext &C,
94                                       const CallEvent &Call) {
95   ProgramStateRef State = C.getState();
96 
97   std::optional<SVal> ErrnoVal = getErrnoValue(State);
98   assert(ErrnoVal && "Errno value should be available.");
99   State =
100       State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), *ErrnoVal);
101 
102   C.addTransition(State);
103 }
104 
105 void ErrnoTesterChecker::evalSetErrnoIfError(CheckerContext &C,
106                                              const CallEvent &Call) {
107   ProgramStateRef State = C.getState();
108   SValBuilder &SVB = C.getSValBuilder();
109 
110   ProgramStateRef StateSuccess = State->BindExpr(
111       Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(0, true));
112   StateSuccess = setErrnoState(StateSuccess, MustNotBeChecked);
113 
114   ProgramStateRef StateFailure = State->BindExpr(
115       Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(1, true));
116   StateFailure = setErrnoValue(StateFailure, C, 11, Irrelevant);
117 
118   C.addTransition(StateSuccess);
119   C.addTransition(StateFailure);
120 }
121 
122 void ErrnoTesterChecker::evalSetErrnoIfErrorRange(CheckerContext &C,
123                                                   const CallEvent &Call) {
124   ProgramStateRef State = C.getState();
125   SValBuilder &SVB = C.getSValBuilder();
126 
127   ProgramStateRef StateSuccess = State->BindExpr(
128       Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(0, true));
129   StateSuccess = setErrnoState(StateSuccess, MustNotBeChecked);
130 
131   ProgramStateRef StateFailure = State->BindExpr(
132       Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(1, true));
133   DefinedOrUnknownSVal ErrnoVal = SVB.conjureSymbolVal(
134       nullptr, Call.getOriginExpr(), C.getLocationContext(), C.blockCount());
135   StateFailure = StateFailure->assume(ErrnoVal, true);
136   assert(StateFailure && "Failed to assume on an initial value.");
137   StateFailure =
138       setErrnoValue(StateFailure, C.getLocationContext(), ErrnoVal, Irrelevant);
139 
140   C.addTransition(StateSuccess);
141   C.addTransition(StateFailure);
142 }
143 
144 void ErrnoTesterChecker::evalSetErrnoCheckState(CheckerContext &C,
145                                                 const CallEvent &Call) {
146   ProgramStateRef State = C.getState();
147   SValBuilder &SVB = C.getSValBuilder();
148 
149   ProgramStateRef StateSuccess = State->BindExpr(
150       Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(0, true));
151   StateSuccess = setErrnoState(StateSuccess, MustNotBeChecked);
152 
153   ProgramStateRef StateFailure1 = State->BindExpr(
154       Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(1, true));
155   StateFailure1 = setErrnoValue(StateFailure1, C, 1, Irrelevant);
156 
157   ProgramStateRef StateFailure2 = State->BindExpr(
158       Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(2, true));
159   StateFailure2 = setErrnoValue(StateFailure2, C, 2, MustBeChecked);
160 
161   C.addTransition(StateSuccess,
162                   getErrnoNoteTag(C, "Assuming that this function succeeds but "
163                                      "sets 'errno' to an unspecified value."));
164   C.addTransition(StateFailure1);
165   C.addTransition(
166       StateFailure2,
167       getErrnoNoteTag(C, "Assuming that this function returns 2. 'errno' "
168                          "should be checked to test for failure."));
169 }
170 
171 bool ErrnoTesterChecker::evalCall(const CallEvent &Call,
172                                   CheckerContext &C) const {
173   const EvalFn *Fn = TestCalls.lookup(Call);
174   if (Fn) {
175     (*Fn)(C, Call);
176     return C.isDifferent();
177   }
178   return false;
179 }
180 
181 void ento::registerErrnoTesterChecker(CheckerManager &Mgr) {
182   Mgr.registerChecker<ErrnoTesterChecker>();
183 }
184 
185 bool ento::shouldRegisterErrnoTesterChecker(const CheckerManager &Mgr) {
186   return true;
187 }
188