Skip to content

Implement a simple e2e test for PFP. #2

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 18 commits into
base: pfp
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 16 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions clang/include/clang/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,12 @@ struct TypeInfoChars {
}
};

struct PFPField {
CharUnits structOffset;
CharUnits offset;
FieldDecl *field;
};

/// Holds long-lived AST nodes (such as types and decls) that can be
/// referred to throughout the semantic analysis of a file.
class ASTContext : public RefCountedBase<ASTContext> {
Expand Down Expand Up @@ -3618,6 +3624,22 @@ OPT_LIST(V)

StringRef getCUIDHash() const;

bool isPFPStruct(const RecordDecl *rec) const;
void findPFPFields(QualType Ty, CharUnits Offset,
std::vector<PFPField> &Fields, bool IncludeVBases) const;
bool hasPFPFields(QualType ty) const;
bool isPFPField(const FieldDecl *field) const;

/// Returns whether this record's PFP fields (if any) are trivially
/// relocatable (i.e. may be memcpy'd). This may also return true if the
/// record does not have any PFP fields, so it may be necessary for the caller
/// to check for PFP fields, e.g. by calling hasPFPFields().
bool arePFPFieldsTriviallyRelocatable(const RecordDecl *RD) const;

llvm::SetVector<const FieldDecl *> PFPFieldsWithEvaluatedOffset;
void recordMemberDataPointerEvaluation(const ValueDecl *VD);
void recordOffsetOfEvaluation(const OffsetOfExpr *E);

private:
/// All OMPTraitInfo objects live in this collection, one per
/// `pragma omp [begin] declare variant` directive.
Expand Down
6 changes: 6 additions & 0 deletions clang/include/clang/Basic/Attr.td
Original file line number Diff line number Diff line change
Expand Up @@ -2460,6 +2460,12 @@ def CountedByOrNull : DeclOrTypeAttr {
let LangOpts = [COnly];
}

def NoPointerFieldProtection : DeclOrTypeAttr {
let Spellings = [Clang<"no_field_protection">];
let Subjects = SubjectList<[Field], ErrorDiag>;
let Documentation = [Undocumented];
}

def SizedBy : DeclOrTypeAttr {
let Spellings = [Clang<"sized_by">];
let Subjects = SubjectList<[Field], ErrorDiag>;
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/Features.def
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,9 @@ FEATURE(shadow_call_stack,
FEATURE(tls, PP.getTargetInfo().isTLSSupported())
FEATURE(underlying_type, LangOpts.CPlusPlus)
FEATURE(experimental_library, LangOpts.ExperimentalLibrary)
FEATURE(pointer_field_protection,
LangOpts.getPointerFieldProtection() !=
LangOptions::PointerFieldProtectionKind::None)

// C11 features supported by other languages as extensions.
EXTENSION(c_alignas, true)
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/LangOptions.def
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,9 @@ LANGOPT(RelativeCXXABIVTables, 1, 0,
LANGOPT(OmitVTableRTTI, 1, 0,
"Use an ABI-incompatible v-table layout that omits the RTTI component")

ENUM_LANGOPT(PointerFieldProtection, PointerFieldProtectionKind, 2, PointerFieldProtectionKind::None,
"Encode struct pointer fields to protect against UAF vulnerabilities")

LANGOPT(VScaleMin, 32, 0, "Minimum vscale value")
LANGOPT(VScaleMax, 32, 0, "Maximum vscale value")

Expand Down
11 changes: 11 additions & 0 deletions clang/include/clang/Basic/LangOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,17 @@ class LangOptionsBase {
BKey
};

enum class PointerFieldProtectionKind {
/// Pointer field protection disabled
None,
/// Pointer field protection enabled, allocator does not tag heap
/// allocations.
Untagged,
/// Pointer field protection enabled, allocator is expected to tag heap
/// allocations.
Tagged,
};

enum class ThreadModelKind {
/// POSIX Threads.
POSIX,
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Basic/TokenKinds.def
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,7 @@ TYPE_TRAIT_2(__is_pointer_interconvertible_base_of, IsPointerInterconvertibleBas

// Clang-only C++ Type Traits
TYPE_TRAIT_1(__is_trivially_relocatable, IsTriviallyRelocatable, KEYCXX)
TYPE_TRAIT_1(__has_non_relocatable_fields, HasNonRelocatableFields, KEYCXX)
TYPE_TRAIT_1(__is_trivially_equality_comparable, IsTriviallyEqualityComparable, KEYCXX)
TYPE_TRAIT_1(__is_bounded_array, IsBoundedArray, KEYCXX)
TYPE_TRAIT_1(__is_unbounded_array, IsUnboundedArray, KEYCXX)
Expand Down
6 changes: 6 additions & 0 deletions clang/include/clang/Driver/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -2957,6 +2957,12 @@ defm experimental_omit_vtable_rtti : BoolFOption<"experimental-omit-vtable-rtti"
NegFlag<SetFalse, [], [CC1Option], "Do not omit">,
BothFlags<[], [CC1Option], " the RTTI component from virtual tables">>;

def experimental_pointer_field_protection_EQ : Joined<["-"], "fexperimental-pointer-field-protection=">, Group<f_Group>,
Visibility<[ClangOption, CC1Option]>,
Values<"none,untagged,tagged">, NormalizedValuesScope<"LangOptions::PointerFieldProtectionKind">,
NormalizedValues<["None", "Untagged", "Tagged"]>,
MarshallingInfoEnum<LangOpts<"PointerFieldProtection">, "None">;

def fcxx_abi_EQ : Joined<["-"], "fc++-abi=">,
Group<f_clang_Group>, Visibility<[ClangOption, CC1Option]>,
HelpText<"C++ ABI to use. This will override the target C++ ABI.">;
Expand Down
95 changes: 95 additions & 0 deletions clang/lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@
#include "llvm/ADT/StringRef.h"
#include "llvm/Frontend/OpenMP/OMPIRBuilder.h"
#include "llvm/Support/Capacity.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/MD5.h"
Expand Down Expand Up @@ -14948,3 +14949,97 @@ bool ASTContext::useAbbreviatedThunkName(GlobalDecl VirtualMethodDecl,
ThunksToBeAbbreviated[VirtualMethodDecl] = std::move(SimplifiedThunkNames);
return Result;
}

bool ASTContext::arePFPFieldsTriviallyRelocatable(const RecordDecl *RD) const {
if (getLangOpts().getPointerFieldProtection() ==
LangOptions::PointerFieldProtectionKind::Tagged)
return !isa<CXXRecordDecl>(RD) ||
cast<CXXRecordDecl>(RD)->hasTrivialDestructor();
return true;
}

bool ASTContext::isPFPStruct(const RecordDecl *rec) const {
if (getLangOpts().getPointerFieldProtection() !=
LangOptions::PointerFieldProtectionKind::None)
if (auto *cxxRec = dyn_cast<CXXRecordDecl>(rec))
return !cxxRec->isStandardLayout();
return false;
}

void ASTContext::findPFPFields(QualType Ty, CharUnits Offset,
std::vector<PFPField> &Fields,
bool IncludeVBases) const {
if (auto *AT = getAsConstantArrayType(Ty)) {
if (auto *ElemDecl = AT->getElementType()->getAsCXXRecordDecl()) {
const ASTRecordLayout &ElemRL = getASTRecordLayout(ElemDecl);
for (unsigned i = 0; i != AT->getSize(); ++i) {
findPFPFields(AT->getElementType(), Offset + i * ElemRL.getSize(),
Fields, true);
}
}
}
auto *Decl = Ty->getAsCXXRecordDecl();
if (!Decl)
return;
const ASTRecordLayout &RL = getASTRecordLayout(Decl);
for (FieldDecl *field : Decl->fields()) {
CharUnits fieldOffset =
Offset + toCharUnitsFromBits(RL.getFieldOffset(field->getFieldIndex()));
if (isPFPField(field))
Fields.push_back({Offset, fieldOffset, field});
findPFPFields(field->getType(), fieldOffset, Fields, true);
}
for (auto &Base : Decl->bases()) {
if (Base.isVirtual())
continue;
CharUnits BaseOffset =
Offset + RL.getBaseClassOffset(Base.getType()->getAsCXXRecordDecl());
findPFPFields(Base.getType(), BaseOffset, Fields, false);
}
if (IncludeVBases) {
for (auto &Base : Decl->vbases()) {
CharUnits BaseOffset =
Offset + RL.getVBaseClassOffset(Base.getType()->getAsCXXRecordDecl());
findPFPFields(Base.getType(), BaseOffset, Fields, false);
}
}
}

bool ASTContext::hasPFPFields(QualType ty) const {
std::vector<PFPField> pfpFields;
findPFPFields(ty, CharUnits::Zero(), pfpFields, true);
return !pfpFields.empty();
}

bool ASTContext::isPFPField(const FieldDecl *field) const {
if (!isPFPStruct(field->getParent()))
return false;
return field->getType()->isPointerType() &&
!field->hasAttr<NoPointerFieldProtectionAttr>();
}

void ASTContext::recordMemberDataPointerEvaluation(const ValueDecl *VD) {
if (getLangOpts().getPointerFieldProtection() ==
LangOptions::PointerFieldProtectionKind::None)
return;
auto *FD = dyn_cast<FieldDecl>(VD);
if (!FD)
FD = cast<FieldDecl>(cast<IndirectFieldDecl>(VD)->chain().back());
if (!isPFPField(FD))
return;
PFPFieldsWithEvaluatedOffset.insert(FD);
}

void ASTContext::recordOffsetOfEvaluation(const OffsetOfExpr *E) {
if (getLangOpts().getPointerFieldProtection() ==
LangOptions::PointerFieldProtectionKind::None ||
E->getNumComponents() == 0)
return;
OffsetOfNode Comp = E->getComponent(E->getNumComponents() - 1);
if (Comp.getKind() != OffsetOfNode::Field)
return;
FieldDecl *FD = Comp.getField();
if (!isPFPField(FD))
return;
PFPFieldsWithEvaluatedOffset.insert(FD);
}
1 change: 1 addition & 0 deletions clang/lib/AST/ExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14932,6 +14932,7 @@ bool IntExprEvaluator::VisitUnaryExprOrTypeTraitExpr(
}

bool IntExprEvaluator::VisitOffsetOfExpr(const OffsetOfExpr *OOE) {
Info.Ctx.recordOffsetOfEvaluation(OOE);
CharUnits Result;
unsigned n = OOE->getNumComponents();
if (n == 0)
Expand Down
4 changes: 3 additions & 1 deletion clang/lib/AST/Type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2852,7 +2852,9 @@ bool QualType::isTriviallyRelocatableType(const ASTContext &Context) const {
} else if (!BaseElementType->isObjectType()) {
return false;
} else if (const auto *RD = BaseElementType->getAsRecordDecl()) {
return RD->canPassInRegisters();
return RD->canPassInRegisters() &&
(Context.arePFPFieldsTriviallyRelocatable(RD) ||
!Context.hasPFPFields(BaseElementType));
} else if (BaseElementType.isTriviallyCopyableType(Context)) {
return true;
} else {
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/AST/TypePrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2096,6 +2096,9 @@ void TypePrinter::printAttributedAfter(const AttributedType *T,
case attr::ExtVectorType:
OS << "ext_vector_type";
break;
case attr::NoPointerFieldProtection:
OS << "no_field_protection";
break;
}
OS << "))";
}
Expand Down
Loading