Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
251 changes: 251 additions & 0 deletions host/xtest/regression_1000.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
#include <openssl/rsa.h>
#include <openssl/sha.h>
#endif
#define RISCV_FP_CTX_IMPLEMENTATION
#include <riscv_fp_ctx.h>

#include <pta_attestation.h>
#include <pta_invoke_tests.h>
#include <pta_secstor_ta_mgmt.h>
Expand Down Expand Up @@ -3475,3 +3478,251 @@ static void xtest_tee_test_1042(ADBG_Case_t *c)
}
ADBG_CASE_DEFINE(regression, 1042, xtest_tee_test_1042,
"Test ASAN (Memory address sanitizer)");

/*
* RISC-V floating-point context switching.
*
* A TA runs with the floating-point unit disabled and is handed a context
* on the first floating-point instruction it executes, while the normal
* world context is switched eagerly whenever a thread crosses into the TEE
* and back. The sub-tests below check both halves of that: the TA side by
* asking the TA to verify its own registers across various excursions, and
* the normal world side by holding a pattern in this process' registers
* across a call into the TEE.
*/

static TEEC_Result fp_invoke(TEEC_Session *session, uint32_t subtest,
uint32_t seed, uint32_t *bad_field,
uint32_t *ret_orig)
{
TEEC_Operation op = { };
TEEC_Result res = TEEC_ERROR_GENERIC;

op.paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_INPUT, TEEC_VALUE_OUTPUT,
TEEC_NONE, TEEC_NONE);
op.params[0].value.a = subtest;
op.params[0].value.b = seed;

res = TEEC_InvokeCommand(session, TA_OS_TEST_CMD_RISCV_FP_CONTEXT,
&op, ret_orig);
if (bad_field)
*bad_field = op.params[1].value.a;

return res;
}

static void fp_log_bad_field(uint32_t field)
{
if (field == 12)
Do_ADBG_Log(" fcsr was not preserved");
else
Do_ADBG_Log(" fs%u was the first register not preserved",
field);
}

#ifdef RISCV_FP_CTX_SUPPORTED
struct fp_ree_arg {
TEEC_Session *session;
uint32_t subtest;
uint32_t seed;
uint32_t bad_field;
uint32_t ret_orig;
};

static unsigned long fp_ree_invoke(void *a)
{
struct fp_ree_arg *arg = a;

return fp_invoke(arg->session, arg->subtest, arg->seed,
&arg->bad_field, &arg->ret_orig);
}

/*
* Holds a pattern in this process' fs0..fs11 and fcsr across an invoke and
* checks that the TEE gave them back. This is the normal world half of the
* domain switch: whatever the TEE does with the floating-point registers
* must not be visible here.
*/
static void fp_check_ree_preserved(ADBG_Case_t *c, TEEC_Session *session,
uint32_t subtest, uint32_t seed)
{
struct riscv_fp_ctx expect = { };
struct riscv_fp_ctx got = { };
struct fp_ree_arg arg = { };
TEEC_Result res = TEEC_ERROR_GENERIC;
int diff = 0;

arg.session = session;
arg.subtest = subtest;
arg.seed = seed;

riscv_fp_ctx_pattern(&expect, seed);

res = (TEEC_Result)riscv_fp_ctx_roundtrip(&expect, &got,
fp_ree_invoke, &arg);
if (!ADBG_EXPECT_TEEC_SUCCESS(c, res)) {
if (arg.bad_field)
fp_log_bad_field(arg.bad_field);
return;
}

diff = riscv_fp_ctx_diff(&expect, &got);
if (!ADBG_EXPECT_COMPARE_SIGNED(c, diff, ==, -1))
fp_log_bad_field((uint32_t)diff);
}
#endif /*RISCV_FP_CTX_SUPPORTED*/

static void fp_check_ta_subtest(ADBG_Case_t *c, TEEC_Session *session,
uint32_t subtest, uint32_t seed)
{
uint32_t ret_orig = 0;
uint32_t bad_field = 0;
TEEC_Result res = TEEC_ERROR_GENERIC;

res = fp_invoke(session, subtest, seed, &bad_field, &ret_orig);
if (!ADBG_EXPECT_TEEC_SUCCESS(c, res))
fp_log_bad_field(bad_field);
}

struct test_1045_thread_arg {
pthread_t thr;
uint32_t seed;
TEEC_Result res;
uint32_t bad_field;
};

static void *test_1045_thread(void *a)
{
struct test_1045_thread_arg *arg = a;
TEEC_Session session = { };
uint32_t ret_orig = 0;
size_t n = 0;

arg->res = xtest_teec_open_session(&session, &os_test_ta_uuid, NULL,
&ret_orig);
if (arg->res != TEEC_SUCCESS)
return NULL;

for (n = 0; n < 8; n++) {
arg->res = fp_invoke(&session, TA_RISCV_FP_SUBTEST_RPC,
arg->seed, &arg->bad_field, &ret_orig);
if (arg->res != TEEC_SUCCESS)
break;
}

TEEC_CloseSession(&session);

return NULL;
}

