Coverage Report

Created: 2023-09-12 09:32

/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/StaticAnalyzer/Checkers/ErrnoTesterChecker.cpp
Line
Count
Source
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
      {{{"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
85
void ErrnoTesterChecker::evalSetErrno(CheckerContext &C,
86
4
                                      const CallEvent &Call) {
87
4
  C.addTransition(setErrnoValue(C.getState(), C.getLocationContext(),
88
4
                                Call.getArgSVal(0), Irrelevant));
89
4
}
90
91
void ErrnoTesterChecker::evalGetErrno(CheckerContext &C,
92
4
                                      const CallEvent &Call) {
93
4
  ProgramStateRef State = C.getState();
94
95
4
  std::optional<SVal> ErrnoVal = getErrnoValue(State);
96
4
  assert(ErrnoVal && "Errno value should be available.");
97
4
  State =
98
4
      State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), *ErrnoVal);
99
100
4
  C.addTransition(State);
101
4
}
102
103
void ErrnoTesterChecker::evalSetErrnoIfError(CheckerContext &C,
104
38
                                             const CallEvent &Call) {
105
38
  ProgramStateRef State = C.getState();
106
38
  SValBuilder &SVB = C.getSValBuilder();
107
108
38
  ProgramStateRef StateSuccess = State->BindExpr(
109
38
      Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(0, true));
110
38
  StateSuccess = setErrnoState(StateSuccess, MustNotBeChecked);
111
112
38
  ProgramStateRef StateFailure = State->BindExpr(
113
38
      Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(1, true));
114
38
  StateFailure = setErrnoValue(StateFailure, C, 11, Irrelevant);
115
116
38
  C.addTransition(StateSuccess);
117
38
  C.addTransition(StateFailure);
118
38
}
119
120
void ErrnoTesterChecker::evalSetErrnoIfErrorRange(CheckerContext &C,
121
2
                                                  const CallEvent &Call) {
122
2
  ProgramStateRef State = C.getState();
123
2
  SValBuilder &SVB = C.getSValBuilder();
124
125
2
  ProgramStateRef StateSuccess = State->BindExpr(
126
2
      Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(0, true));
127
2
  StateSuccess = setErrnoState(StateSuccess, MustNotBeChecked);
128
129
2
  ProgramStateRef StateFailure = State->BindExpr(
130
2
      Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(1, true));
131
2
  DefinedOrUnknownSVal ErrnoVal = SVB.conjureSymbolVal(
132
2
      nullptr, Call.getOriginExpr(), C.getLocationContext(), C.blockCount());
133
2
  StateFailure = StateFailure->assume(ErrnoVal, true);
134
2
  assert(StateFailure && "Failed to assume on an initial value.");
135
2
  StateFailure =
136
2
      setErrnoValue(StateFailure, C.getLocationContext(), ErrnoVal, Irrelevant);
137
138
2
  C.addTransition(StateSuccess);
139
2
  C.addTransition(StateFailure);
140
2
}
141
142
void ErrnoTesterChecker::evalSetErrnoCheckState(CheckerContext &C,
143
74
                                                const CallEvent &Call) {
144
74
  ProgramStateRef State = C.getState();
145
74
  SValBuilder &SVB = C.getSValBuilder();
146
147
74
  ProgramStateRef StateSuccess = State->BindExpr(
148
74
      Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(0, true));
149
74
  StateSuccess = setErrnoState(StateSuccess, MustNotBeChecked);
150
151
74
  ProgramStateRef StateFailure1 = State->BindExpr(
152
74
      Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(1, true));
153
74
  StateFailure1 = setErrnoValue(StateFailure1, C, 1, Irrelevant);
154
155
74
  ProgramStateRef StateFailure2 = State->BindExpr(
156
74
      Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(2, true));
157
74
  StateFailure2 = setErrnoValue(StateFailure2, C, 2, MustBeChecked);
158
159
74
  C.addTransition(StateSuccess,
160
74
                  getErrnoNoteTag(C, "Assuming that this function succeeds but "
161
74
                                     "sets 'errno' to an unspecified value."));
162
74
  C.addTransition(StateFailure1);
163
74
  C.addTransition(
164
74
      StateFailure2,
165
74
      getErrnoNoteTag(C, "Assuming that this function returns 2. 'errno' "
166
74
                         "should be checked to test for failure."));
167
74
}
168
169
bool ErrnoTesterChecker::evalCall(const CallEvent &Call,
170
258
                                  CheckerContext &C) const {
171
258
  const EvalFn *Fn = TestCalls.lookup(Call);
172
258
  if (Fn) {
173
122
    (*Fn)(C, Call);
174
122
    return C.isDifferent();
175
122
  }
176
136
  return false;
177
258
}
178
179
6
void ento::registerErrnoTesterChecker(CheckerManager &Mgr) {
180
6
  Mgr.registerChecker<ErrnoTesterChecker>();
181
6
}
182
183
12
bool ento::shouldRegisterErrnoTesterChecker(const CheckerManager &Mgr) {
184
12
  return true;
185
12
}