clang 19.0.0git
ErrnoTesterChecker.cpp
Go to the documentation of this file.
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"
20#include <optional>
21
22using namespace clang;
23using namespace ento;
24using namespace errno_modeling;
25
26namespace {
27
28class ErrnoTesterChecker : public Checker<eval::Call> {
29public:
30 bool evalCall(const CallEvent &Call, CheckerContext &C) const;
31
32private:
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 {{{"ErrnoTesterChecker_setErrno"}, 1}, &ErrnoTesterChecker::evalSetErrno},
74 {{{"ErrnoTesterChecker_getErrno"}, 0}, &ErrnoTesterChecker::evalGetErrno},
75 {{{"ErrnoTesterChecker_setErrnoIfError"}, 0},
76 &ErrnoTesterChecker::evalSetErrnoIfError},
77 {{{"ErrnoTesterChecker_setErrnoIfErrorRange"}, 0},
78 &ErrnoTesterChecker::evalSetErrnoIfErrorRange},
79 {{{"ErrnoTesterChecker_setErrnoCheckState"}, 0},
80 &ErrnoTesterChecker::evalSetErrnoCheckState}};
81};
82
83} // namespace
84
85void ErrnoTesterChecker::evalSetErrno(CheckerContext &C,
86 const CallEvent &Call) {
87 C.addTransition(setErrnoValue(C.getState(), C.getLocationContext(),
88 Call.getArgSVal(0), Irrelevant));
89}
90
91void ErrnoTesterChecker::evalGetErrno(CheckerContext &C,
92 const CallEvent &Call) {
93 ProgramStateRef State = C.getState();
94
95 std::optional<SVal> ErrnoVal = getErrnoValue(State);
96 assert(ErrnoVal && "Errno value should be available.");
97 State =
98 State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), *ErrnoVal);
99
100 C.addTransition(State);
101}
102
103void ErrnoTesterChecker::evalSetErrnoIfError(CheckerContext &C,
104 const CallEvent &Call) {
105 ProgramStateRef State = C.getState();
106 SValBuilder &SVB = C.getSValBuilder();
107
108 ProgramStateRef StateSuccess = State->BindExpr(
109 Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(0, true));
110 StateSuccess = setErrnoState(StateSuccess, MustNotBeChecked);
111
112 ProgramStateRef StateFailure = State->BindExpr(
113 Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(1, true));
114 StateFailure = setErrnoValue(StateFailure, C, 11, Irrelevant);
115
116 C.addTransition(StateSuccess);
117 C.addTransition(StateFailure);
118}
119
120void ErrnoTesterChecker::evalSetErrnoIfErrorRange(CheckerContext &C,
121 const CallEvent &Call) {
122 ProgramStateRef State = C.getState();
123 SValBuilder &SVB = C.getSValBuilder();
124
125 ProgramStateRef StateSuccess = State->BindExpr(
126 Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(0, true));
127 StateSuccess = setErrnoState(StateSuccess, MustNotBeChecked);
128
129 ProgramStateRef StateFailure = State->BindExpr(
130 Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(1, true));
132 nullptr, Call.getOriginExpr(), C.getLocationContext(), C.blockCount());
133 StateFailure = StateFailure->assume(ErrnoVal, true);
134 assert(StateFailure && "Failed to assume on an initial value.");
135 StateFailure =
136 setErrnoValue(StateFailure, C.getLocationContext(), ErrnoVal, Irrelevant);
137
138 C.addTransition(StateSuccess);
139 C.addTransition(StateFailure);
140}
141
142void ErrnoTesterChecker::evalSetErrnoCheckState(CheckerContext &C,
143 const CallEvent &Call) {
144 ProgramStateRef State = C.getState();
145 SValBuilder &SVB = C.getSValBuilder();
146
147 ProgramStateRef StateSuccess = State->BindExpr(
148 Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(0, true));
149 StateSuccess = setErrnoState(StateSuccess, MustNotBeChecked);
150
151 ProgramStateRef StateFailure1 = State->BindExpr(
152 Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(1, true));
153 StateFailure1 = setErrnoValue(StateFailure1, C, 1, Irrelevant);
154
155 ProgramStateRef StateFailure2 = State->BindExpr(
156 Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(2, true));
157 StateFailure2 = setErrnoValue(StateFailure2, C, 2, MustBeChecked);
158
159 C.addTransition(StateSuccess,
160 getErrnoNoteTag(C, "Assuming that this function succeeds but "
161 "sets 'errno' to an unspecified value."));
162 C.addTransition(StateFailure1);
163 C.addTransition(
164 StateFailure2,
165 getErrnoNoteTag(C, "Assuming that this function returns 2. 'errno' "
166 "should be checked to test for failure."));
167}
168
169bool ErrnoTesterChecker::evalCall(const CallEvent &Call,
170 CheckerContext &C) const {
171 const EvalFn *Fn = TestCalls.lookup(Call);
172 if (Fn) {
173 (*Fn)(C, Call);
174 return C.isDifferent();
175 }
176 return false;
177}
178
179void ento::registerErrnoTesterChecker(CheckerManager &Mgr) {
180 Mgr.registerChecker<ErrnoTesterChecker>();
181}
182
183bool ento::shouldRegisterErrnoTesterChecker(const CheckerManager &Mgr) {
184 return true;
185}
An immutable map from CallDescriptions to arbitrary data.
Represents an abstract call to a function or method along a particular path.
Definition: CallEvent.h:153
CHECKER * registerChecker(AT &&... Args)
Used to register checkers.
nonloc::ConcreteInt makeIntVal(const IntegerLiteral *integer)
Definition: SValBuilder.h:290
DefinedOrUnknownSVal conjureSymbolVal(const void *symbolTag, const Expr *expr, const LocationContext *LCtx, unsigned count)
Create a new symbol with a unique 'name'.
ProgramStateRef setErrnoValue(ProgramStateRef State, const LocationContext *LCtx, SVal Value, ErrnoCheckState EState)
Set value of 'errno' to any SVal, if possible.
ProgramStateRef setErrnoState(ProgramStateRef State, ErrnoCheckState EState)
Set the errno check state, do not modify the errno value.
const NoteTag * getErrnoNoteTag(CheckerContext &C, const std::string &Message)
Create a NoteTag that displays the message if the 'errno' memory region is marked as interesting,...
std::optional< SVal > getErrnoValue(ProgramStateRef State)
Returns the value of 'errno', if 'errno' was found in the AST.
@ MustBeChecked
Value of 'errno' should be checked to find out if a previous function call has failed.
Definition: ErrnoModeling.h:36
@ Irrelevant
We do not know anything about 'errno'.
Definition: ErrnoModeling.h:29
@ MustNotBeChecked
Value of 'errno' is not allowed to be read, it can contain an unspecified value.
Definition: ErrnoModeling.h:42
The JSON file list parser is used to communicate input to InstallAPI.