static void xtest_tee_test_1045(ADBG_Case_t *c)
{
struct test_1045_thread_arg arg[NUM_THREADS] = { };
TEEC_Session session = { };
TEEC_Session tainted = { };
uint32_t ret_orig = 0;
uint32_t bad_field = 0;
TEEC_Result res = TEEC_ERROR_GENERIC;
size_t nt = NUM_THREADS;
size_t n = 0;

if (!ADBG_EXPECT_TEEC_SUCCESS(c,
xtest_teec_open_session(&session, &os_test_ta_uuid,
NULL, &ret_orig)))
return;

/*
* Probe first: a TA built for an architecture without this test, or
* an OP-TEE built without floating-point context switching, has
* nothing to say here.
*/
res = fp_invoke(&session, TA_RISCV_FP_SUBTEST_SYSCALL, 0x11,
&bad_field, &ret_orig);
if (res == TEEC_ERROR_NOT_SUPPORTED) {
Do_ADBG_Log("TA has no RISC-V FP context test - skip tests");
goto out;
}

Do_ADBG_BeginSubCase(c, "TA context across a syscall");
if (!ADBG_EXPECT_TEEC_SUCCESS(c, res))
fp_log_bad_field(bad_field);
Do_ADBG_EndSubCase(c, "TA context across a syscall");

Do_ADBG_BeginSubCase(c, "TA context across an RPC");
fp_check_ta_subtest(c, &session, TA_RISCV_FP_SUBTEST_RPC, 0x22);
Do_ADBG_EndSubCase(c, "TA context across an RPC");

Do_ADBG_BeginSubCase(c, "TA context across a crypto operation");
fp_check_ta_subtest(c, &session, TA_RISCV_FP_SUBTEST_CRYPTO, 0x33);
Do_ADBG_EndSubCase(c, "TA context across a crypto operation");

/*
* A TA that has just been loaded must not find another TA's values
* in the registers. os_test is multi instance, so closing the
* session below unloads the instance that left the pattern behind
* and the next session gets a fresh floating-point context.
*/
Do_ADBG_BeginSubCase(c, "TA context is not carried between instances");
res = xtest_teec_open_session(&tainted, &os_test_ta_uuid, NULL,
&ret_orig);
if (ADBG_EXPECT_TEEC_SUCCESS(c, res)) {
ADBG_EXPECT_TEEC_SUCCESS(c,
fp_invoke(&tainted, TA_RISCV_FP_SUBTEST_TAINT, 0x44,
NULL, &ret_orig));
TEEC_CloseSession(&tainted);

memset(&tainted, 0, sizeof(tainted));
res = xtest_teec_open_session(&tainted, &os_test_ta_uuid, NULL,
&ret_orig);
if (ADBG_EXPECT_TEEC_SUCCESS(c, res)) {
fp_check_ta_subtest(c, &tainted,
TA_RISCV_FP_SUBTEST_CHECK_TAINT,
0x44);
TEEC_CloseSession(&tainted);
}
}
Do_ADBG_EndSubCase(c, "TA context is not carried between instances");

#ifdef RISCV_FP_CTX_SUPPORTED
/*
* The TEE saves the normal world context on the way in. On the way
* out it puts it back, unless nothing in the TEE ever enabled the
* floating-point unit, in which case the registers were never
* disturbed and the restore is skipped. Both paths have to leave
* this process' registers exactly as it left them, so cover them
* with a TA command that uses floating point and one that does not.
*/
Do_ADBG_BeginSubCase(c, "REE context across an invoke using FP");
fp_check_ree_preserved(c, &session, TA_RISCV_FP_SUBTEST_RPC, 0x55);
Do_ADBG_EndSubCase(c, "REE context across an invoke using FP");

Do_ADBG_BeginSubCase(c, "REE context across an invoke not using FP");
fp_check_ree_preserved(c, &session, TA_RISCV_FP_SUBTEST_NO_FP, 0x66);
Do_ADBG_EndSubCase(c, "REE context across an invoke not using FP");
#endif /*RISCV_FP_CTX_SUPPORTED*/

/*
* Each OP-TEE thread carries its own floating-point bookkeeping, so
* run the RPC case from several threads at once to check that one
* thread's context does not end up in another.
*/
Do_ADBG_BeginSubCase(c, "Concurrent TA contexts");
for (n = 0; n < nt; n++) {
arg[n].seed = 0x80 + n;
if (!ADBG_EXPECT(c, 0, pthread_create(&arg[n].thr, NULL,
test_1045_thread,
arg + n)))
nt = n; /* break loop and start cleanup */
}
for (n = 0; n < nt; n++) {
ADBG_EXPECT(c, 0, pthread_join(arg[n].thr, NULL));
if (!ADBG_EXPECT_TEEC_SUCCESS(c, arg[n].res))
fp_log_bad_field(arg[n].bad_field);
}
Do_ADBG_EndSubCase(c, "Concurrent TA contexts");

out:
TEEC_CloseSession(&session);
}
ADBG_CASE_DEFINE(regression, 1045, xtest_tee_test_1045,
"Test RISC-V floating-point context switching");
2 changes: 2 additions & 0 deletions ta/os_test/include/os_test.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,5 +51,7 @@ TEE_Result ta_entry_asan_global(void);
TEE_Result ta_entry_asan_malloc(void);
TEE_Result ta_entry_asan_memfunc(void);
TEE_Result ta_entry_asan_uaf(void);
TEE_Result ta_entry_riscv_fp_context(uint32_t param_types,
TEE_Param params[4]);

#endif /*OS_TEST_H */
Loading