Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
4 changes: 4 additions & 0 deletions clang/include/clang/AST/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -1140,6 +1140,10 @@ class QualType {
/// Return true if this is a trivially relocatable type.
bool isTriviallyRelocatableType(const ASTContext &Context) const;

/// Return true if this QualType has the attribute signaling that it
/// references a sealed type in CHERIoT.
bool hasCHERIoTSealedAttr() const;

/// Returns true if it is a class and it might be dynamic.
bool mayBeDynamicClass() const;

Expand Down
8 changes: 8 additions & 0 deletions clang/include/clang/Basic/Attr.td
Original file line number Diff line number Diff line change
Expand Up @@ -2906,6 +2906,14 @@ def CHERIOTSharedObject : DeclOrTypeAttr {
let Subjects = SubjectList<[GlobalVar], ErrorDiag>;
}

def CHERIoTSealedType : TypeAttr {
let Spellings = [C23<"cheriot", "sealed">, CXX11<"cheriot", "sealed">,
GNU<"cheriot_sealed">];
let Documentation = [CHERIoTSealedTypeDocs];
let Args = [StringArgument<"CompartmentName">,
StringArgument<"SealingTypeName">];
}

def CHERINoSubobjectBounds : DeclOrTypeAttr {
let Spellings = [GNU<"cheri_no_subobject_bounds">,
CXX11<"cheri","no_subobject_bounds">,
Expand Down
16 changes: 16 additions & 0 deletions clang/include/clang/Basic/AttrDocs.td
Original file line number Diff line number Diff line change
Expand Up @@ -1540,6 +1540,22 @@ example, `"RWcm"` and `"mcWR"` are both valid and entail the same permissions.
}];
}

