Skip to content

Commit bee533d

Browse files
committed
[CHERIoT]: Add cheriot_sealed attribute
1 parent d545ca5 commit bee533d

File tree

21 files changed

+715
-39
lines changed

21 files changed

+715
-39
lines changed

clang/include/clang/AST/Type.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1140,6 +1140,10 @@ class QualType {
11401140
/// Return true if this is a trivially relocatable type.
11411141
bool isTriviallyRelocatableType(const ASTContext &Context) const;
11421142

1143+
/// Return true if this QualType has the attribute signaling that it
1144+
/// references a sealed type in CHERIoT.
1145+
bool hasCHERIoTSealedAttr() const;
1146+
11431147
/// Returns true if it is a class and it might be dynamic.
11441148
bool mayBeDynamicClass() const;
11451149

clang/include/clang/Basic/Attr.td

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2906,6 +2906,14 @@ def CHERIOTSharedObject : DeclOrTypeAttr {
29062906
let Subjects = SubjectList<[GlobalVar], ErrorDiag>;
29072907
}
29082908

2909+
def CHERIoTSealedType : TypeAttr {
2910+
let Spellings = [C23<"cheriot", "sealed">, CXX11<"cheriot", "sealed">,
2911+
GNU<"cheriot_sealed">];
2912+
let Documentation = [CHERIoTSealedTypeDocs];
2913+
let Args = [StringArgument<"CompartmentName">,
2914+
StringArgument<"SealingTypeName">];
2915+
}
2916+
29092917
def CHERINoSubobjectBounds : DeclOrTypeAttr {
29102918
let Spellings = [GNU<"cheri_no_subobject_bounds">,
29112919
CXX11<"cheri","no_subobject_bounds">,

clang/include/clang/Basic/AttrDocs.td

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1540,6 +1540,22 @@ example, `"RWcm"` and `"mcWR"` are both valid and entail the same permissions.
15401540
}];
15411541
}
15421542

