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
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(State: setErrnoValue(State: C.getState(), LCtx: C.getLocationContext(),
90 Value: Call.getArgSVal(Index: 0), EState: 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(S: Call.getOriginExpr(), LCtx: C.getLocationContext(), V: *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 S: Call.getOriginExpr(), LCtx: C.getLocationContext(), V: SVB.makeIntVal(integer: 0, isUnsigned: true));
112 StateSuccess = setErrnoState(State: StateSuccess, EState: MustNotBeChecked);
113
114 ProgramStateRef StateFailure = State->BindExpr(
115 S: Call.getOriginExpr(), LCtx: C.getLocationContext(), V: SVB.makeIntVal(integer: 1, isUnsigned: true));
116 StateFailure = setErrnoValue(State: StateFailure, C, Value: 11, EState: Irrelevant);
117
118 C.addTransition(State: StateSuccess);
119 C.addTransition(State: 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 S: Call.getOriginExpr(), LCtx: C.getLocationContext(), V: SVB.makeIntVal(integer: 0, isUnsigned: true));
129 StateSuccess = setErrnoState(State: StateSuccess, EState: MustNotBeChecked);
130
131 ProgramStateRef StateFailure = State->BindExpr(
132 S: Call.getOriginExpr(), LCtx: C.getLocationContext(), V: SVB.makeIntVal(integer: 1, isUnsigned: true));
133 DefinedOrUnknownSVal ErrnoVal = SVB.conjureSymbolVal(
134 symbolTag: nullptr, expr: Call.getOriginExpr(), LCtx: C.getLocationContext(), count: C.blockCount());
135 StateFailure = StateFailure->assume(Cond: ErrnoVal, Assumption: true);
136 assert(StateFailure && "Failed to assume on an initial value.");
137 StateFailure =
138 setErrnoValue(State: StateFailure, LCtx: C.getLocationContext(), Value: ErrnoVal, EState: Irrelevant);
139
140 C.addTransition(State: StateSuccess);
141 C.addTransition(State: 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 S: Call.getOriginExpr(), LCtx: C.getLocationContext(), V: SVB.makeIntVal(integer: 0, isUnsigned: true));
151 StateSuccess = setErrnoState(State: StateSuccess, EState: MustNotBeChecked);
152
153 ProgramStateRef StateFailure1 = State->BindExpr(
154 S: Call.getOriginExpr(), LCtx: C.getLocationContext(), V: SVB.makeIntVal(integer: 1, isUnsigned: true));
155 StateFailure1 = setErrnoValue(State: StateFailure1, C, Value: 1, EState: Irrelevant);
156
157 ProgramStateRef StateFailure2 = State->BindExpr(
158 S: Call.getOriginExpr(), LCtx: C.getLocationContext(), V: SVB.makeIntVal(integer: 2, isUnsigned: true));
159 StateFailure2 = setErrnoValue(State: StateFailure2, C, Value: 2, EState: MustBeChecked);
160
161 C.addTransition(State: StateSuccess,
162 Tag: getErrnoNoteTag(C, Message: "Assuming that this function succeeds but "
163 "sets 'errno' to an unspecified value."));
164 C.addTransition(State: StateFailure1);
165 C.addTransition(
166 State: StateFailure2,
167 Tag: getErrnoNoteTag(C, Message: "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}
188