Skip to content

Commit 3bdbe71

Browse files
committed
[𝘀𝗽𝗿] initial version
Created using spr 1.3.6-beta.1
2 parents 64046e9 + ee0ee25 commit 3bdbe71

File tree

114 files changed

+9321
-387
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

114 files changed

+9321
-387
lines changed

clang/include/clang/AST/ASTContext.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,12 @@ struct TypeInfoChars {
183183
}
184184
};
185185

186+
struct PFPField {
187+
CharUnits structOffset;
188+
CharUnits offset;
189+
FieldDecl *field;
190+
};
191+
186192
/// Holds long-lived AST nodes (such as types and decls) that can be
187193
/// referred to throughout the semantic analysis of a file.
188194
class ASTContext : public RefCountedBase<ASTContext> {
@@ -3618,6 +3624,22 @@ OPT_LIST(V)
36183624

36193625
StringRef getCUIDHash() const;
36203626

3627+
bool isPFPStruct(const RecordDecl *rec) const;
3628+
void findPFPFields(QualType Ty, CharUnits Offset,
3629+
std::vector<PFPField> &Fields, bool IncludeVBases) const;
3630+
bool hasPFPFields(QualType ty) const;
3631+
bool isPFPField(const FieldDecl *field) const;
3632+
3633+
/// Returns whether this record's PFP fields (if any) are trivially
3634+
/// relocatable (i.e. may be memcpy'd). This may also return true if the
3635+
/// record does not have any PFP fields, so it may be necessary for the caller
3636+
/// to check for PFP fields, e.g. by calling hasPFPFields().
3637+
bool arePFPFieldsTriviallyRelocatable(const RecordDecl *RD) const;
3638+
3639+
llvm::SetVector<const FieldDecl *> PFPFieldsWithEvaluatedOffset;
3640+
void recordMemberDataPointerEvaluation(const ValueDecl *VD);
3641+
void recordOffsetOfEvaluation(const OffsetOfExpr *E);
3642+
36213643
private:
36223644
/// All OMPTraitInfo objects live in this collection, one per
36233645
/// `pragma omp [begin] declare variant` directive.

clang/include/clang/Basic/Attr.td

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2460,6 +2460,12 @@ def CountedByOrNull : DeclOrTypeAttr {
24602460
let LangOpts = [COnly];
24612461
}
24622462

2463+
def NoPointerFieldProtection : DeclOrTypeAttr {
2464+
let Spellings = [Clang<"no_field_protection">];
2465+
let Subjects = SubjectList<[Field], ErrorDiag>;
2466+
let Documentation = [Undocumented];
2467+
}
2468+
24632469
def SizedBy : DeclOrTypeAttr {
24642470
let Spellings = [Clang<"sized_by">];
24652471
let Subjects = SubjectList<[Field], ErrorDiag>;

clang/include/clang/Basic/Features.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,9 @@ FEATURE(shadow_call_stack,
262262
FEATURE(tls, PP.getTargetInfo().isTLSSupported())
263263
FEATURE(underlying_type, LangOpts.CPlusPlus)
264264
FEATURE(experimental_library, LangOpts.ExperimentalLibrary)
265+
FEATURE(pointer_field_protection,
266+
LangOpts.getPointerFieldProtection() !=
267+
LangOptions::PointerFieldProtectionKind::None)
265268

266269
// C11 features supported by other languages as extensions.
267270
EXTENSION(c_alignas, true)

clang/include/clang/Basic/LangOptions.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -501,6 +501,9 @@ LANGOPT(RelativeCXXABIVTables, 1, 0,
501501
LANGOPT(OmitVTableRTTI, 1, 0,
502502
"Use an ABI-incompatible v-table layout that omits the RTTI component")
503503

504+
ENUM_LANGOPT(PointerFieldProtection, PointerFieldProtectionKind, 2, PointerFieldProtectionKind::None,
505+
"Encode struct pointer fields to protect against UAF vulnerabilities")
506+
504507
LANGOPT(VScaleMin, 32, 0, "Minimum vscale value")
505508
LANGOPT(VScaleMax, 32, 0, "Maximum vscale value")
506509

clang/include/clang/Basic/LangOptions.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,17 @@ class LangOptionsBase {
365365
BKey
366366
};
367367

368+
enum class PointerFieldProtectionKind {
369+
/// Pointer field protection disabled
370+
None,
371+
/// Pointer field protection enabled, allocator does not tag heap
372+
/// allocations.
373+
Untagged,
374+
/// Pointer field protection enabled, allocator is expected to tag heap
375+
/// allocations.
376+
Tagged,
377+
};
378+
368379
enum class ThreadModelKind {
369380
/// POSIX Threads.
370381
POSIX,

clang/include/clang/Basic/TokenKinds.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -542,6 +542,7 @@ TYPE_TRAIT_2(__is_pointer_interconvertible_base_of, IsPointerInterconvertibleBas
542542

543543
// Clang-only C++ Type Traits
544544
TYPE_TRAIT_1(__is_trivially_relocatable, IsTriviallyRelocatable, KEYCXX)
545+
TYPE_TRAIT_1(__has_non_relocatable_fields, HasNonRelocatableFields, KEYCXX)
545546
TYPE_TRAIT_1(__is_trivially_equality_comparable, IsTriviallyEqualityComparable, KEYCXX)
546547
TYPE_TRAIT_1(__is_bounded_array, IsBoundedArray, KEYCXX)
547548
TYPE_TRAIT_1(__is_unbounded_array, IsUnboundedArray, KEYCXX)

clang/include/clang/Driver/Options.td

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2957,6 +2957,12 @@ defm experimental_omit_vtable_rtti : BoolFOption<"experimental-omit-vtable-rtti"
29572957
NegFlag<SetFalse, [], [CC1Option], "Do not omit">,
29582958
BothFlags<[], [CC1Option], " the RTTI component from virtual tables">>;
29592959

2960+
def experimental_pointer_field_protection_EQ : Joined<["-"], "fexperimental-pointer-field-protection=">, Group<f_Group>,
2961+
Visibility<[ClangOption, CC1Option]>,
2962+
Values<"none,untagged,tagged">, NormalizedValuesScope<"LangOptions::PointerFieldProtectionKind">,
2963+
NormalizedValues<["None", "Untagged", "Tagged"]>,
2964+
MarshallingInfoEnum<LangOpts<"PointerFieldProtection">, "None">;
2965+
29602966
def fcxx_abi_EQ : Joined<["-"], "fc++-abi=">,
29612967
Group<f_clang_Group>, Visibility<[ClangOption, CC1Option]>,
29622968
HelpText<"C++ ABI to use. This will override the target C++ ABI.">;

clang/lib/AST/ASTContext.cpp

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@
7979
#include "llvm/ADT/StringRef.h"
8080
#include "llvm/Frontend/OpenMP/OMPIRBuilder.h"
8181
#include "llvm/Support/Capacity.h"
82+
#include "llvm/Support/CommandLine.h"
8283
#include "llvm/Support/Compiler.h"
8384
#include "llvm/Support/ErrorHandling.h"
8485
#include "llvm/Support/MD5.h"
@@ -14948,3 +14949,97 @@ bool ASTContext::useAbbreviatedThunkName(GlobalDecl VirtualMethodDecl,
1494814949
ThunksToBeAbbreviated[VirtualMethodDecl] = std::move(SimplifiedThunkNames);
1494914950
return Result;
1495014951
}
14952+
14953+
bool ASTContext::arePFPFieldsTriviallyRelocatable(const RecordDecl *RD) const {
14954+
if (getLangOpts().getPointerFieldProtection() ==
14955+
LangOptions::PointerFieldProtectionKind::Tagged)
14956+
return !isa<CXXRecordDecl>(RD) ||
14957+
cast<CXXRecordDecl>(RD)->hasTrivialDestructor();
14958+
return true;
14959+
}
14960+
14961+
bool ASTContext::isPFPStruct(const RecordDecl *rec) const {
14962+
if (getLangOpts().getPointerFieldProtection() !=
14963+
LangOptions::PointerFieldProtectionKind::None)
14964+
if (auto *cxxRec = dyn_cast<CXXRecordDecl>(rec))
14965+
return !cxxRec->isStandardLayout();
14966+
return false;
14967+
}
14968+
14969+
void ASTContext::findPFPFields(QualType Ty, CharUnits Offset,
14970+
std::vector<PFPField> &Fields,
14971+
bool IncludeVBases) const {
14972+
if (auto *AT = getAsConstantArrayType(Ty)) {
14973+
if (auto *ElemDecl = AT->getElementType()->getAsCXXRecordDecl()) {
14974+
const ASTRecordLayout &ElemRL = getASTRecordLayout(ElemDecl);
14975+
for (unsigned i = 0; i != AT->getSize(); ++i) {
14976+
findPFPFields(AT->getElementType(), Offset + i * ElemRL.getSize(),
14977+
Fields, true);
14978+
}
14979+
}
14980+
}
14981+
auto *Decl = Ty->getAsCXXRecordDecl();
14982+
if (!Decl)
14983+
return;
14984+
const ASTRecordLayout &RL = getASTRecordLayout(Decl);
14985+
for (FieldDecl *field : Decl->fields()) {
14986+
CharUnits fieldOffset =
14987+
Offset + toCharUnitsFromBits(RL.getFieldOffset(field->getFieldIndex()));
14988+
if (isPFPField(field))
14989+
Fields.push_back({Offset, fieldOffset, field});
14990+
findPFPFields(field->getType(), fieldOffset, Fields, true);
14991+
}
14992+
for (auto &Base : Decl->bases()) {
14993+
if (Base.isVirtual())
14994+
continue;
14995+
CharUnits BaseOffset =
14996+
Offset + RL.getBaseClassOffset(Base.getType()->getAsCXXRecordDecl());
14997+
findPFPFields(Base.getType(), BaseOffset, Fields, false);
14998+
}
14999+
if (IncludeVBases) {
15000+
for (auto &Base : Decl->vbases()) {
15001+
CharUnits BaseOffset =
15002+
Offset + RL.getVBaseClassOffset(Base.getType()->getAsCXXRecordDecl());
15003+
findPFPFields(Base.getType(), BaseOffset, Fields, false);
15004+
}
15005+
}
15006+
}
15007+
15008+
bool ASTContext::hasPFPFields(QualType ty) const {
15009+
std::vector<PFPField> pfpFields;
15010+
findPFPFields(ty, CharUnits::Zero(), pfpFields, true);
15011+
return !pfpFields.empty();
15012+
}
15013+
15014+
bool ASTContext::isPFPField(const FieldDecl *field) const {
15015+
if (!isPFPStruct(field->getParent()))
15016+
return false;
15017+
return field->getType()->isPointerType() &&
15018+
!field->hasAttr<NoPointerFieldProtectionAttr>();
15019+
}
15020+
15021+
void ASTContext::recordMemberDataPointerEvaluation(const ValueDecl *VD) {
15022+
if (getLangOpts().getPointerFieldProtection() ==
15023+
LangOptions::PointerFieldProtectionKind::None)
15024+
return;
15025+
auto *FD = dyn_cast<FieldDecl>(VD);
15026+
if (!FD)
15027+
FD = cast<FieldDecl>(cast<IndirectFieldDecl>(VD)->chain().back());
15028+
if (!isPFPField(FD))
15029+
return;
15030+
PFPFieldsWithEvaluatedOffset.insert(FD);
15031+
}
15032+
15033+
void ASTContext::recordOffsetOfEvaluation(const OffsetOfExpr *E) {
15034+
if (getLangOpts().getPointerFieldProtection() ==
15035+
LangOptions::PointerFieldProtectionKind::None ||
15036+
E->getNumComponents() == 0)
15037+
return;
15038+
OffsetOfNode Comp = E->getComponent(E->getNumComponents() - 1);
15039+
if (Comp.getKind() != OffsetOfNode::Field)
15040+
return;
15041+
FieldDecl *FD = Comp.getField();
15042+
if (!isPFPField(FD))
15043+
return;
15044+
PFPFieldsWithEvaluatedOffset.insert(FD);
15045+
}

clang/lib/AST/ExprConstant.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14932,6 +14932,7 @@ bool IntExprEvaluator::VisitUnaryExprOrTypeTraitExpr(
1493214932
}
1493314933

1493414934
bool IntExprEvaluator::VisitOffsetOfExpr(const OffsetOfExpr *OOE) {
14935+
Info.Ctx.recordOffsetOfEvaluation(OOE);
1493514936
CharUnits Result;
1493614937
unsigned n = OOE->getNumComponents();
1493714938
if (n == 0)

clang/lib/AST/Type.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2852,7 +2852,9 @@ bool QualType::isTriviallyRelocatableType(const ASTContext &Context) const {
28522852
} else if (!BaseElementType->isObjectType()) {
28532853
return false;
28542854
} else if (const auto *RD = BaseElementType->getAsRecordDecl()) {
2855-
return RD->canPassInRegisters();
2855+
return RD->canPassInRegisters() &&
2856+
(Context.arePFPFieldsTriviallyRelocatable(RD) ||
2857+
!Context.hasPFPFields(BaseElementType));
28562858
} else if (BaseElementType.isTriviallyCopyableType(Context)) {
28572859
return true;
28582860
} else {

0 commit comments

Comments
 (0)