1 //=== ErrnoTesterChecker.cpp ------------------------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 // This defines ErrnoTesterChecker, which is used to test functionality of the
12 //===----------------------------------------------------------------------===//
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"
22 using namespace clang
;
24 using namespace errno_modeling
;
28 class ErrnoTesterChecker
: public Checker
<eval::Call
> {
30 bool evalCall(const CallEvent
&Call
, CheckerContext
&C
) const;
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
);
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
}};
85 void ErrnoTesterChecker::evalSetErrno(CheckerContext
&C
,
86 const CallEvent
&Call
) {
87 C
.addTransition(setErrnoValue(C
.getState(), C
.getLocationContext(),
88 Call
.getArgSVal(0), Irrelevant
));
91 void ErrnoTesterChecker::evalGetErrno(CheckerContext
&C
,
92 const CallEvent
&Call
) {
93 ProgramStateRef State
= C
.getState();
95 std::optional
<SVal
> ErrnoVal
= getErrnoValue(State
);
96 assert(ErrnoVal
&& "Errno value should be available.");
98 State
->BindExpr(Call
.getOriginExpr(), C
.getLocationContext(), *ErrnoVal
);
100 C
.addTransition(State
);
103 void ErrnoTesterChecker::evalSetErrnoIfError(CheckerContext
&C
,
104 const CallEvent
&Call
) {
105 ProgramStateRef State
= C
.getState();
106 SValBuilder
&SVB
= C
.getSValBuilder();
108 ProgramStateRef StateSuccess
= State
->BindExpr(
109 Call
.getOriginExpr(), C
.getLocationContext(), SVB
.makeIntVal(0, true));
110 StateSuccess
= setErrnoState(StateSuccess
, MustNotBeChecked
);
112 ProgramStateRef StateFailure
= State
->BindExpr(
113 Call
.getOriginExpr(), C
.getLocationContext(), SVB
.makeIntVal(1, true));
114 StateFailure
= setErrnoValue(StateFailure
, C
, 11, Irrelevant
);
116 C
.addTransition(StateSuccess
);
117 C
.addTransition(StateFailure
);
120 void ErrnoTesterChecker::evalSetErrnoIfErrorRange(CheckerContext
&C
,
121 const CallEvent
&Call
) {
122 ProgramStateRef State
= C
.getState();
123 SValBuilder
&SVB
= C
.getSValBuilder();
125 ProgramStateRef StateSuccess
= State
->BindExpr(
126 Call
.getOriginExpr(), C
.getLocationContext(), SVB
.makeIntVal(0, true));
127 StateSuccess
= setErrnoState(StateSuccess
, MustNotBeChecked
);
129 ProgramStateRef StateFailure
= State
->BindExpr(
130 Call
.getOriginExpr(), C
.getLocationContext(), SVB
.makeIntVal(1, true));
131 DefinedOrUnknownSVal ErrnoVal
= SVB
.conjureSymbolVal(
132 nullptr, Call
.getOriginExpr(), C
.getLocationContext(), C
.blockCount());
133 StateFailure
= StateFailure
->assume(ErrnoVal
, true);
134 assert(StateFailure
&& "Failed to assume on an initial value.");
136 setErrnoValue(StateFailure
, C
.getLocationContext(), ErrnoVal
, Irrelevant
);
138 C
.addTransition(StateSuccess
);
139 C
.addTransition(StateFailure
);
142 void ErrnoTesterChecker::evalSetErrnoCheckState(CheckerContext
&C
,
143 const CallEvent
&Call
) {
144 ProgramStateRef State
= C
.getState();
145 SValBuilder
&SVB
= C
.getSValBuilder();
147 ProgramStateRef StateSuccess
= State
->BindExpr(
148 Call
.getOriginExpr(), C
.getLocationContext(), SVB
.makeIntVal(0, true));
149 StateSuccess
= setErrnoState(StateSuccess
, MustNotBeChecked
);
151 ProgramStateRef StateFailure1
= State
->BindExpr(
152 Call
.getOriginExpr(), C
.getLocationContext(), SVB
.makeIntVal(1, true));
153 StateFailure1
= setErrnoValue(StateFailure1
, C
, 1, Irrelevant
);
155 ProgramStateRef StateFailure2
= State
->BindExpr(
156 Call
.getOriginExpr(), C
.getLocationContext(), SVB
.makeIntVal(2, true));
157 StateFailure2
= setErrnoValue(StateFailure2
, C
, 2, MustBeChecked
);
159 C
.addTransition(StateSuccess
,
160 getErrnoNoteTag(C
, "Assuming that this function succeeds but "
161 "sets 'errno' to an unspecified value."));
162 C
.addTransition(StateFailure1
);
165 getErrnoNoteTag(C
, "Assuming that this function returns 2. 'errno' "
166 "should be checked to test for failure."));
169 bool ErrnoTesterChecker::evalCall(const CallEvent
&Call
,
170 CheckerContext
&C
) const {
171 const EvalFn
*Fn
= TestCalls
.lookup(Call
);
174 return C
.isDifferent();
179 void ento::registerErrnoTesterChecker(CheckerManager
&Mgr
) {
180 Mgr
.registerChecker
<ErrnoTesterChecker
>();
183 bool ento::shouldRegisterErrnoTesterChecker(const CheckerManager
&Mgr
) {