1343173Sdim//===----- UninitializedPointee.cpp ------------------------------*- C++ -*-==//
2343173Sdim//
3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4353358Sdim// See https://llvm.org/LICENSE.txt for license information.
5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6343173Sdim//
7343173Sdim//===----------------------------------------------------------------------===//
8343173Sdim//
9343173Sdim// This file defines functions and methods for handling pointers and references
10343173Sdim// to reduce the size and complexity of UninitializedObjectChecker.cpp.
11343173Sdim//
12343173Sdim// To read about command line options and documentation about how the checker
13343173Sdim// works, refer to UninitializedObjectChecker.h.
14343173Sdim//
15343173Sdim//===----------------------------------------------------------------------===//
16343173Sdim
17343173Sdim#include "UninitializedObject.h"
18343173Sdim#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
19343173Sdim#include "clang/StaticAnalyzer/Core/Checker.h"
20343173Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
21360784Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
22343173Sdim
23343173Sdimusing namespace clang;
24343173Sdimusing namespace clang::ento;
25343173Sdim
26343173Sdimnamespace {
27343173Sdim
28343173Sdim/// Represents a pointer or a reference field.
29343173Sdimclass LocField final : public FieldNode {
30343173Sdim  /// We'll store whether the pointee or the pointer itself is uninitialited.
31343173Sdim  const bool IsDereferenced;
32343173Sdim
33343173Sdimpublic:
34343173Sdim  LocField(const FieldRegion *FR, const bool IsDereferenced = true)
35343173Sdim      : FieldNode(FR), IsDereferenced(IsDereferenced) {}
36343173Sdim
37343173Sdim  virtual void printNoteMsg(llvm::raw_ostream &Out) const override {
38343173Sdim    if (IsDereferenced)
39343173Sdim      Out << "uninitialized pointee ";
40343173Sdim    else
41343173Sdim      Out << "uninitialized pointer ";
42343173Sdim  }
43343173Sdim
44343173Sdim  virtual void printPrefix(llvm::raw_ostream &Out) const override {}
45343173Sdim
46343173Sdim  virtual void printNode(llvm::raw_ostream &Out) const override {
47343173Sdim    Out << getVariableName(getDecl());
48343173Sdim  }
49343173Sdim
50343173Sdim  virtual void printSeparator(llvm::raw_ostream &Out) const override {
51343173Sdim    if (getDecl()->getType()->isPointerType())
52343173Sdim      Out << "->";
53343173Sdim    else
54343173Sdim      Out << '.';
55343173Sdim  }
56343173Sdim};
57343173Sdim
58343173Sdim/// Represents a nonloc::LocAsInteger or void* field, that point to objects, but
59343173Sdim/// needs to be casted back to its dynamic type for a correct note message.
60343173Sdimclass NeedsCastLocField final : public FieldNode {
61343173Sdim  QualType CastBackType;
62343173Sdim
63343173Sdimpublic:
64343173Sdim  NeedsCastLocField(const FieldRegion *FR, const QualType &T)
65343173Sdim      : FieldNode(FR), CastBackType(T) {}
66343173Sdim
67343173Sdim  virtual void printNoteMsg(llvm::raw_ostream &Out) const override {
68343173Sdim    Out << "uninitialized pointee ";
69343173Sdim  }
70343173Sdim
71343173Sdim  virtual void printPrefix(llvm::raw_ostream &Out) const override {
72343173Sdim    // If this object is a nonloc::LocAsInteger.
73343173Sdim    if (getDecl()->getType()->isIntegerType())
74343173Sdim      Out << "reinterpret_cast";
75343173Sdim    // If this pointer's dynamic type is different then it's static type.
76343173Sdim    else
77343173Sdim      Out << "static_cast";
78343173Sdim    Out << '<' << CastBackType.getAsString() << ">(";
79343173Sdim  }
80343173Sdim
81343173Sdim  virtual void printNode(llvm::raw_ostream &Out) const override {
82343173Sdim    Out << getVariableName(getDecl()) << ')';
83343173Sdim  }
84343173Sdim
85343173Sdim  virtual void printSeparator(llvm::raw_ostream &Out) const override {
86343173Sdim    Out << "->";
87343173Sdim  }
88343173Sdim};
89343173Sdim
90343173Sdim/// Represents a Loc field that points to itself.
91343173Sdimclass CyclicLocField final : public FieldNode {
92343173Sdim
93343173Sdimpublic:
94343173Sdim  CyclicLocField(const FieldRegion *FR) : FieldNode(FR) {}
95343173Sdim
96343173Sdim  virtual void printNoteMsg(llvm::raw_ostream &Out) const override {
97343173Sdim    Out << "object references itself ";
98343173Sdim  }
99343173Sdim
100343173Sdim  virtual void printPrefix(llvm::raw_ostream &Out) const override {}
101343173Sdim
102343173Sdim  virtual void printNode(llvm::raw_ostream &Out) const override {
103343173Sdim    Out << getVariableName(getDecl());
104343173Sdim  }
105343173Sdim
106343173Sdim  virtual void printSeparator(llvm::raw_ostream &Out) const override {
107343173Sdim    llvm_unreachable("CyclicLocField objects must be the last node of the "
108343173Sdim                     "fieldchain!");
109343173Sdim  }
110343173Sdim};
111343173Sdim
112343173Sdim} // end of anonymous namespace
113343173Sdim
114343173Sdim// Utility function declarations.
115343173Sdim
116343173Sdimstruct DereferenceInfo {
117343173Sdim  const TypedValueRegion *R;
118343173Sdim  const bool NeedsCastBack;
119343173Sdim  const bool IsCyclic;
120343173Sdim  DereferenceInfo(const TypedValueRegion *R, bool NCB, bool IC)
121343173Sdim      : R(R), NeedsCastBack(NCB), IsCyclic(IC) {}
122343173Sdim};
123343173Sdim
124343173Sdim/// Dereferences \p FR and returns with the pointee's region, and whether it
125343173Sdim/// needs to be casted back to it's location type. If for whatever reason
126343173Sdim/// dereferencing fails, returns with None.
127343173Sdimstatic llvm::Optional<DereferenceInfo> dereference(ProgramStateRef State,
128343173Sdim                                                   const FieldRegion *FR);
129343173Sdim
130343173Sdim/// Returns whether \p T can be (transitively) dereferenced to a void pointer
131343173Sdim/// type (void*, void**, ...).
132343173Sdimstatic bool isVoidPointer(QualType T);
133343173Sdim
134343173Sdim//===----------------------------------------------------------------------===//
135343173Sdim//                   Methods for FindUninitializedFields.
136343173Sdim//===----------------------------------------------------------------------===//
137343173Sdim
138343173Sdimbool FindUninitializedFields::isDereferencableUninit(
139343173Sdim    const FieldRegion *FR, FieldChainInfo LocalChain) {
140343173Sdim
141343173Sdim  SVal V = State->getSVal(FR);
142343173Sdim
143343173Sdim  assert((isDereferencableType(FR->getDecl()->getType()) ||
144343173Sdim          V.getAs<nonloc::LocAsInteger>()) &&
145343173Sdim         "This method only checks dereferenceable objects!");
146343173Sdim
147343173Sdim  if (V.isUnknown() || V.getAs<loc::ConcreteInt>()) {
148343173Sdim    IsAnyFieldInitialized = true;
149343173Sdim    return false;
150343173Sdim  }
151343173Sdim
152343173Sdim  if (V.isUndef()) {
153343173Sdim    return addFieldToUninits(
154343173Sdim        LocalChain.add(LocField(FR, /*IsDereferenced*/ false)), FR);
155343173Sdim  }
156343173Sdim
157343173Sdim  if (!Opts.CheckPointeeInitialization) {
158343173Sdim    IsAnyFieldInitialized = true;
159343173Sdim    return false;
160343173Sdim  }
161343173Sdim
162343173Sdim  // At this point the pointer itself is initialized and points to a valid
163343173Sdim  // location, we'll now check the pointee.
164343173Sdim  llvm::Optional<DereferenceInfo> DerefInfo = dereference(State, FR);
165343173Sdim  if (!DerefInfo) {
166343173Sdim    IsAnyFieldInitialized = true;
167343173Sdim    return false;
168343173Sdim  }
169343173Sdim
170343173Sdim  if (DerefInfo->IsCyclic)
171343173Sdim    return addFieldToUninits(LocalChain.add(CyclicLocField(FR)), FR);
172343173Sdim
173343173Sdim  const TypedValueRegion *R = DerefInfo->R;
174343173Sdim  const bool NeedsCastBack = DerefInfo->NeedsCastBack;
175343173Sdim
176343173Sdim  QualType DynT = R->getLocationType();
177343173Sdim  QualType PointeeT = DynT->getPointeeType();
178343173Sdim
179343173Sdim  if (PointeeT->isStructureOrClassType()) {
180343173Sdim    if (NeedsCastBack)
181343173Sdim      return isNonUnionUninit(R, LocalChain.add(NeedsCastLocField(FR, DynT)));
182343173Sdim    return isNonUnionUninit(R, LocalChain.add(LocField(FR)));
183343173Sdim  }
184343173Sdim
185343173Sdim  if (PointeeT->isUnionType()) {
186343173Sdim    if (isUnionUninit(R)) {
187343173Sdim      if (NeedsCastBack)
188343173Sdim        return addFieldToUninits(LocalChain.add(NeedsCastLocField(FR, DynT)),
189343173Sdim                                 R);
190343173Sdim      return addFieldToUninits(LocalChain.add(LocField(FR)), R);
191343173Sdim    } else {
192343173Sdim      IsAnyFieldInitialized = true;
193343173Sdim      return false;
194343173Sdim    }
195343173Sdim  }
196343173Sdim
197343173Sdim  if (PointeeT->isArrayType()) {
198343173Sdim    IsAnyFieldInitialized = true;
199343173Sdim    return false;
200343173Sdim  }
201343173Sdim
202343173Sdim  assert((isPrimitiveType(PointeeT) || isDereferencableType(PointeeT)) &&
203343173Sdim         "At this point FR must either have a primitive dynamic type, or it "
204343173Sdim         "must be a null, undefined, unknown or concrete pointer!");
205343173Sdim
206343173Sdim  SVal PointeeV = State->getSVal(R);
207343173Sdim
208343173Sdim  if (isPrimitiveUninit(PointeeV)) {
209343173Sdim    if (NeedsCastBack)
210343173Sdim      return addFieldToUninits(LocalChain.add(NeedsCastLocField(FR, DynT)), R);
211343173Sdim    return addFieldToUninits(LocalChain.add(LocField(FR)), R);
212343173Sdim  }
213343173Sdim
214343173Sdim  IsAnyFieldInitialized = true;
215343173Sdim  return false;
216343173Sdim}
217343173Sdim
218343173Sdim//===----------------------------------------------------------------------===//
219343173Sdim//                           Utility functions.
220343173Sdim//===----------------------------------------------------------------------===//
221343173Sdim
222343173Sdimstatic llvm::Optional<DereferenceInfo> dereference(ProgramStateRef State,
223343173Sdim                                                   const FieldRegion *FR) {
224343173Sdim
225343173Sdim  llvm::SmallSet<const TypedValueRegion *, 5> VisitedRegions;
226343173Sdim
227343173Sdim  SVal V = State->getSVal(FR);
228343173Sdim  assert(V.getAsRegion() && "V must have an underlying region!");
229343173Sdim
230343173Sdim  // If the static type of the field is a void pointer, or it is a
231343173Sdim  // nonloc::LocAsInteger, we need to cast it back to the dynamic type before
232343173Sdim  // dereferencing.
233343173Sdim  bool NeedsCastBack = isVoidPointer(FR->getDecl()->getType()) ||
234343173Sdim                       V.getAs<nonloc::LocAsInteger>();
235343173Sdim
236343173Sdim  // The region we'd like to acquire.
237343173Sdim  const auto *R = V.getAsRegion()->getAs<TypedValueRegion>();
238343173Sdim  if (!R)
239343173Sdim    return None;
240343173Sdim
241343173Sdim  VisitedRegions.insert(R);
242343173Sdim
243343173Sdim  // We acquire the dynamic type of R,
244343173Sdim  QualType DynT = R->getLocationType();
245343173Sdim
246343173Sdim  while (const MemRegion *Tmp = State->getSVal(R, DynT).getAsRegion()) {
247343173Sdim
248343173Sdim    R = Tmp->getAs<TypedValueRegion>();
249343173Sdim    if (!R)
250343173Sdim      return None;
251343173Sdim
252343173Sdim    // We found a cyclic pointer, like int *ptr = (int *)&ptr.
253343173Sdim    if (!VisitedRegions.insert(R).second)
254343173Sdim      return DereferenceInfo{R, NeedsCastBack, /*IsCyclic*/ true};
255343173Sdim
256343173Sdim    DynT = R->getLocationType();
257343173Sdim    // In order to ensure that this loop terminates, we're also checking the
258343173Sdim    // dynamic type of R, since type hierarchy is finite.
259343173Sdim    if (isDereferencableType(DynT->getPointeeType()))
260343173Sdim      break;
261343173Sdim  }
262343173Sdim
263360784Sdim  while (isa<CXXBaseObjectRegion>(R)) {
264343173Sdim    NeedsCastBack = true;
265360784Sdim    const auto *SuperR = dyn_cast<TypedValueRegion>(R->getSuperRegion());
266360784Sdim    if (!SuperR)
267360784Sdim      break;
268343173Sdim
269360784Sdim    R = SuperR;
270343173Sdim  }
271343173Sdim
272343173Sdim  return DereferenceInfo{R, NeedsCastBack, /*IsCyclic*/ false};
273343173Sdim}
274343173Sdim
275343173Sdimstatic bool isVoidPointer(QualType T) {
276343173Sdim  while (!T.isNull()) {
277343173Sdim    if (T->isVoidPointerType())
278343173Sdim      return true;
279343173Sdim    T = T->getPointeeType();
280343173Sdim  }
281343173Sdim  return false;
282343173Sdim}
283