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