brintos

brintos / llvm-project-archived public Read only

0
0
Text · 8.0 KiB · 74eda4e Raw
187 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8//9// This defines ErrnoTesterChecker, which is used to test functionality of the10// 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 on41  /// failure. On the success case \c errno is not allowed to be used (may be42  /// undefined). On the failure case \c errno is set to a fixed value 11 and43  /// 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 is47  /// 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 value58  ///   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 only63  ///   be checked by reading from \c errno. The value of \c errno is changed by64  ///   the function only at failure, the user should set \c errno to 0 before65  ///   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 (or67  ///   \c LONG_MAX). This case applies only if \c LONG_MIN or \c LONG_MAX is68  ///   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} // namespace86 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));133  DefinedOrUnknownSVal ErrnoVal = SVB.conjureSymbolVal(Call, C.blockCount());134  StateFailure = StateFailure->assume(ErrnoVal, true);135  assert(StateFailure && "Failed to assume on an initial value.");136  StateFailure =137      setErrnoValue(StateFailure, C.getLocationContext(), ErrnoVal, Irrelevant);138 139  C.addTransition(StateSuccess);140  C.addTransition(StateFailure);141}142 143void ErrnoTesterChecker::evalSetErrnoCheckState(CheckerContext &C,144                                                const CallEvent &Call) {145  ProgramStateRef State = C.getState();146  SValBuilder &SVB = C.getSValBuilder();147 148  ProgramStateRef StateSuccess = State->BindExpr(149      Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(0, true));150  StateSuccess = setErrnoState(StateSuccess, MustNotBeChecked);151 152  ProgramStateRef StateFailure1 = State->BindExpr(153      Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(1, true));154  StateFailure1 = setErrnoValue(StateFailure1, C, 1, Irrelevant);155 156  ProgramStateRef StateFailure2 = State->BindExpr(157      Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(2, true));158  StateFailure2 = setErrnoValue(StateFailure2, C, 2, MustBeChecked);159 160  C.addTransition(StateSuccess,161                  getErrnoNoteTag(C, "Assuming that this function succeeds but "162                                     "sets 'errno' to an unspecified value."));163  C.addTransition(StateFailure1);164  C.addTransition(165      StateFailure2,166      getErrnoNoteTag(C, "Assuming that this function returns 2. 'errno' "167                         "should be checked to test for failure."));168}169 170bool ErrnoTesterChecker::evalCall(const CallEvent &Call,171                                  CheckerContext &C) const {172  const EvalFn *Fn = TestCalls.lookup(Call);173  if (Fn) {174    (*Fn)(C, Call);175    return C.isDifferent();176  }177  return false;178}179 180void ento::registerErrnoTesterChecker(CheckerManager &Mgr) {181  Mgr.registerChecker<ErrnoTesterChecker>();182}183 184bool ento::shouldRegisterErrnoTesterChecker(const CheckerManager &Mgr) {185  return true;186}187