def CHERIoTSealedTypeDocs : Documentation {
let Category = DocCatVariable;
let Heading = "cheriot_sealed";
let Content = [{
Used to set a type as sealed: this means that values of the attributed type
will be understood as sealed values. The only operation valid on a sealed value
is to take its address. Doing so will result in a ``<T> *
__sealed_capability``, where ``<T>`` is the attributed type.

**Usage**: ``__attribute__((cheriot_sealed("<compartment_name>",
"<sealing_key_name>")))`` where `"<compartment_name>"` is the name of the
compartment that is allowed to unseal the value and `<sealing_key_name>` is the
name of the chosen sealing key exposed from the compartment. The CXX11 and
C23-style attributes syntax (``[[cheriot::sealed]]``) is valid as well.
}];
}
def ObjCMethodFamilyDocs : Documentation {
let Category = DocCatFunction;
let Content = [{
Expand Down
5 changes: 5 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,11 @@ def warn_cheriot_global_cap_import_non_volatile : Warning<
"global variable definition '%0' has attribute %1 but is not qualified as `volatile`">;
def err_cheriot_global_cap_import_initialized: Error<
"global variable definition '%0' with attribute %1 cannot have an initializer">;
def err_cheriot_non_addr_of_expr_on_sealed
: Error<
"the only valid operation on a sealed value is to take its address">;
def err_cheriot_invalid_sealed_declaration
: Error<"cannot declare a sealed variable as %0">;

// C99 variable-length arrays
def ext_vla : Extension<"variable length arrays are a C99 feature">,
Expand Down
23 changes: 23 additions & 0 deletions clang/lib/AST/Type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,29 @@ const IdentifierInfo* QualType::getBaseTypeIdentifier() const {
return nullptr;
}

bool QualType::hasCHERIoTSealedAttr() const {
if (isNull()) {
return false;
}

const auto *T = getTypePtr();

if (!T) {
return false;
}

if (T->hasAttr(attr::Kind::CHERIoTSealedType)) {
return true;
}

if (const auto *TT = T->getAs<TagType>()) {
auto *Decl = TT->getDecl();
return Decl && Decl->hasAttr<CHERIoTSealedTypeAttr>();
}

return false;
}

bool QualType::mayBeDynamicClass() const {
const auto *ClassDecl = getTypePtr()->getPointeeCXXRecordDecl();
return ClassDecl && ClassDecl->mayBeDynamicClass();
Expand Down
87 changes: 87 additions & 0 deletions clang/lib/CodeGen/CodeGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5749,6 +5749,93 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D,
}

llvm::Type* InitType = Init->getType();
if (ASTTy.hasCHERIoTSealedAttr()) {
// In this case, the global variable refers to a to-be-sealed value of type
// <T>. We need to:
// 1. Get (or create) the type of the sealed pointer, which has a fixed
// shape: `__Sealed_<T> {uint32_t sealing_key_ptr; uint32_t padding; <T>
// body}`;
// 2. Change the type of the global variable to `__Sealed_<T>`;
// 3. Create the initializer for the global variable.

auto *Module = &getModule();
auto *Ctxt = &getLLVMContext();
auto *Int32Ty = llvm::Type::getInt32Ty(*Ctxt);
CHERIoTSealedTypeAttr *Attr = nullptr;
std::string TypeName;

if (auto *TT = ASTTy->getAsTagDecl();
TT && TT->hasAttr<CHERIoTSealedTypeAttr>()) {
Attr = TT->getAttr<CHERIoTSealedTypeAttr>();
TypeName = TT->getNameAsString();
}

if (const auto *TT = ASTTy->getAs<TypedefType>();
TT && TT->getDecl() &&
TT->getDecl()->hasAttr<CHERIoTSealedTypeAttr>()) {
auto *TD = TT->getDecl();
Attr = TD->getAttr<CHERIoTSealedTypeAttr>();
TypeName = TD->getNameAsString();
}

llvm::Comdat *C = Module->getOrInsertComdat(D->getName());
C->setSelectionKind(llvm::Comdat::Any);

llvm::Type *SealedStructElements[] = {Int32Ty, Int32Ty, Init->getType()};
auto *SealedStructType = llvm::StructType::create(
*Ctxt, SealedStructElements, ("struct.__Sealed_" + TypeName));

auto SealingKeyName =
("__export.sealing_type." + Attr->getCompartmentName() + "." +
Attr->getSealingTypeName())
.str();

auto *SealingKeyRef = Module->getGlobalVariable(SealingKeyName);

if (!SealingKeyRef) {
llvm::GlobalVariable *SealingKeyVarDef = new llvm::GlobalVariable(
*Module, Int32Ty, false, llvm::GlobalValue::ExternalLinkage, nullptr,
SealingKeyName);
SealingKeyVarDef->setDSOLocal(true);
SealingKeyVarDef->setAlignment(llvm::Align(4));
SealingKeyRef = Module->getGlobalVariable(SealingKeyName);
assert(SealingKeyRef && "is supposed to exist now!");
}

auto *CastedPointer =
llvm::ConstantExpr::getPointerCast(SealingKeyRef, Int32Ty);

llvm::Constant *Values[] = {CastedPointer,
llvm::ConstantInt::get(Int32Ty, 0), Init};

// Create the initializer.
auto *Init = llvm::ConstantStruct::get(SealedStructType, Values);

llvm::Constant *Entry =
GetAddrOfGlobalVar(D, Init->getType(), ForDefinition_t(!IsTentative));
auto *GV = dyn_cast<llvm::GlobalVariable>(Entry);
GV->setSection(".sealed_objects");
GV->setDSOLocal(true);
GV->setLinkage(llvm::GlobalValue::LinkOnceODRLinkage);
GV->setAlignment(llvm::Align(4));
GV->setComdat(C);
GV->setInitializer(Init);
GV->addAttribute(llvm::CHERIoTSealedValueAttr::getAttrName());

addCompilerUsedGlobal(GV);

if (emitter)
emitter->finalize(GV);

SanitizerMD->reportGlobal(GV, *D, NeedsGlobalCtor);

// Emit global variable debug information.
if (CGDebugInfo *DI = getModuleDebugInfo())
if (getCodeGenOpts().hasReducedDebugInfo())
DI->EmitGlobalVariable(GV, D);
return;
}

llvm::Constant *Entry =
GetAddrOfGlobalVar(D, InitType, ForDefinition_t(!IsTentative));

Expand Down
19 changes: 19 additions & 0 deletions clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14947,6 +14947,25 @@ Sema::DeclGroupPtrTy Sema::FinalizeDeclaratorGroup(Scope *S, const DeclSpec &DS,
for (Decl *D : Group) {
if (!D)
continue;

auto *VD = dyn_cast<VarDecl>(D);

/// CHERIoT-specific check. If a variable is declared with a sealed type, it
/// can't have external storage, since its initializer must be available in
/// the unit.
///
/// For example
/// ```
/// typedef int MySealedType __attribute__((cheriot_sealed("MyCompartment",
/// "MySealingKeyType"))); extern MySealedType mySealedObject;
/// ```
/// would not really make sense.
if (VD && VD->getType().hasCHERIoTSealedAttr() &&
VD->hasExternalStorage()) {
Diag(D->getLocation(), diag::err_cheriot_invalid_sealed_declaration)
<< "external";
}

// Check if the Decl has been declared in '#pragma omp declare target'
// directive and has static storage duration.
if (auto *VD = dyn_cast<VarDecl>(D);
Expand Down
27 changes: 27 additions & 0 deletions clang/lib/Sema/SemaDeclAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2227,6 +2227,30 @@ static void handleCHERIOTSharedObject(Sema &S, Decl *D, const ParsedAttr &Attr,
S.Context, Attr, ObjectName, OwnedPermissions));
}

static void handleCHERIoTSealedType(Sema &S, Decl *D, const ParsedAttr &Attr,
Sema::DeclAttributeLocation DAL) {

auto *TDecl = dyn_cast<TypeDecl>(D);
if (TDecl) {
StringRef CompartmentName;
SourceLocation CompartmentNameLiteralLoc;
if (!S.checkStringLiteralArgumentAttr(Attr, 0, CompartmentName,
&CompartmentNameLiteralLoc))
return;
StringRef SealingTypeName;
SourceLocation SealingTypeNameLiteralLoc;

if (!S.checkStringLiteralArgumentAttr(Attr, 1, SealingTypeName,
&SealingTypeNameLiteralLoc))
return;

// Here we simply copy the attribute.
TDecl->addAttr(::new (S.Context) CHERIoTSealedTypeAttr(
S.Context, Attr, CompartmentName, SealingTypeName));
} else // Ignore if it's not a type definition.
S.Diag(Attr.getLoc(), diag::warn_attribute_ignored) << Attr.getAttrName();
}

static void handleCHERICompartmentName(Sema &S, Decl *D, const ParsedAttr &Attr,
Sema::DeclAttributeLocation DAL) {
// cheri_compartment is both:
Expand Down Expand Up @@ -7480,6 +7504,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
case ParsedAttr::AT_CHERIOTSharedObject:
handleCHERIOTSharedObject(S, D, AL, DAL);
break;
case ParsedAttr::AT_CHERIoTSealedType:
handleCHERIoTSealedType(S, D, AL, DAL);
break;
case ParsedAttr::AT_InterruptState:
handleInterruptState(S, D, AL);
break;
Expand Down
48 changes: 48 additions & 0 deletions clang/lib/Sema/SemaExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -704,6 +704,12 @@ ExprResult Sema::DefaultLvalueConversion(Expr *E) {
QualType T = E->getType();
assert(!T.isNull() && "r-value conversion on typeless expression?");

// CHERIoT-specific check.
if (T.hasCHERIoTSealedAttr() && !isUnevaluatedContext()) {
return ExprError(
Diag(E->getExprLoc(), diag::err_cheriot_non_addr_of_expr_on_sealed));
}

// lvalue-to-rvalue conversion cannot be applied to types that decay to
// pointers (i.e. function or array types).
if (T->canDecayToPointerType())
Expand Down Expand Up @@ -4906,6 +4912,11 @@ ExprResult Sema::ActOnArraySubscriptExpr(Scope *S, Expr *base,
MultiExprArg ArgExprs,
SourceLocation rbLoc) {

// CHERIoT-specific check.
if (base->getType().hasCHERIoTSealedAttr() && !isUnevaluatedContext()) {
return ExprError(Diag(lbLoc, diag::err_cheriot_non_addr_of_expr_on_sealed));
}

if (base && !base->getType().isNull() &&
base->hasPlaceholderType(BuiltinType::ArraySection)) {
auto *AS = cast<ArraySectionExpr>(base);
Expand Down Expand Up @@ -8535,6 +8546,21 @@ QualType Sema::CheckConditionalOperands(ExprResult &Cond, ExprResult &LHS,
ExprObjectKind &OK,
SourceLocation QuestionLoc) {

// CHERIoT-specific check.
if (!isUnevaluatedContext()) {
if (LHS.get()->getType().hasCHERIoTSealedAttr()) {
Diag(LHS.get()->getExprLoc(),
diag::err_cheriot_non_addr_of_expr_on_sealed);
return QualType();
}

if (RHS.get()->getType().hasCHERIoTSealedAttr()) {
Diag(RHS.get()->getExprLoc(),
diag::err_cheriot_non_addr_of_expr_on_sealed);
return QualType();
}
}

ExprResult LHSResult = CheckPlaceholderExpr(LHS.get());
if (!LHSResult.isUsable()) return QualType();
LHS = LHSResult;
Expand Down Expand Up @@ -14717,6 +14743,9 @@ QualType Sema::CheckAddressOfOperand(ExprResult &OrigOp, SourceLocation OpLoc) {
CheckAddressOfPackedMember(op);

PointerInterpretationKind PIK = PointerInterpretationForBaseExpr(op);
if (op->getType().hasCHERIoTSealedAttr()) {
PIK = PIK_SealedCapability;
}
return Context.getPointerType(op->getType(), PIK);
}

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

// CHERIoT-specific check.
if (!isUnevaluatedContext()) {
if (LHSExpr->getType().hasCHERIoTSealedAttr()) {
return ExprError(Diag(LHS.get()->getExprLoc(),
diag::err_cheriot_non_addr_of_expr_on_sealed));
}

if (RHSExpr->getType().hasCHERIoTSealedAttr()) {
return ExprError(Diag(RHS.get()->getExprLoc(),
diag::err_cheriot_non_addr_of_expr_on_sealed));
}
}

// We want to end up calling one of SemaPseudoObject::checkAssignment
// (if the LHS is a pseudo-object), BuildOverloadedBinOp (if
// both expressions are overloadable or either is type-dependent),
Expand Down Expand Up @@ -16097,6 +16139,12 @@ bool Sema::isQualifiedMemberAccess(Expr *E) {
ExprResult Sema::BuildUnaryOp(Scope *S, SourceLocation OpLoc,
UnaryOperatorKind Opc, Expr *Input,
bool IsAfterAmp) {
// CHERIoT-specific check.
if (Input->getType().hasCHERIoTSealedAttr() && (Opc != UO_AddrOf) &&
!isUnevaluatedContext()) {
return ExprError(Diag(OpLoc, diag::err_cheriot_non_addr_of_expr_on_sealed));
}

// First things first: handle placeholders so that the
// overloaded-operator check considers the right type.
if (const BuiltinType *pty = Input->getType()->getAsPlaceholderType()) {
Expand Down
15 changes: 15 additions & 0 deletions clang/lib/Sema/SemaExprCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6999,6 +6999,21 @@ QualType Sema::CXXCheckConditionalOperands(ExprResult &Cond, ExprResult &LHS,
ExprResult &RHS, ExprValueKind &VK,
ExprObjectKind &OK,
SourceLocation QuestionLoc) {
// CHERIoT-specific check.
if (!isUnevaluatedContext()) {
if (LHS.get()->getType().hasCHERIoTSealedAttr()) {
Diag(LHS.get()->getExprLoc(),
diag::err_cheriot_non_addr_of_expr_on_sealed);
return QualType();
}

if (RHS.get()->getType().hasCHERIoTSealedAttr()) {
Diag(RHS.get()->getExprLoc(),
diag::err_cheriot_non_addr_of_expr_on_sealed);
return QualType();
}
}

// FIXME: Handle C99's complex types, block pointers and Obj-C++ interface
// pointers.

Expand Down
5 changes: 5 additions & 0 deletions clang/lib/Sema/SemaExprMember.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -999,6 +999,11 @@ Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType,
R.getLookupNameInfo(), TemplateArgs);

QualType BaseType = BaseExprType;
// CHERIoT-specific check.
if (BaseType.hasCHERIoTSealedAttr() && !isUnevaluatedContext()) {
return ExprError(Diag(OpLoc, diag::err_cheriot_non_addr_of_expr_on_sealed));
}

if (IsArrow) {
assert(BaseType->isPointerType());
BaseType = BaseType->castAs<PointerType>()->getPointeeType();
Expand Down
Loading