Skip to content

Commit 95b5e4e

Browse files
committed
[𝘀𝗽𝗿] initial version
Created using spr 1.3.6-beta.1
2 parents e20413c + 69f0701 commit 95b5e4e

File tree

140 files changed

+2764
-209
lines changed

Some content is hidden

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

140 files changed

+2764
-209
lines changed

clang/docs/StructureProtection.rst

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
====================
2+
Structure Protection
3+
====================
4+
5+
.. contents::
6+
:local:
7+
8+
9+
Introduction
10+
============
11+
12+
Structure protection is an *experimental* mitigation
13+
against use-after-free vulnerabilities. For
14+
more information, please see the original `RFC
15+
<https://discourse.llvm.org/t/rfc-structure-protection-a-family-of-uaf-mitigation-techniques/85555>`_.
16+
An independent set of documentation will be contributed when the feature
17+
is promoted to stable.
18+
19+
Usage
20+
=====
21+
22+
To use structure protection, build your program using one of the flags:
23+
24+
- ``-fexperimental-pointer-field-protection=untagged``: Enable pointer
25+
field protection with untagged pointers.
26+
27+
- ``-fexperimental-pointer-field-protection=tagged``: Enable pointer
28+
field protection with heap pointers assumed to be tagged by the allocator.
29+
30+
The entire C++ part of the program must be built with a consistent
31+
``-fexperimental-pointer-field-protection`` flag, and the C++ standard
32+
library must also be built with the same flag and statically linked into
33+
the program.
34+
35+
To build libc++ with pointer field protection support, pass the following
36+
CMake flags:
37+
38+
.. code-block:: console
39+
40+
"-DRUNTIMES_${triple}_LIBCXXABI_ENABLE_SHARED=OFF" \
41+
"-DRUNTIMES_${triple}_LIBCXX_USE_COMPILER_RT=ON" \
42+
"-DRUNTIMES_${triple}_LIBCXX_PFP=untagged" \
43+
"-DRUNTIMES_${triple}_LIBCXX_ENABLE_SHARED=OFF" \
44+
"-DRUNTIMES_${triple}_LIBCXX_TEST_CONFIG=llvm-libc++-static.cfg.in" \
45+
"-DRUNTIMES_${triple}_LIBUNWIND_ENABLE_SHARED=OFF" \
46+
47+
where ``${triple}`` is your target triple, such as
48+
``aarch64-unknown-linux``.
49+
50+
The resulting toolchain may then be used to build programs
51+
with pointer field protection by passing ``-stdlib=libc++
52+
-fexperimental-pointer-field-protection=untagged`` at compile time
53+
and ``-Wl,-Bstatic -lc++ -lc++abi -Wl,-Bdynamic -lm -fuse-ld=lld
54+
-static-libstdc++`` at link time.

clang/docs/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ Using Clang as a Compiler
4747
LTOVisibility
4848
SafeStack
4949
ShadowCallStack
50+
StructureProtection
5051
SourceBasedCodeCoverage
5152
StandardCPlusPlusModules
5253
Modules

clang/include/clang/AST/ASTContext.h

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

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

37213726
StringRef getCUIDHash() const;
37223727

3728+
bool isPFPStruct(const RecordDecl *rec) const;
3729+
void findPFPFields(QualType Ty, CharUnits Offset,
3730+
std::vector<PFPField> &Fields, bool IncludeVBases) const;
3731+
bool hasPFPFields(QualType ty) const;
3732+
bool isPFPField(const FieldDecl *field) const;
3733+
3734+
/// Returns whether this record's PFP fields (if any) are trivially
3735+
/// relocatable (i.e. may be memcpy'd). This may also return true if the
3736+
/// record does not have any PFP fields, so it may be necessary for the caller
3737+
/// to check for PFP fields, e.g. by calling hasPFPFields().
3738+
bool arePFPFieldsTriviallyRelocatable(const RecordDecl *RD) const;
3739+
3740+
llvm::SetVector<const FieldDecl *> PFPFieldsWithEvaluatedOffset;
3741+
void recordMemberDataPointerEvaluation(const ValueDecl *VD);
3742+
void recordOffsetOfEvaluation(const OffsetOfExpr *E);
3743+
37233744
private:
37243745
/// All OMPTraitInfo objects live in this collection, one per
37253746
/// `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
@@ -2597,6 +2597,12 @@ def CountedByOrNull : DeclOrTypeAttr {
25972597
let LangOpts = [COnly];
25982598
}
25992599

