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();