aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/libs/clang16/lib/StaticAnalyzer/Checkers/ErrnoTesterChecker.cpp
blob: c46ebee0c94ff48eefc47295fc2a3976d2c3ae6b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
//=== ErrnoTesterChecker.cpp ------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This defines ErrnoTesterChecker, which is used to test functionality of the
// errno_check API.
//
//===----------------------------------------------------------------------===//

#include "ErrnoModeling.h"
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include <optional>

using namespace clang;
using namespace ento;
using namespace errno_modeling;

namespace {

class ErrnoTesterChecker : public Checker<eval::Call> {
public:
  bool evalCall(const CallEvent &Call, CheckerContext &C) const;

private:
  /// Evaluate function \code void ErrnoTesterChecker_setErrno(int) \endcode.
  /// Set value of \c errno to the argument.
  static void evalSetErrno(CheckerContext &C, const CallEvent &Call);
  /// Evaluate function \code int ErrnoTesterChecker_getErrno() \endcode.
  /// Return the value of \c errno.
  static void evalGetErrno(CheckerContext &C, const CallEvent &Call);
  /// Evaluate function \code int ErrnoTesterChecker_setErrnoIfError() \endcode.
  /// Simulate a standard library function tha returns 0 on success and 1 on
  /// failure. On the success case \c errno is not allowed to be used (may be
  /// undefined). On the failure case \c errno is set to a fixed value 11 and
  /// is not needed to be checked.
  static void evalSetErrnoIfError(CheckerContext &C, const CallEvent &Call);
  /// Evaluate function \code int ErrnoTesterChecker_setErrnoIfErrorRange()
  /// \endcode. Same as \c ErrnoTesterChecker_setErrnoIfError but \c errno is
  /// set to a range (to be nonzero) at the failure case.
  static void evalSetErrnoIfErrorRange(CheckerContext &C,
                                       const CallEvent &Call);
  /// Evaluate function \code int ErrnoTesterChecker_setErrnoCheckState()
  /// \endcode. This function simulates the following:
  /// - Return 0 and leave \c errno with undefined value.
  ///   This is the case of a successful standard function call.
  ///   For example if \c ftell returns not -1.
  /// - Return 1 and sets \c errno to a specific error code (1).
  ///   This is the case of a failed standard function call.
  ///   The function indicates the failure by a special return value
  ///   that is returned only at failure.
  ///   \c errno can be checked but it is not required.
  ///   For example if \c ftell returns -1.
  /// - Return 2 and may set errno to a value (actually it does not set it).
  ///   This is the case of a standard function call where the failure can only
  ///   be checked by reading from \c errno. The value of \c errno is changed by
  ///   the function only at failure, the user should set \c errno to 0 before
  ///   the call (\c ErrnoChecker does not check for this rule).
  ///   \c strtol is an example of this case, if it returns \c LONG_MIN (or
  ///   \c LONG_MAX). This case applies only if \c LONG_MIN or \c LONG_MAX is
  ///   returned, otherwise the first case in this list applies.
  static void evalSetErrnoCheckState(CheckerContext &C, const CallEvent &Call);

  using EvalFn = std::function<void(CheckerContext &, const CallEvent &)>;
  const CallDescriptionMap<EvalFn> TestCalls{
      {{{"ErrnoTesterChecker_setErrno"}, 1}, &ErrnoTesterChecker::evalSetErrno},
      {{{"ErrnoTesterChecker_getErrno"}, 0}, &ErrnoTesterChecker::evalGetErrno},
      {{{"ErrnoTesterChecker_setErrnoIfError"}, 0},
       &ErrnoTesterChecker::evalSetErrnoIfError},
      {{{"ErrnoTesterChecker_setErrnoIfErrorRange"}, 0},
       &ErrnoTesterChecker::evalSetErrnoIfErrorRange},
      {{{"ErrnoTesterChecker_setErrnoCheckState"}, 0},
       &ErrnoTesterChecker::evalSetErrnoCheckState}};
};

} // namespace

