blob: dc09d775c09db377beed9a1b149d1cc33aa8c6fd [file] [log] [blame]
// Copyright CHERIoT Contributors.
// SPDX-License-Identifier: MIT
#define TEST_NAME "Test misc APIs"
#include "tests.hh"
#include <compartment-macros.h>
#include <ds/pointer.h>
#include <string.h>
#include <timeout.h>
using namespace CHERI;
/**
* Test timeouts.
*
* This test checks the following:
*
* - A timeout of zero would not block.
* - `elapse` saturates values, i.e., a `remaining` value of zero will still be
* zero after a call to `elapse`, and an `elapsed` value of `UINT32_MAX`
* would still be `UINT32_MAX` after a call to `elapse`.
* - An unlimited timeout is really unlimited, i.e., a call to `elapse` does
* not modify its `remaining` value, which blocks.
*/
void check_timeouts()
{
debug_log("Test timeouts.");
// Create a zero timeout.
Timeout t{0};
// Ensure that a zero timeout does not block.
TEST(!t.may_block(), "A zero timeout should not block.");
// Create a zero timer with maximum elapsed time.
t = Timeout{UINT32_MAX /* elapsed */, 0 /* remaining */};
// Ensure that a call to `elapse` saturates both `elapsed` and
// `remaining`.
t.elapse(42);
TEST(t.remaining == 0,
"`elapse` does not saturate the `remaining` value of a zero timer.");
TEST(t.elapsed == UINT32_MAX,
"`elapse` does not saturate the `elapsed` value of a zero timer.");
// Create an unlimited timeout.
t = Timeout{UnlimitedTimeout /* remaining */};
// Ensure that a call to `elapse` does not modify the `remaining` value
// of the unlimited timeout.
t.elapse(42);
TEST(t.remaining == UnlimitedTimeout,
"`elapse` alters the remaining value of an unlimited timeout.");
// Ensure that an unlimited timeout blocks.
TEST(t.may_block(), "An unlimited timeout should block.");
}
/**
* Test memchr.
*
* This test checks the following:
*
* - memchr finds the first occurrence of the character when it is present
* (test for different values, particularly the first and the last one).
* - memchr returns NULL when the string does not contain the character (test
* for non-NULL terminated string).
* - memchr does not stop at \0 characters.
* - memchr returns NULL for 0-size pointers.
*/
void check_memchr()
{
debug_log("Test memchr.");
char string[] = {'C', 'H', 'E', 'R', 'R', 'I', 'E', 'S'};
TEST(memchr(string, 'C', sizeof(string)) == &string[0],
"memchr must return the first occurence of the character.");
TEST(memchr(string, 'R', sizeof(string)) == &string[3],
"memchr must return the first occurence of the character.");
TEST(memchr(string, 'S', sizeof(string)) == &string[7],
"memchr must return the first occurence of the character.");
TEST(memchr(string, 'X', sizeof(string)) == NULL,
"memchr must return NULL when a character is not present.");
char stringWithNull[] = {'Y', 'E', 'S', '\0', 'N', 'O', '\0'};
TEST(memchr(stringWithNull, 'N', sizeof(stringWithNull)) ==
&stringWithNull[4],
"memchr must not stop at NULL characters.");
TEST(memchr(stringWithNull, 'N', 0) == NULL,
"memchr must return NULL for zero-size pointers.");
}
/**
* Test memrchr.
*
* This test checks the following:
*
* - memrchr finds the first occurrence of the character when it is present
* (test for different values, particularly the first and the last one).
* - memrchr returns NULL when the string does not contain the character (test
* for non-NULL terminated string).
* - memrchr does not stop at \0 characters.
* - memrchr returns NULL for 0-size pointers.
*/
void check_memrchr()
{
debug_log("Test memrchr.");
char string[] = {'C', 'H', 'E', 'R', 'R', 'I', 'O', 'T'};
TEST(memchr(string, 'C', sizeof(string)) == &string[0],
"memrchr must return the first occurence of the character.");
TEST(memrchr(string, 'R', sizeof(string)) == &string[4],
"memrchr must return the first occurence of the character.");
TEST(memrchr(string, 'T', sizeof(string)) == &string[7],
"memrchr must return the first occurence of the character.");
TEST(memrchr(string, 'X', sizeof(string)) == NULL,
"memrchr must return NULL when a character is not present.");
char stringWithNull[] = {'F', 'U', '\0', 'B', 'A', 'R', '\0'};
TEST(memrchr(stringWithNull, 'F', sizeof(stringWithNull)) ==
&stringWithNull[0],
"memrchr must not stop at NULL characters.");
TEST(memrchr(stringWithNull, 'Y', 0) == NULL,
"memrchr must return NULL for zero-size pointers.");
}
/**
* Test pointer utilities.
*
* Not comprehensive, would benefit from being expanded at some point.
*/
void check_pointer_utilities()
{
debug_log("Test pointer utilities.");
int integer = 42;
int *integerPointer = &integer;
ds::pointer::proxy::Pointer<int> pointer{integerPointer};
TEST((pointer == integerPointer) && (*pointer == 42),
"The pointer proxy does not return the value of its proxy.");
int anotherInteger = -100;
int *anotherIntegerPointer = &anotherInteger;
ds::pointer::proxy::Pointer<int> anotherPointer{anotherIntegerPointer};
pointer = anotherPointer;
TEST((pointer == anotherIntegerPointer) && (*pointer == -100),
"The pointer proxy `=` operator does not correctly set the pointer.");
}
void check_shared_object(const char *name,
Capability<void> object,
size_t size,
PermissionSet permissions)
{
debug_log("Checking shared object {}.", object);
TEST(object.length() == size,
"Object {} is {} bytes, expected {}",
name,
object.length(),
size);
TEST(object.permissions() == permissions,
"Object {} has permissions {}, expected {}",
name,
PermissionSet{object.permissions()},
permissions);
}
// This test is somewhat intimately familiar with parameters of CHERIoT's
// capability encoding and so might need revision if that changes.
void check_capability_set_inexact_at_most()
{
void *p = malloc(3128);
debug_log("Test Capability::BoundsProxy::set_inexact_at_most with {}", p);
// Too many bits for mantissa, regardless of base alignment
{
Capability<void> q = {p};
size_t reqlen = 2047;
q.bounds().set_inexact_at_most(reqlen);
debug_log("Requesting 2047 gives {}: {}", q.length(), q);
TEST(q.is_valid(), "set_inexact_at_most untagged");
TEST(q.length() < 2047, "set_inexact_at_most failed to truncate");
TEST(q.base() == q.address(), "set_inexact_at_most nonzero offset");
}
// Fits in mantissa, but not reachable from misaligned base
{
Capability<void> q = {p};
q.address() += 2;
size_t reqlen = 1024;
q.bounds().set_inexact_at_most(reqlen);
debug_log("Requesting 1024 at align 2 gives {}: {}", q.length(), q);
TEST(q.is_valid(), "set_inexact_at_most untagged");
TEST(q.length() < 1024, "set_inexact_at_most failed to truncate");
TEST(q.base() == q.address(), "set_inexact_at_most nonzero offset");
}
// Fits in mantissa and reachable from misaligned base
{
Capability<void> q = {p};
q.address() += 1;
size_t reqlen = 511;
q.bounds().set_inexact_at_most(reqlen);
debug_log("Requesting 511 at align 1 gives {}: {}", q.length(), q);
TEST(q.is_valid(), "set_inexact_at_most untagged");
TEST(q.length() == 511, "set_inexact_at_most truncated unnecessarily");
TEST(q.base() == q.address(), "set_inexact_at_most nonzero offset");
}
free(p);
}
int test_misc()
{
check_timeouts();
check_memchr();
check_memrchr();
check_pointer_utilities();
check_capability_set_inexact_at_most();
debug_log("Testing shared objects.");
check_shared_object("exampleK",
SHARED_OBJECT(void, exampleK),
1024,
{Permission::Global,
Permission::Load,
Permission::Store,
Permission::LoadStoreCapability,
Permission::LoadMutable});
check_shared_object(
"exampleK",
SHARED_OBJECT_WITH_PERMISSIONS(void, exampleK, true, true, false, false),
1024,
{Permission::Global, Permission::Load, Permission::Store});
check_shared_object(
"test_word",
SHARED_OBJECT_WITH_PERMISSIONS(void, test_word, true, false, true, false),
4,
{Permission::Global, Permission::Load, Permission::LoadStoreCapability});
check_shared_object("test_word",
SHARED_OBJECT_WITH_PERMISSIONS(
void, test_word, true, false, false, false),
4,
{Permission::Global, Permission::Load});
return 0;
}