Skip to content

Commit 72ed786

Browse files
[CHERI] Add a sealed type annotation (#109)
This allows us to disallow dereferences and pointer indexing on sealed capabilities at compile time. Builtins that introspect capabilities can all work on a sealed or unsealed capability. Builtins that mutate capabilities are all disallowed on sealed capabilities, with the exception of clear-tag (which works on sealed or unsealed things). Reinterpret cast and C-style (explicit) casts are allowed. Anyone who does an explicit unsafe cast should know what they're doing *anyway* this is another case of that. Implicit casts are also permitted to `void*`. This avoids needing new overloads for things that treat `void*` as opaque values. Casting back from `void*` to something useful already requires a dangerous explicit cast and you should know what you're doing if you do that. Also adds a __has_extension check for the sealed type qualifier. Co-authored-by: Owen Anderson <[email protected]>
1 parent 6d22636 commit 72ed786

38 files changed

+725
-363
lines changed

clang/include/clang/AST/Type.h

+15-7
Original file line numberDiff line numberDiff line change
@@ -1565,6 +1565,9 @@ enum PointerInterpretationKind {
15651565

15661566
/// The pointer should always be interpreted as an integer.
15671567
PIK_Integer,
1568+
1569+
/// The pointer should be interpreted as a sealed capability.
1570+
PIK_SealedCapability,
15681571
};
15691572

15701573
/// The base class of the type hierarchy.
@@ -1663,7 +1666,7 @@ class alignas(8) Type : public ExtQualsTypeCommonBase {
16631666

16641667
/// The interpretation (PointerInterpretationKind) to use for this array.
16651668
/// For function parameters only.
1666-
unsigned PIK : 1;
1669+
unsigned PIK : 2;
16671670

16681671
/// Whether the pointer interpretation for this array is set.
16691672
unsigned HasPIK : 1;
@@ -1672,7 +1675,7 @@ class alignas(8) Type : public ExtQualsTypeCommonBase {
16721675
class ConstantArrayTypeBitfields {
16731676
friend class ConstantArrayType;
16741677

1675-
unsigned : NumTypeBits + 3 + 3 + 2;
1678+
unsigned : NumTypeBits + 3 + 3 + 3;
16761679

16771680
/// Whether we have a stored size expression.
16781681
unsigned HasStoredSizeExpr : 1;
@@ -1759,7 +1762,7 @@ class alignas(8) Type : public ExtQualsTypeCommonBase {
17591762
unsigned : NumTypeBits;
17601763

17611764
/// The interpretation (PointerInterpretationKind) to use for this pointer.
1762-
unsigned PIK : 1;
1765+
unsigned PIK : 2;
17631766
};
17641767

17651768
class DependentPointerTypeBitfields {
@@ -1768,7 +1771,7 @@ class alignas(8) Type : public ExtQualsTypeCommonBase {
17681771
unsigned : NumTypeBits;
17691772

17701773
/// The interpretation (PointerInterpretationKind) to use for this pointer.
1771-
unsigned PIK : 1;
1774+
unsigned PIK : 2;
17721775
};
17731776

17741777
class ReferenceTypeBitfields {
@@ -1778,7 +1781,7 @@ class alignas(8) Type : public ExtQualsTypeCommonBase {
17781781

17791782
/// The interpretation (PointerInterpretationKind) to use for the pointer
17801783
/// backing this reference type.
1781-
unsigned PIK : 1;
1784+
unsigned PIK : 2;
17821785

17831786
/// True if the type was originally spelled with an lvalue sigil.
17841787
/// This is never true of rvalue references but can also be false
@@ -2239,6 +2242,9 @@ class alignas(8) Type : public ExtQualsTypeCommonBase {
22392242
/// pointers.
22402243
bool isCHERICapabilityType(const ASTContext &Context,
22412244
bool IncludeIntCap = true) const;
2245+
2246+
/// Returns true if this type is a Cheriot sealed capability.
2247+
bool isCHERISealedCapabilityType(const ASTContext &Context) const;
22422248
/// Returns true for __uintcap_t or __intcap_t (and enums/_Atomic with that
22432249
/// underlying type)
22442250
bool isIntCapType() const;
@@ -2898,7 +2904,8 @@ class PointerInterpretationTrait {
28982904

28992905
public:
29002906
bool isCHERICapability() const {
2901-
return getPointerInterpretation() == PIK_Capability;
2907+
return getPointerInterpretation() == PIK_Capability ||
2908+
getPointerInterpretation() == PIK_SealedCapability;
29022909
}
29032910

29042911
PointerInterpretationKind getPointerInterpretation() const {
@@ -2914,7 +2921,8 @@ class OptionalPointerInterpretationTrait {
29142921

29152922
public:
29162923
bool isCHERICapability() const {
2917-
return getPointerInterpretation() == PIK_Capability;
2924+
return getPointerInterpretation() == PIK_Capability ||
2925+
getPointerInterpretation() == PIK_SealedCapability;
29182926
}
29192927

29202928
std::optional<PointerInterpretationKind> getPointerInterpretation() const {

clang/include/clang/Basic/Attr.td

+5
Original file line numberDiff line numberDiff line change
@@ -873,6 +873,11 @@ def MinimumStack : InheritableAttr, TargetSpecificAttr<TargetRISCV> {
873873
let Documentation = [Undocumented];
874874
}
875875

876+
def CHERISealedCapability : TypeAttr {
877+
let Spellings = [CustomKeyword<"__sealed_capability">];
878+
let Documentation = [Undocumented];
879+
}
880+
876881
def InterruptState : InheritableAttr, TargetSpecificAttr<TargetRISCV> {
877882
let Spellings = [CXX11<"cheriot", "interrupt_state">,
878883
C2x<"cheriot", "interrupt_state">,

clang/include/clang/Basic/Builtins.def

+5-4
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
// D -> volatile
7373
// R -> restrict
7474
// m -> memory capability
75+
// l -> sealed capability
7576

7677
// The third value provided to the macro specifies information about attributes
7778
// of the function. These must be kept in sync with the predicates in the
@@ -1699,17 +1700,17 @@ BUILTIN(__builtin_cheri_offset_set, "v*mvC*mY", "nct")
16991700
BUILTIN(__builtin_cheri_perms_and, "v*mvC*mz", "nct")
17001701
BUILTIN(__builtin_cheri_perms_check, "vvC*mCz", "nct")
17011702
BUILTIN(__builtin_cheri_perms_get, "zvC*m", "nct")
1702-
BUILTIN(__builtin_cheri_seal, "v*mvC*mvC*m", "nct")
1703-
BUILTIN(__builtin_cheri_seal_entry, "v*mvC*m", "nct")
1704-
BUILTIN(__builtin_cheri_sealed_get, "bvC*m", "nct")
1703+
BUILTIN(__builtin_cheri_seal, "v*lvC*mvC*m", "nct")
1704+
BUILTIN(__builtin_cheri_seal_entry, "v*lvC*m", "nct")
1705+
BUILTIN(__builtin_cheri_sealed_get, "bvC*l", "nct")
17051706
BUILTIN(__builtin_cheri_subset_test, "bvC*mvC*m", "nct")
17061707
BUILTIN(__builtin_cheri_tag_clear, "v*mvC*m", "nct")
17071708
BUILTIN(__builtin_cheri_tag_get, "bvC*m", "nct")
17081709
BUILTIN(__builtin_cheri_tag_get_temporal, "bvC*m", "nct")
17091710
BUILTIN(__builtin_cheri_top_get, "zvC*m", "nct")
17101711
BUILTIN(__builtin_cheri_type_check, "vvC*mvC*m", "nct")
17111712
BUILTIN(__builtin_cheri_type_get, "YvC*m", "nct")
1712-
BUILTIN(__builtin_cheri_unseal, "v*mvC*mvC*m", "nct")
1713+
BUILTIN(__builtin_cheri_unseal, "v*mvC*lvC*m", "nct")
17131714

17141715
BUILTIN(__builtin_cheri_callback_create, "v.", "nct")
17151716

clang/include/clang/Basic/DiagnosticParseKinds.td

+2-2
Original file line numberDiff line numberDiff line change
@@ -319,8 +319,8 @@ def err_expected_selector_for_method : Error<
319319
"expected selector for Objective-C method">;
320320
def err_expected_property_name : Error<"expected property name">;
321321

322-
def warn_cheri_capability_qualifier_location : Warning<
323-
"use of __capability before the pointer type is deprecated">,
322+
def warn_cheri_qualifier_location : Warning<
323+
"use of %0 before the pointer type is deprecated">,
324324
InGroup<DeprecatedDeclarations>;
325325

326326
def err_unexpected_at : Error<"unexpected '@' in program">;

clang/include/clang/Basic/DiagnosticSemaKinds.td

+30-4
Original file line numberDiff line numberDiff line change
@@ -1336,10 +1336,22 @@ def note_cheri_func_decl_add_types : Note<
13361336
def err_cheri_capability_qualifier_not_supported : Error<
13371337
"use of __capability is not supported without CHERI; "
13381338
"specify an appropriate -march= or -mcpu=">;
1339-
def err_cheri_capability_qualifier_ambiguous : Error<
1340-
"use of __capability is ambiguous">;
1341-
def err_cheri_capability_qualifier_pointers_only : Error<
1342-
"__capability only applies to pointers; type here is %0">;
1339+
def err_cheri_sealed_qualifier_not_supported : Error<
1340+
"use of __sealed_capability is not supported without CHERI; "
1341+
"specify an appropriate -march= or -mcpu=">;
1342+
def err_cheri_qualifier_ambiguous : Error<
1343+
"use of %0 is ambiguous">;
1344+
def err_cheri_qualifier_pointers_only : Error<
1345+
"%0 only applies to pointers; type here is %1">;
1346+
def err_bad_cxx_cast_sealed_qualifier : Error<
1347+
"%select{const_cast|static_cast|reinterpret_cast|dynamic_cast|C-style cast|"
1348+
"functional-style cast|addrspace_cast}0 from %1 to %2 changes sealed qualifier">;
1349+
def err_sealed_pointer_cast : Error<
1350+
"cast from %0 to %1 changes sealed qualifier">;
1351+
def err_typecheck_expect_sealed_operand : Error<
1352+
"operand of type %0 where sealed capability is required">;
1353+
def err_typecheck_expect_unsealed_operand : Error<
1354+
"operand of type %0 where unsealed capability is required">;
13431355

13441356
def err_objc_var_decl_inclass :
13451357
Error<"cannot declare variable inside @interface or @protocol">;
@@ -7306,6 +7318,20 @@ def err_typecheck_convert_ptr_to_cap_unrelated_type: Error<"cannot implicitly "
73067318
"or explicitly convert non-capability%select{| reference to}2 type %0 to "
73077319
"unrelated capability type %1">;
73087320

7321+
def err_typecheck_convert_sealed_to_ptr: Error<"converting sealed"
7322+
"%select{| reference to}2 type %0 to non-sealed type %1 without "
7323+
"an explicit unsealing">;
7324+
def err_typecheck_convert_ptr_to_sealed: Error<"converting unsealed"
7325+
"%select{| reference to}2 type %0 to sealed type %1 without "
7326+
"an explicit sealing">;
7327+
def err_sealed_reference: Error<"reference type %0 cannot be sealed">;
7328+
def err_sealed_bad_operator: Error<"invalid operator applied to sealed type %0">;
7329+
def err_sealed_this_pointer: Error<"sealed type %0 cannot be used as 'this' pointer for non-static method calls">;
7330+
def err_sealed_func_pointer: Error<"sealed type %0 cannot be called">;
7331+
def err_sealed_member_access: Error<"cannot access members of sealed type %0">;
7332+
7333+
7334+
73097335
def warn_uintcap_bitwise_and : Warning<
73107336
"using bitwise and on a capability type may give surprising results; "
73117337
"if this is an alignment check use __builtin_{is_aligned,align_up,align_down}(); "

clang/include/clang/Basic/Features.def

+2
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,8 @@ FEATURE(experimental_library, LangOpts.ExperimentalLibrary)
242242
FEATURE(capabilities, PP.getTargetInfo().SupportsCapabilities())
243243
FEATURE(pointer_interpretation, PP.getTargetInfo().SupportsCapabilities())
244244
FEATURE(cheri_casts, PP.getTargetInfo().SupportsCapabilities())
245+
// CHERI typed sealed pointers
246+
EXTENSION(cheri_sealed_pointers, PP.getTargetInfo().SupportsCapabilities())
245247

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

clang/include/clang/Basic/TokenKinds.def

+3
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,9 @@ KEYWORD(__cheri_input , KEYALL)
341341
// CHERIoT Predefine Expr
342342
KEYWORD(__cheriot_minimum_stack__ , KEYALL)
343343

344+
// Cheriot Qualifiers
345+
KEYWORD(__sealed_capability , KEYALL)
346+
344347
// C++ 2.11p1: Keywords.
345348
KEYWORD(asm , KEYCXX|KEYGNU)
346349
KEYWORD(bool , BOOLSUPPORT|KEYC2X)

clang/include/clang/Parse/Parser.h

+1
Original file line numberDiff line numberDiff line change
@@ -2976,6 +2976,7 @@ class Parser : public CodeCompletionHandler {
29762976
void ParseOpenCLQualifiers(ParsedAttributes &Attrs);
29772977
void ParseNullabilityTypeSpecifiers(ParsedAttributes &attrs);
29782978
void ParseCapabilityQualifier(ParsedAttributes &Attrs);
2979+
void ParseCheriotSealedQualifier(ParsedAttributes &Attrs);
29792980
void ParseCUDAFunctionAttributes(ParsedAttributes &attrs);
29802981
bool isHLSLQualifier(const Token &Tok) const;
29812982
void ParseHLSLQualifiers(ParsedAttributes &Attrs);

clang/lib/AST/ASTContext.cpp

+4-3
Original file line numberDiff line numberDiff line change
@@ -11768,14 +11768,15 @@ static QualType DecodeTypeFromStr(const char *&Str, const ASTContext &Context,
1176811768
case 'R':
1176911769
Type = Type.withRestrict();
1177011770
break;
11771+
case 'l':
1177111772
case 'm': {
1177211773
Qualifiers Qs = Type.getQualifiers();
1177311774
if (const auto *PT = Type->getAs<PointerType>())
1177411775
Type = Context.getPointerType(PT->getPointeeType(), PIK_Capability);
1177511776
else if (const auto *LRT = Type->getAs<LValueReferenceType>())
11776-
Type = Context.getLValueReferenceType(LRT->getPointeeTypeAsWritten(),
11777-
LRT->isSpelledAsLValue(),
11778-
PIK_Capability);
11777+
Type = Context.getLValueReferenceType(
11778+
LRT->getPointeeTypeAsWritten(), LRT->isSpelledAsLValue(),
11779+
c == 'l' ? PIK_SealedCapability : PIK_Capability);
1177911780
else {
1178011781
const auto *BT = Type->getAs<BuiltinType>();
1178111782
assert(BT &&

clang/lib/AST/ItaniumMangle.cpp

+15-4
Original file line numberDiff line numberDiff line change
@@ -3594,7 +3594,10 @@ void CXXNameMangler::mangleType(const SubstTemplateTypeParmPackType *T) {
35943594

35953595
// <type> ::= P <type> # pointer-to
35963596
void CXXNameMangler::mangleType(const PointerType *T) {
3597-
if (Context.shouldMangleCapabilityQualifier() && T->isCHERICapability())
3597+
if (T->getPointerInterpretation() == PIK_SealedCapability)
3598+
Out << "U19__sealed_capability";
3599+
else if (Context.shouldMangleCapabilityQualifier() &&
3600+
T->getPointerInterpretation() == PIK_Capability)
35983601
Out << "U12__capability";
35993602
Out << 'P';
36003603
mangleType(T->getPointeeType());
@@ -3606,16 +3609,21 @@ void CXXNameMangler::mangleType(const ObjCObjectPointerType *T) {
36063609

36073610
// <type> ::= R <type> # reference-to
36083611
void CXXNameMangler::mangleType(const LValueReferenceType *T) {
3609-
if (Context.shouldMangleCapabilityQualifier() && T->isCHERICapability())
3612+
if (T->getPointerInterpretation() == PIK_SealedCapability)
3613+
Out << "U19__sealed_capability";
3614+
else if (Context.shouldMangleCapabilityQualifier() &&
3615+
T->getPointerInterpretation() == PIK_Capability)
36103616
Out << "U12__capability";
36113617
Out << 'R';
36123618
mangleType(T->getPointeeType());
36133619
}
36143620

36153621
// <type> ::= O <type> # rvalue reference-to (C++0x)
36163622
void CXXNameMangler::mangleType(const RValueReferenceType *T) {
3617-
if (Context.shouldMangleCapabilityQualifier() && T->isCHERICapability())
3623+
if (Context.shouldMangleCapabilityQualifier() && T->isCHERICapability()) {
3624+
assert(T->getPointerInterpretation() != PIK_SealedCapability);
36183625
Out << "U12__capability";
3626+
}
36193627
Out << 'O';
36203628
mangleType(T->getPointeeType());
36213629
}
@@ -4057,7 +4065,10 @@ void CXXNameMangler::mangleType(const DependentAddressSpaceType *T) {
40574065
}
40584066

40594067
void CXXNameMangler::mangleType(const DependentPointerType *T) {
4060-
if (Context.shouldMangleCapabilityQualifier() && T->isCHERICapability())
4068+
if (T->getPointerInterpretation() == PIK_SealedCapability)
4069+
Out << "U19__sealed_capability";
4070+
else if (Context.shouldMangleCapabilityQualifier() &&
4071+
T->getPointerInterpretation() == PIK_Capability)
40614072
Out << "U12__capability";
40624073
mangleType(T->getPointerType());
40634074
}

clang/lib/AST/QualTypeNames.cpp

+2-3
Original file line numberDiff line numberDiff line change
@@ -379,12 +379,11 @@ QualType getFullyQualifiedType(QualType QT, const ASTContext &Ctx,
379379
bool WithGlobalNsPrefix) {
380380
// In case of myType* we need to strip the pointer first, fully
381381
// qualify and attach the pointer once again.
382-
if (isa<PointerType>(QT.getTypePtr())) {
382+
if (const auto *PT = dyn_cast<PointerType>(QT.getTypePtr())) {
383383
// Get the qualifiers.
384384
Qualifiers Quals = QT.getQualifiers();
385385
QT = getFullyQualifiedType(QT->getPointeeType(), Ctx, WithGlobalNsPrefix);
386-
QT = Ctx.getPointerType(QT, QT->isCHERICapabilityType(Ctx)
387-
? PIK_Capability : PIK_Integer);
386+
QT = Ctx.getPointerType(QT, PT->getPointerInterpretation());
388387
// Add back the qualifiers.
389388
QT = Ctx.getQualifiedType(QT, Quals);
390389
return QT;

clang/lib/AST/Type.cpp

+11
Original file line numberDiff line numberDiff line change
@@ -650,6 +650,17 @@ bool Type::isCHERICapabilityType(const ASTContext &Context,
650650
return false;
651651
}
652652

653+
bool Type::isCHERISealedCapabilityType(const ASTContext &Context) const {
654+
if (!isCHERICapabilityType(Context, false))
655+
return false;
656+
657+
const PointerType *PT = getAs<PointerType>();
658+
if (!PT)
659+
return false;
660+
661+
return PT->getPointerInterpretation() == PIK_SealedCapability;
662+
}
663+
653664
bool Type::isIntCapType() const {
654665
if (const BuiltinType *BT = dyn_cast<BuiltinType>(CanonicalType))
655666
return BT->getKind() == BuiltinType::IntCap ||

clang/lib/AST/TypePrinter.cpp

+26-17
Original file line numberDiff line numberDiff line change
@@ -353,20 +353,27 @@ void TypePrinter::printBefore(const Type *T,Qualifiers Quals, raw_ostream &OS) {
353353
}
354354

355355
// Print __capability
356-
if (!Policy.SuppressCapabilityQualifier) {
357-
if (const PointerType *PTy = dyn_cast<PointerType>(T)) {
358-
if (PTy->getPointerInterpretation() == PIK_Capability) {
359-
OS << " __capability";
360-
if (hasAfterQuals || !PrevPHIsEmpty.get())
361-
OS << " ";
362-
}
356+
if (const PointerType *PTy = dyn_cast<PointerType>(T)) {
357+
if (PTy->getPointerInterpretation() == PIK_Capability &&
358+
!Policy.SuppressCapabilityQualifier) {
359+
OS << " __capability";
360+
if (hasAfterQuals || !PrevPHIsEmpty.get())
361+
OS << " ";
362+
} else if (PTy->getPointerInterpretation() == PIK_SealedCapability) {
363+
OS << " __sealed_capability";
364+
if (hasAfterQuals || !PrevPHIsEmpty.get())
365+
OS << " ";
363366
}
364-
else if (const ReferenceType *RTy = dyn_cast<ReferenceType>(T)) {
365-
if (RTy->getPointerInterpretation() == PIK_Capability) {
366-
OS << " __capability";
367-
if (hasAfterQuals || !PrevPHIsEmpty.get())
368-
OS << " ";
369-
}
367+
} else if (const ReferenceType *RTy = dyn_cast<ReferenceType>(T)) {
368+
if (RTy->getPointerInterpretation() == PIK_Capability &&
369+
!Policy.SuppressCapabilityQualifier) {
370+
OS << " __capability";
371+
if (hasAfterQuals || !PrevPHIsEmpty.get())
372+
OS << " ";
373+
} else if (RTy->getPointerInterpretation() == PIK_SealedCapability) {
374+
OS << " __sealed_capability";
375+
if (hasAfterQuals || !PrevPHIsEmpty.get())
376+
OS << " ";
370377
}
371378
}
372379

@@ -651,10 +658,11 @@ void TypePrinter::printDependentPointerBefore(
651658

652659
void TypePrinter::printDependentPointerAfter(
653660
const DependentPointerType *T, raw_ostream &OS) {
654-
if (!Policy.SuppressCapabilityQualifier) {
655-
if (T->getPointerInterpretation() == PIK_Capability) {
656-
OS << " __capability";
657-
}
661+
if (T->getPointerInterpretation() == PIK_Capability &&
662+
!Policy.SuppressCapabilityQualifier) {
663+
OS << " __capability";
664+
} else if (T->getPointerInterpretation() == PIK_SealedCapability) {
665+
OS << " __sealed_capability";
658666
}
659667
printAfter(T->getPointerType(), OS);
660668
}
@@ -1858,6 +1866,7 @@ void TypePrinter::printAttributedAfter(const AttributedType *T,
18581866
case attr::SPtr:
18591867
case attr::UPtr:
18601868
case attr::CHERICapability:
1869+
case attr::CHERISealedCapability:
18611870
case attr::AddressSpace:
18621871
case attr::CmseNSCall:
18631872
case attr::AnnotateType:

clang/lib/Format/Format.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -1419,6 +1419,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
14191419
LLVMStyle.AlwaysBreakBeforeMultilineStrings = false;
14201420
LLVMStyle.AlwaysBreakTemplateDeclarations = FormatStyle::BTDS_MultiLine;
14211421
LLVMStyle.AttributeMacros.push_back("__capability");
1422+
LLVMStyle.AttributeMacros.push_back("__sealed_capability");
14221423
LLVMStyle.BitFieldColonSpacing = FormatStyle::BFCS_Both;
14231424
LLVMStyle.BinPackArguments = true;
14241425
LLVMStyle.BinPackParameters = true;

clang/lib/Format/FormatToken.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -628,7 +628,8 @@ struct FormatToken {
628628
return isOneOf(tok::kw_const, tok::kw_restrict, tok::kw_volatile,
629629
tok::kw___attribute, tok::kw__Nonnull, tok::kw__Nullable,
630630
tok::kw__Null_unspecified, tok::kw___ptr32, tok::kw___ptr64,
631-
tok::kw___capability, tok::kw___funcref, TT_AttributeMacro);
631+
tok::kw___capability, tok::kw___funcref, TT_AttributeMacro,
632+
tok::kw___sealed_capability);
632633
}
633634

634635
/// Determine whether the token is a simple-type-specifier.

0 commit comments

Comments
 (0)