void ErrnoTesterChecker::evalSetErrno(CheckerContext &C,
                                      const CallEvent &Call) {
  C.addTransition(setErrnoValue(C.getState(), C.getLocationContext(),
                                Call.getArgSVal(0), Irrelevant));
}

void ErrnoTesterChecker::evalGetErrno(CheckerContext &C,
                                      const CallEvent &Call) {
  ProgramStateRef State = C.getState();

  std::optional<SVal> ErrnoVal = getErrnoValue(State);
  assert(ErrnoVal && "Errno value should be available.");
  State =
      State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), *ErrnoVal);

  C.addTransition(State);
}

void ErrnoTesterChecker::evalSetErrnoIfError(CheckerContext &C,
                                             const CallEvent &Call) {
  ProgramStateRef State = C.getState();
  SValBuilder &SVB = C.getSValBuilder();

  ProgramStateRef StateSuccess = State->BindExpr(
      Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(0, true));
  StateSuccess = setErrnoState(StateSuccess, MustNotBeChecked);

  ProgramStateRef StateFailure = State->BindExpr(
      Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(1, true));
  StateFailure = setErrnoValue(StateFailure, C, 11, Irrelevant);

  C.addTransition(StateSuccess);
  C.addTransition(StateFailure);
}

void ErrnoTesterChecker::evalSetErrnoIfErrorRange(CheckerContext &C,
                                                  const CallEvent &Call) {
  ProgramStateRef State = C.getState();
  SValBuilder &SVB = C.getSValBuilder();

  ProgramStateRef StateSuccess = State->BindExpr(
      Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(0, true));
  StateSuccess = setErrnoState(StateSuccess, MustNotBeChecked);

  ProgramStateRef StateFailure = State->BindExpr(
      Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(1, true));
  DefinedOrUnknownSVal ErrnoVal = SVB.conjureSymbolVal(
      nullptr, Call.getOriginExpr(), C.getLocationContext(), C.blockCount());
  StateFailure = StateFailure->assume(ErrnoVal, true);
  assert(StateFailure && "Failed to assume on an initial value.");
  StateFailure =
      setErrnoValue(StateFailure, C.getLocationContext(), ErrnoVal, Irrelevant);

  C.addTransition(StateSuccess);
  C.addTransition(StateFailure);
}

void ErrnoTesterChecker::evalSetErrnoCheckState(CheckerContext &C,
                                                const CallEvent &Call) {
  ProgramStateRef State = C.getState();
  SValBuilder &SVB = C.getSValBuilder();

  ProgramStateRef StateSuccess = State->BindExpr(
      Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(0, true));
  StateSuccess = setErrnoState(StateSuccess, MustNotBeChecked);

  ProgramStateRef StateFailure1 = State->BindExpr(
      Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(1, true));
  StateFailure1 = setErrnoValue(StateFailure1, C, 1, Irrelevant);

  ProgramStateRef StateFailure2 = State->BindExpr(
      Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(2, true));
  StateFailure2 = setErrnoValue(StateFailure2, C, 2, MustBeChecked);

  C.addTransition(StateSuccess,
                  getErrnoNoteTag(C, "Assuming that this function succeeds but "
                                     "sets 'errno' to an unspecified value."));
  C.addTransition(StateFailure1);
  C.addTransition(
      StateFailure2,
      getErrnoNoteTag(C, "Assuming that this function returns 2. 'errno' "
                         "should be checked to test for failure."));
}

bool ErrnoTesterChecker::evalCall(const CallEvent &Call,
                                  CheckerContext &C) const {
  const EvalFn *Fn = TestCalls.lookup(Call);
  if (Fn) {
    (*Fn)(C, Call);
    return C.isDifferent();
  }
  return false;
}

void ento::registerErrnoTesterChecker(CheckerManager &Mgr) {
  Mgr.registerChecker<ErrnoTesterChecker>();
}

bool ento::shouldRegisterErrnoTesterChecker(const CheckerManager &Mgr) {
  return true;
}