2600+
def NoPointerFieldProtection : DeclOrTypeAttr {
2601+
let Spellings = [Clang<"no_field_protection">];
2602+
let Subjects = SubjectList<[Field], ErrorDiag>;
2603+
let Documentation = [Undocumented];
2604+
}
2605+
26002606
def SizedBy : DeclOrTypeAttr {
26012607
let Spellings = [Clang<"sized_by">];
26022608
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
@@ -312,6 +312,9 @@ FEATURE(shadow_call_stack,
312312
FEATURE(tls, PP.getTargetInfo().isTLSSupported())
313313
FEATURE(underlying_type, LangOpts.CPlusPlus)
314314
FEATURE(experimental_library, LangOpts.ExperimentalLibrary)
315+
EXTENSION(pointer_field_protection,
316+
LangOpts.getPointerFieldProtection() !=
317+
LangOptions::PointerFieldProtectionKind::None)
315318

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

clang/include/clang/Basic/LangOptions.def

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

459+
ENUM_LANGOPT(PointerFieldProtection, PointerFieldProtectionKind, 2, PointerFieldProtectionKind::None, NotCompatible,
460+
"Encode struct pointer fields to protect against UAF vulnerabilities")
461+
459462
LANGOPT(VScaleMin, 32, 0, NotCompatible, "Minimum vscale value")
460463
LANGOPT(VScaleMax, 32, 0, NotCompatible, "Maximum vscale value")
461464

clang/include/clang/Basic/LangOptions.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,17 @@ class LangOptionsBase {
376376
BKey
377377
};
378378

379+
enum class PointerFieldProtectionKind {
380+
/// Pointer field protection disabled
381+
None,
382+
/// Pointer field protection enabled, allocator does not tag heap
383+
/// allocations.
384+
Untagged,
385+
/// Pointer field protection enabled, allocator is expected to tag heap
386+
/// allocations.
387+
Tagged,
388+
};
389+
379390
enum class ThreadModelKind {
380391
/// POSIX Threads.
381392
POSIX,

clang/include/clang/Driver/Options.td

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

3054+
def experimental_pointer_field_protection_EQ : Joined<["-"], "fexperimental-pointer-field-protection=">, Group<f_Group>,
3055+
Visibility<[ClangOption, CC1Option]>,
3056+
Values<"none,untagged,tagged">, NormalizedValuesScope<"LangOptions::PointerFieldProtectionKind">,
3057+
NormalizedValues<["None", "Untagged", "Tagged"]>,
3058+
MarshallingInfoEnum<LangOpts<"PointerFieldProtection">, "None">;
3059+
30543060
def fcxx_abi_EQ : Joined<["-"], "fc++-abi=">,
30553061
Group<f_clang_Group>, Visibility<[ClangOption, CC1Option]>,
30563062
HelpText<"C++ ABI to use. This will override the target C++ ABI.">;

clang/lib/AST/ASTContext.cpp

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15181,3 +15181,97 @@ bool ASTContext::useAbbreviatedThunkName(GlobalDecl VirtualMethodDecl,
1518115181
ThunksToBeAbbreviated[VirtualMethodDecl] = std::move(SimplifiedThunkNames);
1518215182
return Result;
1518315183
}
15184+
15185+
bool ASTContext::arePFPFieldsTriviallyRelocatable(const RecordDecl *RD) const {
15186+
if (getLangOpts().getPointerFieldProtection() ==
15187+
LangOptions::PointerFieldProtectionKind::Tagged)
15188+
return !isa<CXXRecordDecl>(RD) ||
15189+
cast<CXXRecordDecl>(RD)->hasTrivialDestructor();
15190+
return true;
15191+
}
15192+
15193+
bool ASTContext::isPFPStruct(const RecordDecl *rec) const {
15194+
if (getLangOpts().getPointerFieldProtection() !=
15195+
LangOptions::PointerFieldProtectionKind::None)
15196+
if (auto *cxxRec = dyn_cast<CXXRecordDecl>(rec))
15197+
return !cxxRec->isStandardLayout();
15198+
return false;
15199+
}
15200+
15201+
void ASTContext::findPFPFields(QualType Ty, CharUnits Offset,
15202+
std::vector<PFPField> &Fields,
15203+
bool IncludeVBases) const {
15204+
if (auto *AT = getAsConstantArrayType(Ty)) {
15205+
if (auto *ElemDecl = AT->getElementType()->getAsCXXRecordDecl()) {
15206+
const ASTRecordLayout &ElemRL = getASTRecordLayout(ElemDecl);
15207+
for (unsigned i = 0; i != AT->getSize(); ++i) {
15208+
findPFPFields(AT->getElementType(), Offset + i * ElemRL.getSize(),
15209+
Fields, true);
15210+
}
15211+
}
15212+
}
15213+
auto *Decl = Ty->getAsCXXRecordDecl();
15214+
if (!Decl)
15215+
return;
15216+
const ASTRecordLayout &RL = getASTRecordLayout(Decl);
15217+
for (FieldDecl *field : Decl->fields()) {
15218+
CharUnits fieldOffset =
15219+
Offset + toCharUnitsFromBits(RL.getFieldOffset(field->getFieldIndex()));
15220+
if (isPFPField(field))
15221+
Fields.push_back({fieldOffset, field});
15222+
findPFPFields(field->getType(), fieldOffset, Fields, true);
15223+
}
15224+
for (auto &Base : Decl->bases()) {
15225+
if (Base.isVirtual())
15226+
continue;
15227+
CharUnits BaseOffset =
15228+
Offset + RL.getBaseClassOffset(Base.getType()->getAsCXXRecordDecl());
15229+
findPFPFields(Base.getType(), BaseOffset, Fields, false);
15230+
}
15231+
if (IncludeVBases) {
15232+
for (auto &Base : Decl->vbases()) {
15233+
CharUnits BaseOffset =
15234+
Offset + RL.getVBaseClassOffset(Base.getType()->getAsCXXRecordDecl());
15235+
findPFPFields(Base.getType(), BaseOffset, Fields, false);
15236+
}
15237+
}
15238+
}
15239+
15240+
bool ASTContext::hasPFPFields(QualType ty) const {
15241+
std::vector<PFPField> pfpFields;
15242+
findPFPFields(ty, CharUnits::Zero(), pfpFields, true);
15243+
return !pfpFields.empty();
15244+
}
15245+
15246+
bool ASTContext::isPFPField(const FieldDecl *field) const {
15247+
if (!isPFPStruct(field->getParent()))
15248+
return false;
15249+
return field->getType()->isPointerType() &&
15250+
!field->hasAttr<NoPointerFieldProtectionAttr>();
15251+
}
15252+
15253+
void ASTContext::recordMemberDataPointerEvaluation(const ValueDecl *VD) {
15254+
if (getLangOpts().getPointerFieldProtection() ==
15255+
LangOptions::PointerFieldProtectionKind::None)
15256+
return;
15257+
auto *FD = dyn_cast<FieldDecl>(VD);
15258+
if (!FD)
15259+
FD = cast<FieldDecl>(cast<IndirectFieldDecl>(VD)->chain().back());
15260+
if (!isPFPField(FD))
15261+
return;
15262+
PFPFieldsWithEvaluatedOffset.insert(FD);
15263+
}
15264+
15265+
void ASTContext::recordOffsetOfEvaluation(const OffsetOfExpr *E) {
15266+
if (getLangOpts().getPointerFieldProtection() ==
15267+
LangOptions::PointerFieldProtectionKind::None ||
15268+
E->getNumComponents() == 0)
15269+
return;
15270+
OffsetOfNode Comp = E->getComponent(E->getNumComponents() - 1);
15271+
if (Comp.getKind() != OffsetOfNode::Field)
15272+
return;
15273+
FieldDecl *FD = Comp.getField();
15274+
if (!isPFPField(FD))
15275+
return;
15276+
PFPFieldsWithEvaluatedOffset.insert(FD);
15277+
}

clang/lib/AST/ExprConstant.cpp

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

1508615086
bool IntExprEvaluator::VisitOffsetOfExpr(const OffsetOfExpr *OOE) {
15087+
Info.Ctx.recordOffsetOfEvaluation(OOE);
1508715088
CharUnits Result;
1508815089
unsigned n = OOE->getNumComponents();
1508915090
if (n == 0)

0 commit comments

Comments
 (0)