Fix for stores of backward sentries.
PiperOrigin-RevId: 666512365
Change-Id: I49ecd63e7200f9c5011f1f25e8f288ceffbeb243
diff --git a/cheriot/riscv_cheriot_instructions.cc b/cheriot/riscv_cheriot_instructions.cc
index a1358c7..cdd30ee 100644
--- a/cheriot/riscv_cheriot_instructions.cc
+++ b/cheriot/riscv_cheriot_instructions.cc
@@ -457,7 +457,7 @@
return;
}
if (!cs1->HasPermission(CapReg::kPermitStoreLocalCapability) && cs2->tag() &&
- !cs2->HasPermission(CapReg::kPermitGlobal)) {
+ (!cs2->HasPermission(CapReg::kPermitGlobal) || cs2->IsBackwardSentry())) {
tag = 0;
}
if (!cs1->IsInBounds(address, CapReg::kCapabilitySizeInBytes)) {
diff --git a/cheriot/test/riscv_cheriot_instructions_test.cc b/cheriot/test/riscv_cheriot_instructions_test.cc
index c0bc046..23238fa 100644
--- a/cheriot/test/riscv_cheriot_instructions_test.cc
+++ b/cheriot/test/riscv_cheriot_instructions_test.cc
@@ -1106,10 +1106,13 @@
inst()->Execute(nullptr);
EXPECT_FALSE(trap_taken());
auto *db = state()->db_factory()->Allocate<uint32_t>(2);
- memory()->Load(kMemAddress + 0x200, db, nullptr, nullptr);
+ auto *tag_db = state()->db_factory()->Allocate<uint8_t>(1);
+ memory()->Load(kMemAddress + 0x200, db, tag_db, nullptr, nullptr);
+ EXPECT_EQ(tag_db->Get<uint8_t>(0), 1);
EXPECT_EQ(db->Get<uint32_t>(0), c3_reg()->address());
EXPECT_EQ(db->Get<uint32_t>(1), c3_reg()->Compress());
db->DecRef();
+ tag_db->DecRef();
}
// Check store capability with invalid capability.
@@ -1202,13 +1205,53 @@
inst()->Execute(nullptr);
EXPECT_FALSE(trap_taken());
auto *db = state()->db_factory()->Allocate<uint32_t>(2);
- memory()->Load(kMemAddress + 0x200, db, nullptr, nullptr);
- // Invalidate c3 - the stored tag should be the same as c3, but with the tag
- // cleared.
- c3_reg()->Invalidate();
+ auto *tag_db = state()->db_factory()->Allocate<uint8_t>(1);
+ memory()->Load(kMemAddress + 0x200, db, tag_db, nullptr, nullptr);
+ // Expect the tag to be cleared.
+ EXPECT_EQ(tag_db->Get<uint8_t>(0), 0);
EXPECT_EQ(db->Get<uint32_t>(0), c3_reg()->address());
EXPECT_EQ(db->Get<uint32_t>(1), c3_reg()->Compress());
db->DecRef();
+ tag_db->DecRef();
+}
+
+// Check for proper handling of backward sentry with no local cap permission.
+TEST_F(RiscVCheriotInstructionsTest, CScStoreLocalCapViolationBackwardSentry) {
+ inst()->set_semantic_function(&CheriotCSc);
+ AppendCapabilityOperands(inst(), {kC1, kC2, kC3}, {});
+ c1_reg()->ResetMemoryRoot();
+ c1_reg()->ClearPermissions(PB::kPermitStoreLocalCapability);
+ c1_reg()->set_address(kMemAddress);
+ c2_reg()->set_address(0x200);
+ c3_reg()->ResetExecuteRoot();
+ c3_reg()->set_object_type(CheriotRegister::kInterruptDisablingReturnSentry);
+ c3_reg()->ClearPermissions(PB::kPermitGlobal);
+ EXPECT_TRUE(c3_reg()->IsBackwardSentry());
+ inst()->Execute(nullptr);
+ EXPECT_FALSE(trap_taken());
+ auto *db = state()->db_factory()->Allocate<uint32_t>(2);
+ auto *tag_db = state()->db_factory()->Allocate<uint8_t>(1);
+ memory()->Load(kMemAddress + 0x200, db, tag_db, nullptr, nullptr);
+ // Expect the tag to be cleared.
+ EXPECT_EQ(tag_db->Get<uint8_t>(0), 0);
+ EXPECT_EQ(db->Get<uint32_t>(0), c3_reg()->address());
+ EXPECT_EQ(db->Get<uint32_t>(1), c3_reg()->Compress());
+
+ // Now with global flag on the stored capability.
+ c3_reg()->ResetExecuteRoot();
+ c3_reg()->set_object_type(CheriotRegister::kInterruptDisablingReturnSentry);
+ EXPECT_TRUE(c3_reg()->IsBackwardSentry());
+ inst()->Execute(nullptr);
+ EXPECT_FALSE(trap_taken());
+ memory()->Load(kMemAddress + 0x200, db, tag_db, nullptr, nullptr);
+ // Expect the tag to be cleared.
+ EXPECT_EQ(tag_db->Get<uint8_t>(0), 0);
+ EXPECT_EQ(db->Get<uint32_t>(0), c3_reg()->address());
+ EXPECT_EQ(db->Get<uint32_t>(1), c3_reg()->Compress());
+
+ // Clean up.
+ db->DecRef();
+ tag_db->DecRef();
}
// Check store capability with bounds violation.