| // Copyright CHERIoT Contributors. | |
| // SPDX-License-Identifier: MIT | |
| #define TEST_NAME "Test check_pointer" | |
| #include "tests.hh" | |
| int object; | |
| using namespace CHERI; | |
| /** | |
| * Test the `EnforceStrictPermissions` feature of `CHERI::check_pointer`. | |
| * | |
| * This test checks the following: | |
| * | |
| * 1. When passed `EnforceStrictPermissions = false` (default behavior), | |
| * `CHERI::check_pointer` does not modify the permissions and length of | |
| * passed capability with a Capability argument. | |
| * 2. When passed `EnforceStrictPermissions = false` (default behavior), | |
| * `CHERI::check_pointer` does not modify the permissions and length of | |
| * passed capability with a raw capability argument. | |
| * 3. When passed `EnforceStrictPermissions = true`, `CHERI::check_pointer` | |
| * changes permissions and length appropriately with a Capability argument. | |
| * 4. When passed `EnforceStrictPermissions = true`, `CHERI::check_pointer` | |
| * changes permissions and length appropriately with a raw capability | |
| * argument. | |
| * 5. For both 3. and 4., it also checks that `EnforceStrictPermissions = true` | |
| * does not invalidate the capability. | |
| */ | |
| void check_pointer_strict_mode(int *ptr) | |
| { | |
| bool isValid; | |
| Capability cap{ptr}; | |
| debug_log("Test default check_pointer (EnforceStrictPermissions = false)."); | |
| debug_log("Test with Capability argument."); | |
| PermissionSet permissionsAtCreation = cap.permissions(); | |
| auto boundsAtCreation = cap.bounds(); | |
| // Check that the capability has > load permissions at creation. | |
| TEST(permissionsAtCreation.contains(Permission::Load, Permission::Store), | |
| "Permissions of new capability do not include load/store."); | |
| // Check that the capability has length > 1 at creation. | |
| TEST(boundsAtCreation > 1, | |
| "Permissions of new capability do not include load/store."); | |
| // We will detect if `check_pointer` changes the permissions because we | |
| // are calling load only and bounds = 1 here. | |
| TEST(check_pointer<PermissionSet{Permission::Load}>(cap, 1), | |
| "Cannot check valid capability (calling with Capability object)."); | |
| TEST(cap.permissions() == permissionsAtCreation, | |
| "check_pointer reduced the permissions of passed Capability " | |
| "object although EnforceStrictPermissions = false."); | |
| TEST(cap.bounds() == boundsAtCreation, | |
| "check_pointer reduced the bounds of passed Capability " | |
| "object although EnforceStrictPermissions = false."); | |
| debug_log("Test with raw capability argument."); | |
| TEST(check_pointer<PermissionSet{Permission::Load}>(ptr, 1), | |
| "Cannot check valid capability (calling with raw capability)."); | |
| Capability capShouldBeTheSame{ptr}; | |
| TEST(capShouldBeTheSame.permissions() == permissionsAtCreation, | |
| "check_pointer reduced the permissions of passed raw " | |
| "capability although EnforceStrictPermissions = false."); | |
| TEST(capShouldBeTheSame.bounds() == boundsAtCreation, | |
| "check_pointer reduced the bounds of passed Capability " | |
| "capability although EnforceStrictPermissions = false."); | |
| debug_log("Test check_pointer with EnforceStrictPermissions = true."); | |
| debug_log("Test with Capability argument."); | |
| // We can re-use `cap` here because it was not changed. | |
| isValid = | |
| check_pointer<PermissionSet{Permission::Load}, true, true>(cap, 1); | |
| TEST(isValid, | |
| "Cannot check valid capability when EnforceStrictPermissions = true " | |
| "(calling with Capability object)."); | |
| // The capability should now be load-only. | |
| TEST(cap.permissions() == PermissionSet{Permission::Load}, | |
| "check_pointer did not reduce the permissions of passed Capability " | |
| "object although EnforceStrictPermissions = true."); | |
| // The bounds should now be 1. | |
| TEST(cap.bounds() == 1, | |
| "check_pointer did not reduce the bounds of passed Capability " | |
| "object although EnforceStrictPermissions = true."); | |
| TEST(check_pointer<PermissionSet{Permission::Load}>(cap, 1), | |
| "EnforceStrictPermissions = true breaks the capability when called " | |
| "with a Capability object."); | |
| debug_log("Test with raw capability argument."); | |
| // We cannot re-use `cap` because it was changed. | |
| Capability cap2{ptr}; | |
| TEST( | |
| cap2.permissions() == permissionsAtCreation, | |
| "Permissions of freshly created capability do not include load/store."); | |
| isValid = | |
| check_pointer<PermissionSet{Permission::Load}, true, true>(ptr, 1); | |
| TEST(isValid, | |
| "Cannot check valid capability when EnforceStrictPermissions = true " | |
| "(calling with raw capability)."); | |
| // We need to create a new Capability object with the updated pointer | |
| // to check its permissions. | |
| Capability cap3{ptr}; | |
| TEST(cap3.permissions() == PermissionSet{Permission::Load}, | |
| "check_pointer did not reduce the permissions of passed raw " | |
| "capability although EnforceStrictPermissions = true."); | |
| TEST(cap3.bounds() == 1, | |
| "check_pointer did not reduce the bounds of passed raw " | |
| "capability although EnforceStrictPermissions = true."); | |
| TEST(check_pointer<PermissionSet{Permission::Load}>(ptr, 1), | |
| "EnforceStrictPermissions = true breaks the capability when called " | |
| "with a raw capability."); | |
| } | |
| void test_check_pointer() | |
| { | |
| check_pointer_strict_mode(&object); | |
| } |