cheri-security / MorelloLinux / exampleCode / src / freestanding / selftest.c
selftest.c
Raw
/*
 * Copyright (c) 2023 Arm Limited. All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include "libc.h"
#include "morello.h"

static int test_strings(char *argv[], char *envp[]);
static int test_sprintf(char *argv[], char *envp[]);
static int test_morello(char *argv[], char *envp[]);
static int test_memcopy(char *argv[], char *envp[]);

int main(int argc, char *argv[], char *envp[])
{
    int r = 0;
    r += test_strings(argv, envp);
    r += test_sprintf(argv, envp);
    r += test_morello(argv, envp);
    r += test_memcopy(argv, envp);
    if (r) {
        return printf("%d test(s) failed\n", r);
    } else {
        return 0;
    }
}

#define TEST(code, test, fail) ({ \
    count++; \
    code; \
    if ((test)) { \
        printf("Test %s %2d passed: %s\n", name, count, #test); \
    } else { \
        r += 1; \
        printf("Test %s %2d FAILED: %s\n", name, count, #test); \
        fail; \
    } \
})

#define STEST(exp, fmt, ...) \
    TEST(int n = sprintf(str, fmt, __VA_ARGS__), \
    strcmp(str, exp) == 0 && n == (sizeof((exp)) - 1), \
    printf(" - output: `%s`\n", str))

static int test_strings(char *argv[], char *envp[])
{
    int r = 0;
    size_t count = 0;
    const char name[] = "strings";

    TEST({}, strcmp("abc", "ABC") > 0, {});
    TEST({}, strcmp("ABC", "abc") < 0, {});
    TEST({}, strcmp("123", "123") == 0, {});
    TEST(const char *str = "01234567",
        strcmp(str, cheri_bounds_set_exact(str, 4)) > 0, {});
    TEST(const char *str = "01234567",
        strcmp(cheri_bounds_set_exact(str, 5), str) < 0, {});
    TEST({}, strcmp(NULL, NULL) == 0, {});
    TEST(const char str[] = "",
        strlen(str) == 0, {});
    TEST(const char str[] = "01234",
        strlen(str) == 5, {});
    TEST(const char str[] = "0123456789",
        strlen(cheri_bounds_set_exact(str, 3)) == 3, {});
    TEST(const char str[] = "0123456789",
        strlen(cheri_tag_clear(str)) == 0, {});
    TEST(const char src[] = "0123456789"; char dst[64];
        strcpy(dst, src),
        strcmp(src, dst) == 0, {});
    TEST(const char src[] = "0123456789"; char dst[64];
        strcpy(dst, cheri_bounds_set_exact(src, 7)),
        strcmp("0123456", dst) == 0, {});
    TEST(const char src[] = "0123456789"; char dst[7];
        strcpy(dst, src),
        strcmp("0123456", dst) == 0, {});
    TEST(char buf[8]; memset(buf, 'u', sizeof(buf)),
        strcmp(buf, "uuuuuuuu") == 0, {});
    TEST(char buf[8]; memset(buf, 'u', sizeof(buf)+10),
        strcmp(buf, "uuuuuuuu") == 0, {});
    TEST(char buf[8]; memset(buf, 'g', 4); memset(buf + 4, 0, 4),
        strcmp(buf, "gggg") == 0, {});

    return r;
}

static int test_sprintf(char *argv[], char *envp[])
{
    int r = 0;
    size_t count = 0;
    const char name[] = "sprintf";
    char str[256];
    int m;

    STEST("hello", "hello", 0);
    STEST("hello", "%s", "hello");
    STEST("(null)", "%s", NULL);
    STEST("hello (null)", "%s %s", "hello", NULL);
    STEST("hello 42 morello", "%s %d %s", "hello", 42, "morello");
    STEST("CHERI % abc", "%c%c%c%c%c %% %s", 'C', 'H', 'E', 'R', 'I', "abc");
    STEST("13", "%i", 13);
    STEST("-987", "%d", -987);
    STEST("+987", "%+d", 987);
    STEST(" 987", "% d", 987);
    STEST("1024", "%u", 1024);
    STEST("c0ffee", "%x", 0xc0ffee);
    STEST("223338299392", "%li", 13l << 34);
    STEST("8681743812919296", "%ld", 987l << 43);
    STEST("140737488355328", "%lu", 1024ul << 37);
    STEST("303ffb800000000", "%lx", 0xc0ffeeul << 34);
    STEST("0xc0ffee", "%#x", 0xc0ffee);
    STEST("0001234", "%07x", 0x1234);
    STEST("fffffffd", "%x", -3);
    STEST("fffffffffffffffe", "%lx", -2l);
    STEST("4294967293", "%u", -3);
    STEST("18446744073709551614", "%lu", -2l);
    STEST("0", "%p", NULL);
    STEST("0:0000000000000000:0000000000000000", "%#p", NULL);
    TEST(void *cap = NULL; const char exp[] = "0000000000000000 0 [0000000000000000:0000000000000000) ------------------ none 0 of 0";
        m = sprintf(str, "%+#p", cap), strcmp(str, exp) == 0 && m == (sizeof(exp) - 1), {});
    STEST("     hello", "%10s", "hello");
    STEST("hello     ", "%-10s", "hello");
    STEST("hello", "%s%n", "hello", &m);
    TEST(int q = 0; sprintf(str, "%-10d%n", 1, &q), q == 10, {});

    STEST("mismatch 5 %s %c %u", "mismatch %d %s %c %u", 5);
    STEST("mismatch 5", "mismatch %d", 5, "str", false, 2.71f);
    STEST("unsupported 7 (invalid)", "unsupported %d %s", 7, (char *)42ul);
    STEST("unsupported 7 %a (invalid)", "unsupported %d %a %s", 7, 99, (char *)42ul);
    STEST("unsupported -1 %10.4f %-.5e (invalid)", "unsupported %d %10.4f %-.5e %s", -1, 1.2f, 3.14f, "string");

    return r;
}

#undef STEST

static int test_morello(char *argv[], char *envp[])
{
    int r = 0;
    size_t count = 0;
    const char name[] = "morello";

    TEST({}, cheri_is_local(argv) == false, {});
    TEST({}, cheri_is_deref(envp), {});
    TEST({}, cheri_in_bounds(argv), {});
    TEST({}, cheri_length_get_zero(NULL) == 0, {});
    TEST({}, cheri_get_tail(NULL) == 0, {});
    TEST({}, cheri_length_get_zero(argv) == cheri_get_tail(argv), {});
    TEST({}, cheri_length_get_zero(argv) == cheri_length_get(argv), {});
    TEST(char buf[64], strcmp(cap_perms_to_str(buf, argv), "GrRMwWL-----------") == 0, {});
    TEST(void *pcc = cheri_pcc_get(); char buf[64],
        strcmp(cap_perms_to_str(buf, pcc), "GrRM---xES--------") == 0, {});
    TEST(void *sentry = main; char buf[64], strcmp(cap_seal_to_str(buf, sentry), "rb") == 0, {});
    TEST(char buf[64], strcmp(cap_seal_to_str(buf, envp), "none") == 0, {});
    TEST({}, cheri_get_limit(NULL) == NULL, {});

    return r;
}

static int test_memcopy(char *argv[], char *envp[])
{
    int r = 0;
    size_t count = 0;
    const char name[] = "memcopy";

    char dst[64], src[64];
    strcpy(src, "this is morello test 0123456789 for mem copy function 01 234567");

    memset(dst, 0, sizeof(dst));
    TEST(memcpy(dst, src, sizeof(dst)), strcmp(dst, src) == 0 && strlen(dst) == strlen(src), {
        printf("output: `%s`\n", dst);
    });
    memset(dst, 0, sizeof(dst));
    TEST(memcpy(dst, src, 15), strcmp(dst, "this is morello") == 0 && strlen(dst) == 15, {
        printf("output: `%s`\n", dst);
    });
    memset(dst, 0, sizeof(dst));
    TEST(memcpy(dst + 5, src, 15), strcmp(dst + 5, "this is morello") == 0 && strlen(dst + 5) == 15, {
        printf("output: `%s`\n", dst);
    });
    memset(dst, 0, sizeof(dst));
    TEST(memcpy(dst + 5, src + 5, 10), strcmp(dst + 5, "is morello") == 0 && strlen(dst + 5) == 10, {
        printf("output: `%s`\n", dst);
    });

    double x = 3.14;
    typedef struct { int x; bool y; char *p1; double *p2; long z; } object_t;
    object_t s = { .x = 1, .y = true, .p1 = argv[0], .p2 = &x, .z = -1l };
    object_t d = { .x = 2, .y = false, .p1 = NULL, .p2 = NULL, .z = -1l };

    TEST(memcpy(&d, &s, sizeof(object_t)),
        d.x == 1 && d.y && d.p1 == s.p1 && *d.p2 == x && d.z == -1l, {});

    return r;
}


__attribute__((used))
void _start(int argc, char *argv[], char *envp[], auxv_t *auxv)
{
    init(auxv, false);
    exit(main(argc, argv, envp));
}