1543+
def CHERIoTSealedTypeDocs : Documentation {
1544+
let Category = DocCatVariable;
1545+
let Heading = "cheriot_sealed";
1546+
let Content = [{
1547+
Used to set a type as sealed: this means that values of the attributed type
1548+
will be understood as sealed values. The only operation valid on a sealed value
1549+
is to take its address. Doing so will result in a ``<T> *
1550+
__sealed_capability``, where ``<T>`` is the attributed type.
1551+
1552+
**Usage**: ``__attribute__((cheriot_sealed("<compartment_name>",
1553+
"<sealing_key_name>")))`` where `"<compartment_name>"` is the name of the
1554+
compartment that is allowed to unseal the value and `<sealing_key_name>` is the
1555+
name of the chosen sealing key exposed from the compartment. The CXX11 and
1556+
C23-style attributes syntax (``[[cheriot::sealed]]``) is valid as well.
1557+
}];
1558+
}
15431559
def ObjCMethodFamilyDocs : Documentation {
15441560
let Category = DocCatFunction;
15451561
let Content = [{

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,11 @@ def warn_cheriot_global_cap_import_non_volatile : Warning<
150150
"global variable definition '%0' has attribute %1 but is not qualified as `volatile`">;
151151
def err_cheriot_global_cap_import_initialized: Error<
152152
"global variable definition '%0' with attribute %1 cannot have an initializer">;
153+
def err_cheriot_non_addr_of_expr_on_sealed
154+
: Error<
155+
"the only valid operation on a sealed value is to take its address">;
156+
def err_cheriot_invalid_sealed_declaration
157+
: Error<"cannot declare a sealed variable as %0">;
153158

154159
// C99 variable-length arrays
155160
def ext_vla : Extension<"variable length arrays are a C99 feature">,

clang/lib/AST/Type.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,29 @@ const IdentifierInfo* QualType::getBaseTypeIdentifier() const {
120120
return nullptr;
121121
}
122122

123+
bool QualType::hasCHERIoTSealedAttr() const {
124+
if (isNull()) {
125+
return false;
126+
}
127+
128+
const auto *T = getTypePtr();
129+
130+
if (!T) {
131+
return false;
132+
}
133+
134+
if (T->hasAttr(attr::Kind::CHERIoTSealedType)) {
135+
return true;
136+
}
137+
138+
if (const auto *TT = T->getAs<TagType>()) {
139+
auto *Decl = TT->getDecl();
140+
return Decl && Decl->hasAttr<CHERIoTSealedTypeAttr>();
141+
}
142+
143+
return false;
144+
}
145+
123146
bool QualType::mayBeDynamicClass() const {
124147
const auto *ClassDecl = getTypePtr()->getPointeeCXXRecordDecl();
125148
return ClassDecl && ClassDecl->mayBeDynamicClass();

clang/lib/CodeGen/CodeGenModule.cpp

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5749,6 +5749,93 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D,
57495749
}
57505750

57515751
llvm::Type* InitType = Init->getType();
5752+
if (ASTTy.hasCHERIoTSealedAttr()) {
5753+
// In this case, the global variable refers to a to-be-sealed value of type
5754+
// <T>. We need to:
5755+
// 1. Get (or create) the type of the sealed pointer, which has a fixed
5756+
// shape: `__Sealed_<T> {uint32_t sealing_key_ptr; uint32_t padding; <T>
5757+
// body}`;
5758+
// 2. Change the type of the global variable to `__Sealed_<T>`;
5759+
// 3. Create the initializer for the global variable.
5760+
5761+
auto *Module = &getModule();
5762+
auto *Ctxt = &getLLVMContext();
5763+
auto *Int32Ty = llvm::Type::getInt32Ty(*Ctxt);
5764+
CHERIoTSealedTypeAttr *Attr = nullptr;
5765+
std::string TypeName;
5766+
5767+
if (auto *TT = ASTTy->getAsTagDecl();
5768+
TT && TT->hasAttr<CHERIoTSealedTypeAttr>()) {
5769+
Attr = TT->getAttr<CHERIoTSealedTypeAttr>();
5770+
TypeName = TT->getNameAsString();
5771+
}
5772+
5773+
if (const auto *TT = ASTTy->getAs<TypedefType>();
5774+
TT && TT->getDecl() &&
5775+
TT->getDecl()->hasAttr<CHERIoTSealedTypeAttr>()) {
5776+
auto *TD = TT->getDecl();
5777+
Attr = TD->getAttr<CHERIoTSealedTypeAttr>();
5778+
TypeName = TD->getNameAsString();
5779+
}
5780+
5781+
llvm::Comdat *C = Module->getOrInsertComdat(D->getName());
5782+
C->setSelectionKind(llvm::Comdat::Any);
5783+
5784+
llvm::Type *SealedStructElements[] = {Int32Ty, Int32Ty, Init->getType()};
5785+
auto *SealedStructType = llvm::StructType::create(
5786+
*Ctxt, SealedStructElements, ("struct.__Sealed_" + TypeName));
5787+
5788+
auto SealingKeyName =
5789+
("__export.sealing_type." + Attr->getCompartmentName() + "." +
5790+
Attr->getSealingTypeName())
5791+
.str();
5792+
5793+
auto *SealingKeyRef = Module->getGlobalVariable(SealingKeyName);
5794+
5795+
if (!SealingKeyRef) {
5796+
llvm::GlobalVariable *SealingKeyVarDef = new llvm::GlobalVariable(
5797+
*Module, Int32Ty, false, llvm::GlobalValue::ExternalLinkage, nullptr,
5798+
SealingKeyName);
5799+
SealingKeyVarDef->setDSOLocal(true);
5800+
SealingKeyVarDef->setAlignment(llvm::Align(4));
5801+
SealingKeyRef = Module->getGlobalVariable(SealingKeyName);
5802+
assert(SealingKeyRef && "is supposed to exist now!");
5803+
}
5804+
5805+
auto *CastedPointer =
5806+
llvm::ConstantExpr::getPointerCast(SealingKeyRef, Int32Ty);
5807+
5808+
llvm::Constant *Values[] = {CastedPointer,
5809+
llvm::ConstantInt::get(Int32Ty, 0), Init};
5810+
5811+
// Create the initializer.
5812+
auto *Init = llvm::ConstantStruct::get(SealedStructType, Values);
5813+
5814+
llvm::Constant *Entry =
5815+
GetAddrOfGlobalVar(D, Init->getType(), ForDefinition_t(!IsTentative));
5816+
auto *GV = dyn_cast<llvm::GlobalVariable>(Entry);
5817+
GV->setSection(".sealed_objects");
5818+
GV->setDSOLocal(true);
5819+
GV->setLinkage(llvm::GlobalValue::LinkOnceODRLinkage);
5820+
GV->setAlignment(llvm::Align(4));
5821+
GV->setComdat(C);
5822+
GV->setInitializer(Init);
5823+
GV->addAttribute(llvm::CHERIoTSealedValueAttr::getAttrName());
5824+
5825+
addCompilerUsedGlobal(GV);
5826+
5827+
if (emitter)
5828+
emitter->finalize(GV);
5829+
5830+
SanitizerMD->reportGlobal(GV, *D, NeedsGlobalCtor);
5831+
5832+
// Emit global variable debug information.
5833+
if (CGDebugInfo *DI = getModuleDebugInfo())
5834+
if (getCodeGenOpts().hasReducedDebugInfo())
5835+
DI->EmitGlobalVariable(GV, D);
5836+
return;
5837+
}
5838+
57525839
llvm::Constant *Entry =
57535840
GetAddrOfGlobalVar(D, InitType, ForDefinition_t(!IsTentative));
57545841

clang/lib/Sema/SemaDecl.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14947,6 +14947,25 @@ Sema::DeclGroupPtrTy Sema::FinalizeDeclaratorGroup(Scope *S, const DeclSpec &DS,
1494714947
for (Decl *D : Group) {
1494814948
if (!D)
1494914949
continue;
14950+
14951+
auto *VD = dyn_cast<VarDecl>(D);
14952+
14953+
/// CHERIoT-specific check. If a variable is declared with a sealed type, it
14954+
/// can't have external storage, since its initializer must be available in
14955+
/// the unit.
14956+
///
14957+
/// For example
14958+
/// ```
14959+
/// typedef int MySealedType __attribute__((cheriot_sealed("MyCompartment",
14960+
/// "MySealingKeyType"))); extern MySealedType mySealedObject;
14961+
/// ```
14962+
/// would not really make sense.
14963+
if (VD && VD->getType().hasCHERIoTSealedAttr() &&
14964+
VD->hasExternalStorage()) {
14965+
Diag(D->getLocation(), diag::err_cheriot_invalid_sealed_declaration)
14966+
<< "external";
14967+
}
14968+
1495014969
// Check if the Decl has been declared in '#pragma omp declare target'
1495114970
// directive and has static storage duration.
1495214971
if (auto *VD = dyn_cast<VarDecl>(D);

clang/lib/Sema/SemaDeclAttr.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2227,6 +2227,30 @@ static void handleCHERIOTSharedObject(Sema &S, Decl *D, const ParsedAttr &Attr,
22272227
S.Context, Attr, ObjectName, OwnedPermissions));
22282228
}
22292229

2230+
static void handleCHERIoTSealedType(Sema &S, Decl *D, const ParsedAttr &Attr,
2231+
Sema::DeclAttributeLocation DAL) {
2232+
2233+
auto *TDecl = dyn_cast<TypeDecl>(D);
2234+
if (TDecl) {
2235+
StringRef CompartmentName;
2236+
SourceLocation CompartmentNameLiteralLoc;
2237+
if (!S.checkStringLiteralArgumentAttr(Attr, 0, CompartmentName,
2238+
&CompartmentNameLiteralLoc))
2239+
return;
2240+
StringRef SealingTypeName;
2241+
SourceLocation SealingTypeNameLiteralLoc;
2242+
2243+
if (!S.checkStringLiteralArgumentAttr(Attr, 1, SealingTypeName,
2244+
&SealingTypeNameLiteralLoc))
2245+
return;
2246+
2247+
// Here we simply copy the attribute.
2248+
TDecl->addAttr(::new (S.Context) CHERIoTSealedTypeAttr(
2249+
S.Context, Attr, CompartmentName, SealingTypeName));
2250+
} else // Ignore if it's not a type definition.
2251+
S.Diag(Attr.getLoc(), diag::warn_attribute_ignored) << Attr.getAttrName();
2252+
}
2253+
22302254
static void handleCHERICompartmentName(Sema &S, Decl *D, const ParsedAttr &Attr,
22312255
Sema::DeclAttributeLocation DAL) {
22322256
// cheri_compartment is both:
@@ -7480,6 +7504,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
74807504
case ParsedAttr::AT_CHERIOTSharedObject:
74817505
handleCHERIOTSharedObject(S, D, AL, DAL);
74827506
break;
7507+
case ParsedAttr::AT_CHERIoTSealedType:
7508+
handleCHERIoTSealedType(S, D, AL, DAL);
7509+
break;
74837510
case ParsedAttr::AT_InterruptState:
74847511
handleInterruptState(S, D, AL);
74857512
break;

clang/lib/Sema/SemaExpr.cpp

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -704,6 +704,12 @@ ExprResult Sema::DefaultLvalueConversion(Expr *E) {
704704
QualType T = E->getType();
705705
assert(!T.isNull() && "r-value conversion on typeless expression?");
706706

707+
// CHERIoT-specific check.
708+
if (T.hasCHERIoTSealedAttr() && !isUnevaluatedContext()) {
709+
return ExprError(
710+
Diag(E->getExprLoc(), diag::err_cheriot_non_addr_of_expr_on_sealed));
711+
}
712+
707713
// lvalue-to-rvalue conversion cannot be applied to types that decay to
708714
// pointers (i.e. function or array types).
709715
if (T->canDecayToPointerType())
@@ -4906,6 +4912,11 @@ ExprResult Sema::ActOnArraySubscriptExpr(Scope *S, Expr *base,
49064912
MultiExprArg ArgExprs,
49074913
SourceLocation rbLoc) {
49084914

4915+
// CHERIoT-specific check.
4916+
if (base->getType().hasCHERIoTSealedAttr() && !isUnevaluatedContext()) {
4917+
return ExprError(Diag(lbLoc, diag::err_cheriot_non_addr_of_expr_on_sealed));
4918+
}
4919+
49094920
if (base && !base->getType().isNull() &&
49104921
base->hasPlaceholderType(BuiltinType::ArraySection)) {
49114922
auto *AS = cast<ArraySectionExpr>(base);
@@ -8535,6 +8546,21 @@ QualType Sema::CheckConditionalOperands(ExprResult &Cond, ExprResult &LHS,
85358546
ExprObjectKind &OK,
85368547
SourceLocation QuestionLoc) {
85378548

8549+
// CHERIoT-specific check.
8550+
if (!isUnevaluatedContext()) {
8551+
if (LHS.get()->getType().hasCHERIoTSealedAttr()) {
8552+
Diag(LHS.get()->getExprLoc(),
8553+
diag::err_cheriot_non_addr_of_expr_on_sealed);
8554+
return QualType();
8555+
}
8556+
8557+
if (RHS.get()->getType().hasCHERIoTSealedAttr()) {
8558+
Diag(RHS.get()->getExprLoc(),
8559+
diag::err_cheriot_non_addr_of_expr_on_sealed);
8560+
return QualType();
8561+
}
8562+
}
8563+
85388564
ExprResult LHSResult = CheckPlaceholderExpr(LHS.get());
85398565
if (!LHSResult.isUsable()) return QualType();
85408566
LHS = LHSResult;
@@ -14717,6 +14743,9 @@ QualType Sema::CheckAddressOfOperand(ExprResult &OrigOp, SourceLocation OpLoc) {
1471714743
CheckAddressOfPackedMember(op);
1471814744

1471914745
PointerInterpretationKind PIK = PointerInterpretationForBaseExpr(op);
14746+
if (op->getType().hasCHERIoTSealedAttr()) {
14747+
PIK = PIK_SealedCapability;
14748+
}
1472014749
return Context.getPointerType(op->getType(), PIK);
1472114750
}
1472214751

@@ -15659,6 +15688,19 @@ ExprResult Sema::BuildBinOp(Scope *S, SourceLocation OpLoc,
1565915688
LHSExpr = LHS.get();
1566015689
RHSExpr = RHS.get();
1566115690

15691+
// CHERIoT-specific check.
15692+
if (!isUnevaluatedContext()) {
15693+
if (LHSExpr->getType().hasCHERIoTSealedAttr()) {
15694+
return ExprError(Diag(LHS.get()->getExprLoc(),
15695+
diag::err_cheriot_non_addr_of_expr_on_sealed));
15696+
}
15697+
15698+
if (RHSExpr->getType().hasCHERIoTSealedAttr()) {
15699+
return ExprError(Diag(RHS.get()->getExprLoc(),
15700+
diag::err_cheriot_non_addr_of_expr_on_sealed));
15701+
}
15702+
}
15703+
1566215704
// We want to end up calling one of SemaPseudoObject::checkAssignment
1566315705
// (if the LHS is a pseudo-object), BuildOverloadedBinOp (if
1566415706
// both expressions are overloadable or either is type-dependent),
@@ -16097,6 +16139,12 @@ bool Sema::isQualifiedMemberAccess(Expr *E) {
1609716139
ExprResult Sema::BuildUnaryOp(Scope *S, SourceLocation OpLoc,
1609816140
UnaryOperatorKind Opc, Expr *Input,
1609916141
bool IsAfterAmp) {
16142+
// CHERIoT-specific check.
16143+
if (Input->getType().hasCHERIoTSealedAttr() && (Opc != UO_AddrOf) &&
16144+
!isUnevaluatedContext()) {
16145+
return ExprError(Diag(OpLoc, diag::err_cheriot_non_addr_of_expr_on_sealed));
16146+
}
16147+
1610016148
// First things first: handle placeholders so that the
1610116149
// overloaded-operator check considers the right type.
1610216150
if (const BuiltinType *pty = Input->getType()->getAsPlaceholderType()) {

clang/lib/Sema/SemaExprCXX.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6999,6 +6999,21 @@ QualType Sema::CXXCheckConditionalOperands(ExprResult &Cond, ExprResult &LHS,
69996999
ExprResult &RHS, ExprValueKind &VK,
70007000
ExprObjectKind &OK,
70017001
SourceLocation QuestionLoc) {
7002+
// CHERIoT-specific check.
7003+
if (!isUnevaluatedContext()) {
7004+
if (LHS.get()->getType().hasCHERIoTSealedAttr()) {
7005+
Diag(LHS.get()->getExprLoc(),
7006+
diag::err_cheriot_non_addr_of_expr_on_sealed);
7007+
return QualType();
7008+
}
7009+
7010+
if (RHS.get()->getType().hasCHERIoTSealedAttr()) {
7011+
Diag(RHS.get()->getExprLoc(),
7012+
diag::err_cheriot_non_addr_of_expr_on_sealed);
7013+
return QualType();
7014+
}
7015+
}
7016+
70027017
// FIXME: Handle C99's complex types, block pointers and Obj-C++ interface
70037018
// pointers.
70047019

0 commit comments

Comments
 (0)