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