clang  10.0.0svn
ReturnValueChecker.cpp
Go to the documentation of this file.
1 //===- ReturnValueChecker - Applies guaranteed return values ----*- 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 ReturnValueChecker, which checks for calls with guaranteed
10 // boolean return value. It ensures the return value of each function call.
11 //
12 //===----------------------------------------------------------------------===//
13 
19 #include "llvm/ADT/Optional.h"
20 #include "llvm/ADT/SmallVector.h"
21 
22 using namespace clang;
23 using namespace ento;
24 
25 namespace {
26 class ReturnValueChecker : public Checker<check::PostCall, check::EndFunction> {
27 public:
28  // It sets the predefined invariant ('CDM') if the current call not break it.
29  void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
30 
31  // It reports whether a predefined invariant ('CDM') is broken.
32  void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const;
33 
34 private:
35  // The pairs are in the following form: {{{class, call}}, return value}
36  const CallDescriptionMap<bool> CDM = {
37  // These are known in the LLVM project: 'Error()'
38  {{{"ARMAsmParser", "Error"}}, true},
39  {{{"HexagonAsmParser", "Error"}}, true},
40  {{{"LLLexer", "Error"}}, true},
41  {{{"LLParser", "Error"}}, true},
42  {{{"MCAsmParser", "Error"}}, true},
43  {{{"MCAsmParserExtension", "Error"}}, true},
44  {{{"TGParser", "Error"}}, true},
45  {{{"X86AsmParser", "Error"}}, true},
46  // 'TokError()'
47  {{{"LLParser", "TokError"}}, true},
48  {{{"MCAsmParser", "TokError"}}, true},
49  {{{"MCAsmParserExtension", "TokError"}}, true},
50  {{{"TGParser", "TokError"}}, true},
51  // 'error()'
52  {{{"MIParser", "error"}}, true},
53  {{{"WasmAsmParser", "error"}}, true},
54  {{{"WebAssemblyAsmParser", "error"}}, true},
55  // Other
56  {{{"AsmParser", "printError"}}, true}};
57 };
58 } // namespace
59 
60 static std::string getName(const CallEvent &Call) {
61  std::string Name = "";
62  if (const auto *MD = dyn_cast<CXXMethodDecl>(Call.getDecl()))
63  if (const CXXRecordDecl *RD = MD->getParent())
64  Name += RD->getNameAsString() + "::";
65 
66  Name += Call.getCalleeIdentifier()->getName();
67  return Name;
68 }
69 
70 // The predefinitions ('CDM') could break due to the ever growing code base.
71 // Check for the expected invariants and see whether they apply.
72 static Optional<bool> isInvariantBreak(bool ExpectedValue, SVal ReturnV,
73  CheckerContext &C) {
74  auto ReturnDV = ReturnV.getAs<DefinedOrUnknownSVal>();
75  if (!ReturnDV)
76  return None;
77 
78  if (ExpectedValue)
79  return C.getState()->isNull(*ReturnDV).isConstrainedTrue();
80 
81  return C.getState()->isNull(*ReturnDV).isConstrainedFalse();
82 }
83 
84 void ReturnValueChecker::checkPostCall(const CallEvent &Call,
85  CheckerContext &C) const {
86  const bool *RawExpectedValue = CDM.lookup(Call);
87  if (!RawExpectedValue)
88  return;
89 
90  SVal ReturnV = Call.getReturnValue();
91  bool ExpectedValue = *RawExpectedValue;
92  Optional<bool> IsInvariantBreak = isInvariantBreak(ExpectedValue, ReturnV, C);
93  if (!IsInvariantBreak)
94  return;
95 
96  // If the invariant is broken it is reported by 'checkEndFunction()'.
97  if (*IsInvariantBreak)
98  return;
99 
100  std::string Name = getName(Call);
101  const NoteTag *CallTag = C.getNoteTag(
102  [Name, ExpectedValue](BugReport &) -> std::string {
103  SmallString<128> Msg;
104  llvm::raw_svector_ostream Out(Msg);
105 
106  Out << '\'' << Name << "' returns "
107  << (ExpectedValue ? "true" : "false");
108  return Out.str();
109  },
110  /*IsPrunable=*/true);
111 
112  ProgramStateRef State = C.getState();
113  State = State->assume(ReturnV.castAs<DefinedOrUnknownSVal>(), ExpectedValue);
114  C.addTransition(State, CallTag);
115 }
116 
117 void ReturnValueChecker::checkEndFunction(const ReturnStmt *RS,
118  CheckerContext &C) const {
119  if (!RS || !RS->getRetValue())
120  return;
121 
122  // We cannot get the caller in the top-frame.
123  const StackFrameContext *SFC = C.getStackFrame();
124  if (C.getStackFrame()->inTopFrame())
125  return;
126 
127  ProgramStateRef State = C.getState();
128  CallEventManager &CMgr = C.getStateManager().getCallEventManager();
129  CallEventRef<> Call = CMgr.getCaller(SFC, State);
130  if (!Call)
131  return;
132 
133  const bool *RawExpectedValue = CDM.lookup(*Call);
134  if (!RawExpectedValue)
135  return;
136 
137  SVal ReturnV = State->getSVal(RS->getRetValue(), C.getLocationContext());
138  bool ExpectedValue = *RawExpectedValue;
139  Optional<bool> IsInvariantBreak = isInvariantBreak(ExpectedValue, ReturnV, C);
140  if (!IsInvariantBreak)
141  return;
142 
143  // If the invariant is appropriate it is reported by 'checkPostCall()'.
144  if (!*IsInvariantBreak)
145  return;
146 
147  std::string Name = getName(*Call);
148  const NoteTag *CallTag = C.getNoteTag(
149  [Name, ExpectedValue](BugReport &BR) -> std::string {
150  SmallString<128> Msg;
151  llvm::raw_svector_ostream Out(Msg);
152 
153  // The following is swapped because the invariant is broken.
154  Out << '\'' << Name << "' returns "
155  << (ExpectedValue ? "false" : "true");
156 
157  return Out.str();
158  },
159  /*IsPrunable=*/false);
160 
161  C.addTransition(State, CallTag);
162 }
163 
164 void ento::registerReturnValueChecker(CheckerManager &Mgr) {
165  Mgr.registerChecker<ReturnValueChecker>();
166 }
167 
168 bool ento::shouldRegisterReturnValueChecker(const LangOptions &LO) {
169  return true;
170 }
Manages the lifetime of CallEvent objects.
Definition: CallEvent.h:1148
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
static Optional< bool > isInvariantBreak(bool ExpectedValue, SVal ReturnV, CheckerContext &C)
LineState State
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Definition: LangOptions.h:49
CallEventRef getCaller(const StackFrameContext *CalleeCtx, ProgramStateRef State)
Gets an outside caller given a callee context.
Definition: CallEvent.cpp:1380
DeclContext * getParent()
getParent - Returns the containing DeclContext.
Definition: DeclBase.h:1779
ReturnStmt - This represents a return, optionally of an expression: return; return 4;...
Definition: Stmt.h:2620
Expr * getRetValue()
Definition: Stmt.h:2653
Dataflow Directional Tag Classes.
static std::string getName(const CallEvent &Call)
const StackFrameContext * getStackFrame() const
Represents a C++ struct/union/class.
Definition: DeclCXX.h:255
An immutable map from CallDescriptions to arbitrary data.
Definition: CallEvent.h:1110