clang 20.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 {{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
87void ErrnoTesterChecker::evalSetErrno(CheckerContext &C,
88 const CallEvent &Call) {
89 C.addTransition(setErrnoValue(C.getState(), C.getLocationContext(),
90 Call.getArgSVal(0), Irrelevant));
91}
92
93void 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
105void 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
122void 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));
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
144void 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
171bool 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
181void ento::registerErrnoTesterChecker(CheckerManager &Mgr) {
182 Mgr.registerChecker<ErrnoTesterChecker>();
183}
184
185bool ento::shouldRegisterErrnoTesterChecker(const CheckerManager &Mgr) {
186 return true;
187}
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.