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.