Fixed issue where a CLC without global permissions removed load global
permission from the sealed loaded capability.
PiperOrigin-RevId: 707934877
Change-Id: Iea1f3eb08824c176852cdaae27aa46ded33a876e
diff --git a/cheriot/riscv_cheriot_instructions.cc b/cheriot/riscv_cheriot_instructions.cc
index d1b1637..5d4c3ef 100644
--- a/cheriot/riscv_cheriot_instructions.cc
+++ b/cheriot/riscv_cheriot_instructions.cc
@@ -401,23 +401,24 @@
auto *cd = GetCapDest(instruction, 0);
cd->Expand(context->db->Get<uint32_t>(0), context->db->Get<uint32_t>(1),
context->tag_db->Get<uint8_t>(0));
+ // If the source capability did not have load/store capability, invalidate.
+ if ((context->permissions & CapReg::kPermitLoadStoreCapability) == 0) {
+ cd->Invalidate();
+ }
if (cd->tag()) {
if ((context->permissions & CapReg::kPermitLoadGlobal) == 0) {
- cd->ClearPermissions(CapReg::kPermitGlobal | CapReg::kPermitLoadGlobal);
+ cd->ClearPermissions(CapReg::kPermitGlobal);
+ if (!cd->IsSealed()) cd->ClearPermissions(CapReg::kPermitLoadGlobal);
}
if (!cd->IsSealed() &&
((context->permissions & CapReg::kPermitLoadMutable) == 0)) {
cd->ClearPermissions(CapReg::kPermitStore | CapReg::kPermitLoadMutable);
}
- // If the source capability did not have load/store capability, invalidate.
- if ((context->permissions & CapReg::kPermitLoadStoreCapability) == 0) {
- cd->Invalidate();
- }
// If it's not a sealing cap, check for revocation.
- if (cd->tag() &&
- ((cd->permissions() & (CapReg::kPermitSeal | CapReg::kPermitUnseal |
- CapReg::kUserPerm0)) == 0)) {
- if (state->MustRevoke(cd->base())) {
+ if ((cd->permissions() & (CapReg::kPermitSeal | CapReg::kPermitUnseal |
+ CapReg::kUserPerm0)) == 0) {
+ auto granule_addr = cd->base() & ~((1ULL << CapReg::kGranuleShift) - 1);
+ if (state->MustRevoke(granule_addr)) {
cd->Invalidate();
}
}
diff --git a/cheriot/test/riscv_cheriot_instructions_test.cc b/cheriot/test/riscv_cheriot_instructions_test.cc
index 2a10c91..bf66129 100644
--- a/cheriot/test/riscv_cheriot_instructions_test.cc
+++ b/cheriot/test/riscv_cheriot_instructions_test.cc
@@ -90,6 +90,7 @@
constexpr char kC2[] = "c12";
constexpr char kC3[] = "c13";
constexpr char kC4[] = "c14";
+constexpr char kC5[] = "c15";
// Register number definitions.
constexpr int kC1Num = 11;
constexpr int kPccNum = 0b1'00000;
@@ -117,6 +118,7 @@
{kC2, &c2_reg_},
{kC3, &c3_reg_},
{kC4, &c4_reg_},
+ {kC5, &c5_reg_},
{kCra, &cra_reg_}}) {
*cap_reg_ptr = state_->GetRegister<CheriotRegister>(reg_name).first;
}
@@ -219,6 +221,7 @@
CheriotRegister *c2_reg() { return c2_reg_; }
CheriotRegister *c3_reg() { return c3_reg_; }
CheriotRegister *c4_reg() { return c4_reg_; }
+ CheriotRegister *c5_reg() { return c5_reg_; }
CheriotRegister *cra_reg() { return cra_reg_; }
absl::BitGen &bitgen() { return bitgen_; }
bool trap_taken() { return trap_taken_; }
@@ -236,6 +239,7 @@
CheriotRegister *c2_reg_;
CheriotRegister *c3_reg_;
CheriotRegister *c4_reg_;
+ CheriotRegister *c5_reg_;
CheriotRegister *cra_reg_;
absl::BitGen bitgen_;
bool trap_taken_ = false;
@@ -935,8 +939,9 @@
EXPECT_TRUE(*c3_reg() == *state()->memory_root());
}
-// Load without global flag should clear global flag of loaded capability.
-TEST_F(RiscVCheriotInstructionsTest, CLcNoLoadGlobal) {
+// Load unsealed without global flag should clear global and load global flags
+// of loaded capability.
+TEST_F(RiscVCheriotInstructionsTest, CLcNoLoadGlobalUnsealed) {
c4_reg()->ResetMemoryRoot();
c4_reg()->ClearPermissions(PB::kPermitGlobal | PB::kPermitLoadGlobal);
SetUpForLoadCapabilityTest(kMemAddress + 0x200, state()->memory_root());
@@ -952,6 +957,29 @@
EXPECT_TRUE(*c3_reg() == *c4_reg());
}
+// Load sealed without global flag should clear global flag of loaded
+// capability.
+TEST_F(RiscVCheriotInstructionsTest, CLcNoLoadGlobalSealed) {
+ c4_reg()->ResetMemoryRoot();
+ c4_reg()->ClearPermissions(PB::kPermitGlobal);
+ c4_reg()->set_address(0xdeadbeef);
+ CHECK_OK(c4_reg()->Seal(*state()->sealing_root(), kDataSeal10));
+ c5_reg()->ResetMemoryRoot();
+ CHECK_OK(c5_reg()->Seal(*state()->sealing_root(), kDataSeal10));
+ SetUpForLoadCapabilityTest(kMemAddress + 0x200, c5_reg());
+ AppendCapabilityOperands(inst(), {kC1, kC2}, {});
+ AppendCapabilityOperands(inst()->child(), {}, {kC3});
+ c1_reg()->ResetMemoryRoot();
+ c1_reg()->ClearPermissions(PB::kPermitLoadGlobal);
+ c1_reg()->set_address(kMemAddress);
+ c2_reg()->set_address(0x200);
+ inst()->Execute(nullptr);
+ EXPECT_FALSE(trap_taken());
+ EXPECT_FALSE(*c3_reg() == *state()->memory_root());
+ EXPECT_TRUE(*c3_reg() == *c4_reg()) << "c3_reg(): " << c3_reg()->AsString()
+ << "\nc4_reg(): " << c4_reg()->AsString();
+}
+
// Load without mutable flag should clear mutable and store permissions of
// unsealed capability.
TEST_F(RiscVCheriotInstructionsTest, CLcNoLoadMutableUnsealed) {
@@ -997,13 +1025,9 @@
c2_reg()->set_address(0x200);
inst()->Execute(nullptr);
EXPECT_FALSE(trap_taken());
- // Result should be equal to memory root, but without the valid tag and some
- // permissions removed.
+ // Result should be equal to memory root, but without the valid tag.
c4_reg()->ResetMemoryRoot();
c4_reg()->set_address(0xdeadbeef);
- c4_reg()->ClearPermissions(
- PB::kPermitGlobal | PB::kPermitLoadGlobal | PB::kPermitLoadMutable |
- PB::kPermitStoreLocalCapability | PB::kPermitStore);
c4_reg()->Invalidate();
EXPECT_TRUE(*c3_reg() == *c4_reg()) << "c3_reg(): " << c3_reg()->AsString()
<< "\nc4_reg(): " << c4_reg()->AsString();