Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ErrnoTesterChecker.cpp
35266 views
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
189