283 lines
8.8 KiB
C++
283 lines
8.8 KiB
C++
|
//===----- UninitializedPointee.cpp ------------------------------*- C++ -*-==//
|
||
|
//
|
||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||
|
//
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
//
|
||
|
// This file defines functions and methods for handling pointers and references
|
||
|
// to reduce the size and complexity of UninitializedObjectChecker.cpp.
|
||
|
//
|
||
|
// To read about command line options and documentation about how the checker
|
||
|
// works, refer to UninitializedObjectChecker.h.
|
||
|
//
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
|
||
|
#include "UninitializedObject.h"
|
||
|
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
|
||
|
#include "clang/StaticAnalyzer/Core/Checker.h"
|
||
|
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
|
||
|
#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
|
||
|
|
||
|
using namespace clang;
|
||
|
using namespace clang::ento;
|
||
|
|
||
|
namespace {
|
||
|
|
||
|
/// Represents a pointer or a reference field.
|
||
|
class LocField final : public FieldNode {
|
||
|
/// We'll store whether the pointee or the pointer itself is uninitialited.
|
||
|
const bool IsDereferenced;
|
||
|
|
||
|
public:
|
||
|
LocField(const FieldRegion *FR, const bool IsDereferenced = true)
|
||
|
: FieldNode(FR), IsDereferenced(IsDereferenced) {}
|
||
|
|
||
|
virtual void printNoteMsg(llvm::raw_ostream &Out) const override {
|
||
|
if (IsDereferenced)
|
||
|
Out << "uninitialized pointee ";
|
||
|
else
|
||
|
Out << "uninitialized pointer ";
|
||
|
}
|
||
|
|
||
|
virtual void printPrefix(llvm::raw_ostream &Out) const override {}
|
||
|
|
||
|
virtual void printNode(llvm::raw_ostream &Out) const override {
|
||
|
Out << getVariableName(getDecl());
|
||
|
}
|
||
|
|
||
|
virtual void printSeparator(llvm::raw_ostream &Out) const override {
|
||
|
if (getDecl()->getType()->isPointerType())
|
||
|
Out << "->";
|
||
|
else
|
||
|
Out << '.';
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/// Represents a nonloc::LocAsInteger or void* field, that point to objects, but
|
||
|
/// needs to be casted back to its dynamic type for a correct note message.
|
||
|
class NeedsCastLocField final : public FieldNode {
|
||
|
QualType CastBackType;
|
||
|
|
||
|
public:
|
||
|
NeedsCastLocField(const FieldRegion *FR, const QualType &T)
|
||
|
: FieldNode(FR), CastBackType(T) {}
|
||
|
|
||
|
virtual void printNoteMsg(llvm::raw_ostream &Out) const override {
|
||
|
Out << "uninitialized pointee ";
|
||
|
}
|
||
|
|
||
|
virtual void printPrefix(llvm::raw_ostream &Out) const override {
|
||
|
// If this object is a nonloc::LocAsInteger.
|
||
|
if (getDecl()->getType()->isIntegerType())
|
||
|
Out << "reinterpret_cast";
|
||
|
// If this pointer's dynamic type is different then it's static type.
|
||
|
else
|
||
|
Out << "static_cast";
|
||
|
Out << '<' << CastBackType.getAsString() << ">(";
|
||
|
}
|
||
|
|
||
|
virtual void printNode(llvm::raw_ostream &Out) const override {
|
||
|
Out << getVariableName(getDecl()) << ')';
|
||
|
}
|
||
|
|
||
|
virtual void printSeparator(llvm::raw_ostream &Out) const override {
|
||
|
Out << "->";
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/// Represents a Loc field that points to itself.
|
||
|
class CyclicLocField final : public FieldNode {
|
||
|
|
||
|
public:
|
||
|
CyclicLocField(const FieldRegion *FR) : FieldNode(FR) {}
|
||
|
|
||
|
virtual void printNoteMsg(llvm::raw_ostream &Out) const override {
|
||
|
Out << "object references itself ";
|
||
|
}
|
||
|
|
||
|
virtual void printPrefix(llvm::raw_ostream &Out) const override {}
|
||
|
|
||
|
virtual void printNode(llvm::raw_ostream &Out) const override {
|
||
|
Out << getVariableName(getDecl());
|
||
|
}
|
||
|
|
||
|
virtual void printSeparator(llvm::raw_ostream &Out) const override {
|
||
|
llvm_unreachable("CyclicLocField objects must be the last node of the "
|
||
|
"fieldchain!");
|
||
|
}
|
||
|
};
|
||
|
|
||
|
} // end of anonymous namespace
|
||
|
|
||
|
// Utility function declarations.
|
||
|
|
||
|
struct DereferenceInfo {
|
||
|
const TypedValueRegion *R;
|
||
|
const bool NeedsCastBack;
|
||
|
const bool IsCyclic;
|
||
|
DereferenceInfo(const TypedValueRegion *R, bool NCB, bool IC)
|
||
|
: R(R), NeedsCastBack(NCB), IsCyclic(IC) {}
|
||
|
};
|
||
|
|
||
|
/// Dereferences \p FR and returns with the pointee's region, and whether it
|
||
|
/// needs to be casted back to it's location type. If for whatever reason
|
||
|
/// dereferencing fails, returns with None.
|
||
|
static llvm::Optional<DereferenceInfo> dereference(ProgramStateRef State,
|
||
|
const FieldRegion *FR);
|
||
|
|
||
|
/// Returns whether \p T can be (transitively) dereferenced to a void pointer
|
||
|
/// type (void*, void**, ...).
|
||
|
static bool isVoidPointer(QualType T);
|
||
|
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
// Methods for FindUninitializedFields.
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
|
||
|
bool FindUninitializedFields::isDereferencableUninit(
|
||
|
const FieldRegion *FR, FieldChainInfo LocalChain) {
|
||
|
|
||
|
SVal V = State->getSVal(FR);
|
||
|
|
||
|
assert((isDereferencableType(FR->getDecl()->getType()) ||
|
||
|
V.getAs<nonloc::LocAsInteger>()) &&
|
||
|
"This method only checks dereferenceable objects!");
|
||
|
|
||
|
if (V.isUnknown() || V.getAs<loc::ConcreteInt>()) {
|
||
|
IsAnyFieldInitialized = true;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (V.isUndef()) {
|
||
|
return addFieldToUninits(
|
||
|
LocalChain.add(LocField(FR, /*IsDereferenced*/ false)), FR);
|
||
|
}
|
||
|
|
||
|
if (!Opts.CheckPointeeInitialization) {
|
||
|
IsAnyFieldInitialized = true;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// At this point the pointer itself is initialized and points to a valid
|
||
|
// location, we'll now check the pointee.
|
||
|
llvm::Optional<DereferenceInfo> DerefInfo = dereference(State, FR);
|
||
|
if (!DerefInfo) {
|
||
|
IsAnyFieldInitialized = true;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (DerefInfo->IsCyclic)
|
||
|
return addFieldToUninits(LocalChain.add(CyclicLocField(FR)), FR);
|
||
|
|
||
|
const TypedValueRegion *R = DerefInfo->R;
|
||
|
const bool NeedsCastBack = DerefInfo->NeedsCastBack;
|
||
|
|
||
|
QualType DynT = R->getLocationType();
|
||
|
QualType PointeeT = DynT->getPointeeType();
|
||
|
|
||
|
if (PointeeT->isStructureOrClassType()) {
|
||
|
if (NeedsCastBack)
|
||
|
return isNonUnionUninit(R, LocalChain.add(NeedsCastLocField(FR, DynT)));
|
||
|
return isNonUnionUninit(R, LocalChain.add(LocField(FR)));
|
||
|
}
|
||
|
|
||
|
if (PointeeT->isUnionType()) {
|
||
|
if (isUnionUninit(R)) {
|
||
|
if (NeedsCastBack)
|
||
|
return addFieldToUninits(LocalChain.add(NeedsCastLocField(FR, DynT)),
|
||
|
R);
|
||
|
return addFieldToUninits(LocalChain.add(LocField(FR)), R);
|
||
|
} else {
|
||
|
IsAnyFieldInitialized = true;
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (PointeeT->isArrayType()) {
|
||
|
IsAnyFieldInitialized = true;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
assert((isPrimitiveType(PointeeT) || isDereferencableType(PointeeT)) &&
|
||
|
"At this point FR must either have a primitive dynamic type, or it "
|
||
|
"must be a null, undefined, unknown or concrete pointer!");
|
||
|
|
||
|
SVal PointeeV = State->getSVal(R);
|
||
|
|
||
|
if (isPrimitiveUninit(PointeeV)) {
|
||
|
if (NeedsCastBack)
|
||
|
return addFieldToUninits(LocalChain.add(NeedsCastLocField(FR, DynT)), R);
|
||
|
return addFieldToUninits(LocalChain.add(LocField(FR)), R);
|
||
|
}
|
||
|
|
||
|
IsAnyFieldInitialized = true;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
// Utility functions.
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
|
||
|
static llvm::Optional<DereferenceInfo> dereference(ProgramStateRef State,
|
||
|
const FieldRegion *FR) {
|
||
|
|
||
|
llvm::SmallSet<const TypedValueRegion *, 5> VisitedRegions;
|
||
|
|
||
|
SVal V = State->getSVal(FR);
|
||
|
assert(V.getAsRegion() && "V must have an underlying region!");
|
||
|
|
||
|
// If the static type of the field is a void pointer, or it is a
|
||
|
// nonloc::LocAsInteger, we need to cast it back to the dynamic type before
|
||
|
// dereferencing.
|
||
|
bool NeedsCastBack = isVoidPointer(FR->getDecl()->getType()) ||
|
||
|
V.getAs<nonloc::LocAsInteger>();
|
||
|
|
||
|
// The region we'd like to acquire.
|
||
|
const auto *R = V.getAsRegion()->getAs<TypedValueRegion>();
|
||
|
if (!R)
|
||
|
return None;
|
||
|
|
||
|
VisitedRegions.insert(R);
|
||
|
|
||
|
// We acquire the dynamic type of R,
|
||
|
QualType DynT = R->getLocationType();
|
||
|
|
||
|
while (const MemRegion *Tmp = State->getSVal(R, DynT).getAsRegion()) {
|
||
|
|
||
|
R = Tmp->getAs<TypedValueRegion>();
|
||
|
if (!R)
|
||
|
return None;
|
||
|
|
||
|
// We found a cyclic pointer, like int *ptr = (int *)&ptr.
|
||
|
if (!VisitedRegions.insert(R).second)
|
||
|
return DereferenceInfo{R, NeedsCastBack, /*IsCyclic*/ true};
|
||
|
|
||
|
DynT = R->getLocationType();
|
||
|
// In order to ensure that this loop terminates, we're also checking the
|
||
|
// dynamic type of R, since type hierarchy is finite.
|
||
|
if (isDereferencableType(DynT->getPointeeType()))
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
while (isa<CXXBaseObjectRegion>(R)) {
|
||
|
NeedsCastBack = true;
|
||
|
const auto *SuperR = dyn_cast<TypedValueRegion>(R->getSuperRegion());
|
||
|
if (!SuperR)
|
||
|
break;
|
||
|
|
||
|
R = SuperR;
|
||
|
}
|
||
|
|
||
|
return DereferenceInfo{R, NeedsCastBack, /*IsCyclic*/ false};
|
||
|
}
|
||
|
|
||
|
static bool isVoidPointer(QualType T) {
|
||
|
while (!T.isNull()) {
|
||
|
if (T->isVoidPointerType())
|
||
|
return true;
|
||
|
T = T->getPointeeType();
|
||
|
}
|
||
|
return false;
|
||
|
}
|