1//===----- UninitializedPointee.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 file defines functions and methods for handling pointers and references
10// to reduce the size and complexity of UninitializedObjectChecker.cpp.
11//
12// To read about command line options and documentation about how the checker
13// works, refer to UninitializedObjectChecker.h.
14//
15//===----------------------------------------------------------------------===//
16
17#include "UninitializedObject.h"
18#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
19#include "clang/StaticAnalyzer/Core/Checker.h"
20#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
21#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
22#include <optional>
23
24using namespace clang;
25using namespace clang::ento;
26
27namespace {
28
29/// Represents a pointer or a reference field.
30class LocField final : public FieldNode {
31  /// We'll store whether the pointee or the pointer itself is uninitialited.
32  const bool IsDereferenced;
33
34public:
35  LocField(const FieldRegion *FR, const bool IsDereferenced = true)
36      : FieldNode(FR), IsDereferenced(IsDereferenced) {}
37
38  void printNoteMsg(llvm::raw_ostream &Out) const override {
39    if (IsDereferenced)
40      Out << "uninitialized pointee ";
41    else
42      Out << "uninitialized pointer ";
43  }
44
45  void printPrefix(llvm::raw_ostream &Out) const override {}
46
47  void printNode(llvm::raw_ostream &Out) const override {
48    Out << getVariableName(getDecl());
49  }
50
51  void printSeparator(llvm::raw_ostream &Out) const override {
52    if (getDecl()->getType()->isPointerType())
53      Out << "->";
54    else
55      Out << '.';
56  }
57};
58
59/// Represents a nonloc::LocAsInteger or void* field, that point to objects, but
60/// needs to be casted back to its dynamic type for a correct note message.
61class NeedsCastLocField final : public FieldNode {
62  QualType CastBackType;
63
64public:
65  NeedsCastLocField(const FieldRegion *FR, const QualType &T)
66      : FieldNode(FR), CastBackType(T) {}
67
68  void printNoteMsg(llvm::raw_ostream &Out) const override {
69    Out << "uninitialized pointee ";
70  }
71
72  void printPrefix(llvm::raw_ostream &Out) const override {
73    // If this object is a nonloc::LocAsInteger.
74    if (getDecl()->getType()->isIntegerType())
75      Out << "reinterpret_cast";
76    // If this pointer's dynamic type is different then it's static type.
77    else
78      Out << "static_cast";
79    Out << '<' << CastBackType.getAsString() << ">(";
80  }
81
82  void printNode(llvm::raw_ostream &Out) const override {
83    Out << getVariableName(getDecl()) << ')';
84  }
85
86  void printSeparator(llvm::raw_ostream &Out) const override { Out << "->"; }
87};
88
89/// Represents a Loc field that points to itself.
90class CyclicLocField final : public FieldNode {
91
92public:
93  CyclicLocField(const FieldRegion *FR) : FieldNode(FR) {}
94
95  void printNoteMsg(llvm::raw_ostream &Out) const override {
96    Out << "object references itself ";
97  }
98
99  void printPrefix(llvm::raw_ostream &Out) const override {}
100
101  void printNode(llvm::raw_ostream &Out) const override {
102    Out << getVariableName(getDecl());
103  }
104
105  void printSeparator(llvm::raw_ostream &Out) const override {
106    llvm_unreachable("CyclicLocField objects must be the last node of the "
107                     "fieldchain!");
108  }
109};
110
111} // end of anonymous namespace
112
113// Utility function declarations.
114
115struct DereferenceInfo {
116  const TypedValueRegion *R;
117  const bool NeedsCastBack;
118  const bool IsCyclic;
119  DereferenceInfo(const TypedValueRegion *R, bool NCB, bool IC)
120      : R(R), NeedsCastBack(NCB), IsCyclic(IC) {}
121};
122
123/// Dereferences \p FR and returns with the pointee's region, and whether it
124/// needs to be casted back to it's location type. If for whatever reason
125/// dereferencing fails, returns std::nullopt.
126static std::optional<DereferenceInfo> dereference(ProgramStateRef State,
127                                                  const FieldRegion *FR);
128
129/// Returns whether \p T can be (transitively) dereferenced to a void pointer
130/// type (void*, void**, ...).
131static bool isVoidPointer(QualType T);
132
133//===----------------------------------------------------------------------===//
134//                   Methods for FindUninitializedFields.
135//===----------------------------------------------------------------------===//
136
137bool FindUninitializedFields::isDereferencableUninit(
138    const FieldRegion *FR, FieldChainInfo LocalChain) {
139
140  SVal V = State->getSVal(FR);
141
142  assert((isDereferencableType(FR->getDecl()->getType()) ||
143          isa<nonloc::LocAsInteger>(V)) &&
144         "This method only checks dereferenceable objects!");
145
146  if (V.isUnknown() || isa<loc::ConcreteInt>(V)) {
147    IsAnyFieldInitialized = true;
148    return false;
149  }
150
151  if (V.isUndef()) {
152    return addFieldToUninits(
153        LocalChain.add(LocField(FR, /*IsDereferenced*/ false)), FR);
154  }
155
156  if (!Opts.CheckPointeeInitialization) {
157    IsAnyFieldInitialized = true;
158    return false;
159  }
160
161  // At this point the pointer itself is initialized and points to a valid
162  // location, we'll now check the pointee.
163  std::optional<DereferenceInfo> DerefInfo = dereference(State, FR);
164  if (!DerefInfo) {
165    IsAnyFieldInitialized = true;
166    return false;
167  }
168
169  if (DerefInfo->IsCyclic)
170    return addFieldToUninits(LocalChain.add(CyclicLocField(FR)), FR);
171
172  const TypedValueRegion *R = DerefInfo->R;
173  const bool NeedsCastBack = DerefInfo->NeedsCastBack;
174
175  QualType DynT = R->getLocationType();
176  QualType PointeeT = DynT->getPointeeType();
177
178  if (PointeeT->isStructureOrClassType()) {
179    if (NeedsCastBack)
180      return isNonUnionUninit(R, LocalChain.add(NeedsCastLocField(FR, DynT)));
181    return isNonUnionUninit(R, LocalChain.add(LocField(FR)));
182  }
183
184  if (PointeeT->isUnionType()) {
185    if (isUnionUninit(R)) {
186      if (NeedsCastBack)
187        return addFieldToUninits(LocalChain.add(NeedsCastLocField(FR, DynT)),
188                                 R);
189      return addFieldToUninits(LocalChain.add(LocField(FR)), R);
190    } else {
191      IsAnyFieldInitialized = true;
192      return false;
193    }
194  }
195
196  if (PointeeT->isArrayType()) {
197    IsAnyFieldInitialized = true;
198    return false;
199  }
200
201  assert((isPrimitiveType(PointeeT) || isDereferencableType(PointeeT)) &&
202         "At this point FR must either have a primitive dynamic type, or it "
203         "must be a null, undefined, unknown or concrete pointer!");
204
205  SVal PointeeV = State->getSVal(R);
206
207  if (isPrimitiveUninit(PointeeV)) {
208    if (NeedsCastBack)
209      return addFieldToUninits(LocalChain.add(NeedsCastLocField(FR, DynT)), R);
210    return addFieldToUninits(LocalChain.add(LocField(FR)), R);
211  }
212
213  IsAnyFieldInitialized = true;
214  return false;
215}
216
217//===----------------------------------------------------------------------===//
218//                           Utility functions.
219//===----------------------------------------------------------------------===//
220
221static std::optional<DereferenceInfo> dereference(ProgramStateRef State,
222                                                  const FieldRegion *FR) {
223
224  llvm::SmallSet<const TypedValueRegion *, 5> VisitedRegions;
225
226  SVal V = State->getSVal(FR);
227  assert(V.getAsRegion() && "V must have an underlying region!");
228
229  // If the static type of the field is a void pointer, or it is a
230  // nonloc::LocAsInteger, we need to cast it back to the dynamic type before
231  // dereferencing.
232  bool NeedsCastBack =
233      isVoidPointer(FR->getDecl()->getType()) || isa<nonloc::LocAsInteger>(V);
234
235  // The region we'd like to acquire.
236  const auto *R = V.getAsRegion()->getAs<TypedValueRegion>();
237  if (!R)
238    return std::nullopt;
239
240  VisitedRegions.insert(R);
241
242  // We acquire the dynamic type of R,
243  QualType DynT = R->getLocationType();
244
245  while (const MemRegion *Tmp = State->getSVal(R, DynT).getAsRegion()) {
246
247    R = Tmp->getAs<TypedValueRegion>();
248    if (!R)
249      return std::nullopt;
250
251    // We found a cyclic pointer, like int *ptr = (int *)&ptr.
252    if (!VisitedRegions.insert(R).second)
253      return DereferenceInfo{R, NeedsCastBack, /*IsCyclic*/ true};
254
255    DynT = R->getLocationType();
256    // In order to ensure that this loop terminates, we're also checking the
257    // dynamic type of R, since type hierarchy is finite.
258    if (isDereferencableType(DynT->getPointeeType()))
259      break;
260  }
261
262  while (isa<CXXBaseObjectRegion>(R)) {
263    NeedsCastBack = true;
264    const auto *SuperR = dyn_cast<TypedValueRegion>(R->getSuperRegion());
265    if (!SuperR)
266      break;
267
268    R = SuperR;
269  }
270
271  return DereferenceInfo{R, NeedsCastBack, /*IsCyclic*/ false};
272}
273
274static bool isVoidPointer(QualType T) {
275  while (!T.isNull()) {
276    if (T->isVoidPointerType())
277      return true;
278    T = T->getPointeeType();
279  }
280  return false;
281}
282