xref: /llvm-project/clang/lib/StaticAnalyzer/Checkers/ErrnoTesterChecker.cpp (revision 29b512ba322cb6dd2c45d5e07645e20db47fad0d)
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 "Errno.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 
21 using namespace clang;
22 using namespace ento;
23 
24 namespace {
25 
26 class ErrnoTesterChecker : public Checker<eval::Call> {
27 public:
28   bool evalCall(const CallEvent &Call, CheckerContext &C) const;
29 
30 private:
31   static void evalSetErrno(CheckerContext &C, const CallEvent &Call);
32   static void evalGetErrno(CheckerContext &C, const CallEvent &Call);
33   static void evalSetErrnoIfError(CheckerContext &C, const CallEvent &Call);
34   static void evalSetErrnoIfErrorRange(CheckerContext &C,
35                                        const CallEvent &Call);
36 
37   using EvalFn = std::function<void(CheckerContext &, const CallEvent &)>;
38   const CallDescriptionMap<EvalFn> TestCalls{
39       {{"ErrnoTesterChecker_setErrno", 1}, &ErrnoTesterChecker::evalSetErrno},
40       {{"ErrnoTesterChecker_getErrno", 0}, &ErrnoTesterChecker::evalGetErrno},
41       {{"ErrnoTesterChecker_setErrnoIfError", 0},
42        &ErrnoTesterChecker::evalSetErrnoIfError},
43       {{"ErrnoTesterChecker_setErrnoIfErrorRange", 0},
44        &ErrnoTesterChecker::evalSetErrnoIfErrorRange}};
45 };
46 
47 } // namespace
48 
49 void ErrnoTesterChecker::evalSetErrno(CheckerContext &C,
50                                       const CallEvent &Call) {
51   C.addTransition(errno_modeling::setErrnoValue(
52       C.getState(), C.getLocationContext(), Call.getArgSVal(0)));
53 }
54 
55 void ErrnoTesterChecker::evalGetErrno(CheckerContext &C,
56                                       const CallEvent &Call) {
57   ProgramStateRef State = C.getState();
58 
59   Optional<SVal> ErrnoVal = errno_modeling::getErrnoValue(State);
60   assert(ErrnoVal && "Errno value should be available.");
61   State =
62       State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), *ErrnoVal);
63 
64   C.addTransition(State);
65 }
66 
67 void ErrnoTesterChecker::evalSetErrnoIfError(CheckerContext &C,
68                                              const CallEvent &Call) {
69   ProgramStateRef State = C.getState();
70   SValBuilder &SVB = C.getSValBuilder();
71 
72   ProgramStateRef StateSuccess = State->BindExpr(
73       Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(0, true));
74 
75   ProgramStateRef StateFailure = State->BindExpr(
76       Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(1, true));
77   StateFailure = errno_modeling::setErrnoValue(StateFailure, C, 11);
78 
79   C.addTransition(StateSuccess);
80   C.addTransition(StateFailure);
81 }
82 
83 void ErrnoTesterChecker::evalSetErrnoIfErrorRange(CheckerContext &C,
84                                                   const CallEvent &Call) {
85   ProgramStateRef State = C.getState();
86   SValBuilder &SVB = C.getSValBuilder();
87 
88   ProgramStateRef StateSuccess = State->BindExpr(
89       Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(0, true));
90 
91   ProgramStateRef StateFailure = State->BindExpr(
92       Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(1, true));
93   DefinedOrUnknownSVal ErrnoVal = SVB.conjureSymbolVal(
94       nullptr, Call.getOriginExpr(), C.getLocationContext(), C.blockCount());
95   StateFailure = StateFailure->assume(ErrnoVal, true);
96   assert(StateFailure && "Failed to assume on an initial value.");
97   StateFailure = errno_modeling::setErrnoValue(
98       StateFailure, C.getLocationContext(), ErrnoVal);
99 
100   C.addTransition(StateSuccess);
101   C.addTransition(StateFailure);
102 }
103 
104 bool ErrnoTesterChecker::evalCall(const CallEvent &Call,
105                                   CheckerContext &C) const {
106   const EvalFn *Fn = TestCalls.lookup(Call);
107   if (Fn) {
108     (*Fn)(C, Call);
109     return C.isDifferent();
110   }
111   return false;
112 }
113 
114 void ento::registerErrnoTesterChecker(CheckerManager &Mgr) {
115   Mgr.registerChecker<ErrnoTesterChecker>();
116 }
117 
118 bool ento::shouldRegisterErrnoTesterChecker(const CheckerManager &Mgr) {
119   return true;
120 }
121