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