Skip to content

Commit f05cfb7

Browse files
bmolodanclaude
andcommitted
tee: validate TA-supplied pointers in secure-kernel syscalls
The copy helpers tee_svc_copy_to_user/tee_svc_copy_from_user and the live crypto syscalls (utee_hash_update/final, utee_cipher_init, cipher update, copy_in_attrs) dereferenced or memcpy'd TA-supplied pointer ranges with their tee_mmu_check_access_rights() guard commented out. A malicious or buggy TA could pass a NULL or wrapping range and drive an out-of-bounds copy in the secure world. This port has no per-TA MMU/MPU context or region table, and every call site requests TEE_MEMORY_ACCESS_ANY_OWNER, so ownership cannot (and is not meant to) be enforced. Implement tee_mmu_check_access_rights() as the accessibility check that is meaningful here - reject NULL+len and address-space-wrapping ranges, mirroring cmse_check_address_range()'s end-of-range test on the non-secure boundary - and restore the guard at every live call site, including the copy_in_attrs path that stores unvalidated attribute buffers later memcpy'd by op_attr_secret_value_from_user. Add a temporary CONFIG_APPS_ACCESS_RIGHTS_TEST negative test (mps2 AN505) that drives the guard with malformed ranges and asserts TEE_ERROR_ACCESS_DENIED. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 27e314c commit f05cfb7

7 files changed

Lines changed: 216 additions & 79 deletions

File tree

apps/Kconfig

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,16 @@ config APPS_TEST
3434
---help---
3535
"GP TEE Client API and TEE Internal API test suite."
3636

37+
config APPS_ACCESS_RIGHTS_TEST
38+
bool "TA->secure-kernel pointer-access guard test (temporary)"
39+
default n
40+
---help---
41+
"Temporary negative test that drives tee_mmu_check_access_rights()
42+
with malformed TA-supplied pointer ranges (NULL+len, wrapping range)
43+
and checks that they are rejected with TEE_ERROR_ACCESS_DENIED while a
44+
valid buffer is accepted. Remove once the access-rights fix is
45+
verified."
46+
3747
comment "H/W Security exception:"
3848

3949
config APPS_HW_SECURITY_EXCEPTION_EXAMPLE
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
* Temporary negative test for the TA->secure-kernel pointer-access guard.
3+
*
4+
* tee_svc_copy_to_user/copy_from_user and the crypto syscalls
5+
* (utee_hash_update/final, utee_cipher_init, cipher update, copy_in_attrs)
6+
* validate TA-supplied pointer ranges through tee_mmu_check_access_rights()
7+
* before dereferencing them. This test drives that guard directly with
8+
* malformed ranges and asserts they are rejected with TEE_ERROR_ACCESS_DENIED,
9+
* and that a normal in-range buffer is still accepted.
10+
*
11+
* The guard is exercised directly rather than through a syscall such as
12+
* utee_hash_update(). At boot there is no active TA session, and in this port
13+
* tee_ta_get_current_session() does not fail in that case - it returns
14+
* TEE_SUCCESS with a NULL current_session - so a session-dependent syscall
15+
* would fault on the NULL session rather than reach the range check. Calling
16+
* the guard directly tests exactly the logic under test with no such
17+
* dependency.
18+
*
19+
* This app is not part of the product; remove it once the fix is verified.
20+
* Gated behind CONFIG_APPS_ACCESS_RIGHTS_TEST.
21+
*/
22+
23+
#include <stdint.h>
24+
#include <tee_api_types.h>
25+
#include <tee/tee_svc.h>
26+
#include "printf.h"
27+
28+
static int check(const char *name, TEE_Result got, TEE_Result expected)
29+
{
30+
if (got == expected) {
31+
printf(" [PASS] %s -> 0x%08x\n", name, (unsigned int)got);
32+
return 0;
33+
}
34+
printf(" [FAIL] %s -> 0x%08x (expected 0x%08x)\n", name,
35+
(unsigned int)got, (unsigned int)expected);
36+
return 1;
37+
}
38+
39+
void tee_access_rights_test(void)
40+
{
41+
int fails = 0;
42+
uint32_t rd = TEE_MEMORY_ACCESS_READ | TEE_MEMORY_ACCESS_ANY_OWNER;
43+
uint32_t wr = TEE_MEMORY_ACCESS_WRITE | TEE_MEMORY_ACCESS_ANY_OWNER;
44+
volatile uint32_t valid = 0;
45+
46+
printf("== access-rights guard negative test ==\n");
47+
48+
/* NULL buffer with non-zero length must be rejected. */
49+
fails += check("null+len (read)",
50+
tee_mmu_check_access_rights(NULL, rd, 0, 8),
51+
TEE_ERROR_ACCESS_DENIED);
52+
53+
/* A range that wraps the address space must be rejected. */
54+
fails += check("wrapping range (write)",
55+
tee_mmu_check_access_rights(NULL, wr, (uaddr_t)~0UL, 2),
56+
TEE_ERROR_ACCESS_DENIED);
57+
58+
/* Zero-length range is trivially accepted. */
59+
fails += check("zero length",
60+
tee_mmu_check_access_rights(NULL, rd, 0, 0),
61+
TEE_SUCCESS);
62+
63+
/* A normal in-range buffer is still accepted. */
64+
fails += check("valid buffer",
65+
tee_mmu_check_access_rights(NULL, wr, (uaddr_t)&valid,
66+
sizeof(valid)),
67+
TEE_SUCCESS);
68+
69+
if (fails == 0)
70+
printf("== RESULT: all cases handled correctly ==\n");
71+
else
72+
printf("== RESULT: %d case(s) FAILED ==\n", fails);
73+
}

arch/cortex-m33/mps2/src/mps2_an505_qemu/secure/Make.defs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,7 @@ endif
6868
ifeq ($(CONFIG_APPS_TEST),y)
6969
CHIP_CSRCS_S += ../../../../../../apps/test/ta/test_ta.c
7070
endif
71+
72+
ifeq ($(CONFIG_APPS_ACCESS_RIGHTS_TEST),y)
73+
CHIP_CSRCS_S += ../../../../../../apps/access_rights_test/access_rights_test.c
74+
endif

arch/cortex-m33/mps2/src/mps2_an505_qemu/secure/main.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,10 @@ void menu_security_exception_example(void);
8181

8282
extern TEE_Result tee_cryp_init(void);
8383

84+
#ifdef CONFIG_APPS_ACCESS_RIGHTS_TEST
85+
extern void tee_access_rights_test(void);
86+
#endif
87+
8488
///* Private Functions. */
8589
//
8690
///**
@@ -685,6 +689,10 @@ int main(void)
685689
#endif
686690

687691
tee_cryp_init();
692+
693+
#ifdef CONFIG_APPS_ACCESS_RIGHTS_TEST
694+
tee_access_rights_test();
695+
#endif
688696
//
689697
// Boot_Init(CONFIG_START_ADDRESS_BL33);
690698

tee/include/tee/tee_svc.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,11 @@ TEE_Result syscall_invoke_ta_command(unsigned long sess,
8888
TEE_Result syscall_check_access_rights(unsigned long flags, const void *buf,
8989
size_t len);
9090

91+
struct user_ta_ctx;
92+
93+
TEE_Result tee_mmu_check_access_rights(struct user_ta_ctx *utc, uint32_t flags,
94+
uaddr_t uaddr, size_t len);
95+
9196
TEE_Result tee_svc_copy_from_user(void *kaddr, const void *uaddr, size_t len);
9297
TEE_Result tee_svc_copy_to_user(void *uaddr, const void *kaddr, size_t len);
9398

tee/tee/tee_svc.c

Lines changed: 60 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -926,41 +926,74 @@ vaddr_t tee_svc_uref_base = 0;
926926
// (uaddr_t)buf, len);
927927
//}
928928
//
929+
/*
930+
* tee_mmu_check_access_rights - validate a TA-supplied pointer range.
931+
*
932+
* In this ARMv8-M port TAs are statically linked into the secure image and run
933+
* privileged in a single flat address space: there is no per-TA MMU/MPU context
934+
* or region table (utc->mmu is not populated), so the OP-TEE-style ownership and
935+
* secure/non-secure attribution this signature was designed for have no backing
936+
* state. Every live caller requests TEE_MEMORY_ACCESS_ANY_OWNER, i.e. "do not
937+
* enforce per-TA ownership, just accessibility", so this validates the property
938+
* that is actually meaningful here: the range is a real, non-wrapping range. This
939+
* mirrors the wrap/overflow guard cmse_check_address_range() performs on the
940+
* non-secure boundary and rejects the dangerous primitive of an attacker- or
941+
* bug-supplied length that would drive an out-of-bounds copy.
942+
*/
943+
TEE_Result tee_mmu_check_access_rights(struct user_ta_ctx *utc, uint32_t flags,
944+
uaddr_t uaddr, size_t len)
945+
{
946+
(void)utc; /* unused: no per-TA region data here; callers pass NULL */
947+
(void)flags; /* ANY_OWNER: ownership is not (and can not be) enforced */
948+
949+
if (!len)
950+
return TEE_SUCCESS; /* zero-length range is trivially OK */
951+
if (!uaddr)
952+
return TEE_ERROR_ACCESS_DENIED;
953+
/*
954+
* Reject a range that wraps the address space. len is non-zero here, so
955+
* the last byte is at uaddr + len - 1; if that is below uaddr the range
956+
* overflowed. Matches cmse_check_address_range()'s end-of-range test and
957+
* still accepts a range whose last byte sits at the top of memory.
958+
*/
959+
if (uaddr + len - 1 < uaddr)
960+
return TEE_ERROR_ACCESS_DENIED;
961+
962+
return TEE_SUCCESS;
963+
}
964+
929965
TEE_Result tee_svc_copy_from_user(void *kaddr, const void *uaddr, size_t len)
930966
{
931-
// TEE_Result res;
932-
// struct tee_ta_session *s;
933-
//
934-
// res = tee_ta_get_current_session(&s);
935-
// if (res != TEE_SUCCESS)
936-
// return res;
937-
//
938-
// res = tee_mmu_check_access_rights(to_user_ta_ctx(s->ctx),
939-
// TEE_MEMORY_ACCESS_READ |
940-
// TEE_MEMORY_ACCESS_ANY_OWNER,
941-
// (uaddr_t)uaddr, len);
942-
// if (res != TEE_SUCCESS)
943-
// return res;
944-
//
967+
TEE_Result res;
968+
969+
/*
970+
* No session lookup here: tee_mmu_check_access_rights() ignores the utc
971+
* in this port, and tee_ta_get_current_session() returns TEE_SUCCESS with
972+
* a NULL current_session when no TA session is active, so dereferencing
973+
* it (to_user_ta_ctx(s->ctx)) would fault instead of failing gracefully.
974+
*/
975+
res = tee_mmu_check_access_rights(NULL,
976+
TEE_MEMORY_ACCESS_READ |
977+
TEE_MEMORY_ACCESS_ANY_OWNER,
978+
(uaddr_t)uaddr, len);
979+
if (res != TEE_SUCCESS)
980+
return res;
981+
945982
memcpy(kaddr, uaddr, len);
946983
return TEE_SUCCESS;
947984
}
948985

949986
TEE_Result tee_svc_copy_to_user(void *uaddr, const void *kaddr, size_t len)
950987
{
951-
// TEE_Result res;
952-
// struct tee_ta_session *s;
953-
//
954-
// res = tee_ta_get_current_session(&s);
955-
// if (res != TEE_SUCCESS)
956-
// return res;
957-
//
958-
// res = tee_mmu_check_access_rights(to_user_ta_ctx(s->ctx),
959-
// TEE_MEMORY_ACCESS_WRITE |
960-
// TEE_MEMORY_ACCESS_ANY_OWNER,
961-
// (uaddr_t)uaddr, len);
962-
// if (res != TEE_SUCCESS)
963-
// return res;
988+
TEE_Result res;
989+
990+
/* See tee_svc_copy_from_user() on why no session is looked up here. */
991+
res = tee_mmu_check_access_rights(NULL,
992+
TEE_MEMORY_ACCESS_WRITE |
993+
TEE_MEMORY_ACCESS_ANY_OWNER,
994+
(uaddr_t)uaddr, len);
995+
if (res != TEE_SUCCESS)
996+
return res;
964997

965998
memcpy(uaddr, kaddr, len);
966999
return TEE_SUCCESS;

tee/tee/tee_svc_cryp.c

Lines changed: 56 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1337,15 +1337,19 @@ static TEE_Result copy_in_attrs(struct user_ta_ctx *utc,
13371337
const struct utee_attribute *usr_attrs,
13381338
uint32_t attr_count, TEE_Attribute *attrs)
13391339
{
1340-
// TEE_Result res;
1340+
TEE_Result res;
13411341
uint32_t n;
1342-
(void) utc;
1343-
// res = tee_mmu_check_access_rights(utc,
1344-
// TEE_MEMORY_ACCESS_READ | TEE_MEMORY_ACCESS_ANY_OWNER,
1345-
// (uaddr_t)usr_attrs,
1346-
// attr_count * sizeof(struct utee_attribute));
1347-
// if (res != TEE_SUCCESS)
1348-
// return res;
1342+
size_t attrs_size;
1343+
1344+
/* Guard the range length against a 32-bit multiply wrap. */
1345+
if (MUL_OVERFLOW(attr_count, sizeof(struct utee_attribute), &attrs_size))
1346+
return TEE_ERROR_OVERFLOW;
1347+
1348+
res = tee_mmu_check_access_rights(utc,
1349+
TEE_MEMORY_ACCESS_READ | TEE_MEMORY_ACCESS_ANY_OWNER,
1350+
(uaddr_t)usr_attrs, attrs_size);
1351+
if (res != TEE_SUCCESS)
1352+
return res;
13491353

13501354
for (n = 0; n < attr_count; n++) {
13511355
attrs[n].attributeID = usr_attrs[n].attribute_id;
@@ -1356,11 +1360,11 @@ static TEE_Result copy_in_attrs(struct user_ta_ctx *utc,
13561360
uintptr_t buf = usr_attrs[n].a;
13571361
size_t len = usr_attrs[n].b;
13581362

1359-
// res = tee_mmu_check_access_rights(utc,
1360-
// TEE_MEMORY_ACCESS_READ |
1361-
// TEE_MEMORY_ACCESS_ANY_OWNER, buf, len);
1362-
// if (res != TEE_SUCCESS)
1363-
// return res;
1363+
res = tee_mmu_check_access_rights(utc,
1364+
TEE_MEMORY_ACCESS_READ |
1365+
TEE_MEMORY_ACCESS_ANY_OWNER, buf, len);
1366+
if (res != TEE_SUCCESS)
1367+
return res;
13641368
attrs[n].content.ref.buffer = (void *)buf;
13651369
attrs[n].content.ref.length = len;
13661370
}
@@ -2231,12 +2235,12 @@ TEE_Result utee_hash_update(unsigned long state, const void *chunk,
22312235
if (res != TEE_SUCCESS)
22322236
return res;
22332237

2234-
// res = tee_mmu_check_access_rights(to_user_ta_ctx(sess->ctx),
2235-
// TEE_MEMORY_ACCESS_READ |
2236-
// TEE_MEMORY_ACCESS_ANY_OWNER,
2237-
// (uaddr_t)chunk, chunk_size);
2238-
// if (res != TEE_SUCCESS)
2239-
// return res;
2238+
res = tee_mmu_check_access_rights(to_user_ta_ctx(sess->ctx),
2239+
TEE_MEMORY_ACCESS_READ |
2240+
TEE_MEMORY_ACCESS_ANY_OWNER,
2241+
(uaddr_t)chunk, chunk_size);
2242+
if (res != TEE_SUCCESS)
2243+
return res;
22402244

22412245
res = tee_svc_cryp_get_state(sess, tee_svc_uref_to_vaddr(state), &cs);
22422246
if (res != TEE_SUCCESS)
@@ -2277,24 +2281,24 @@ TEE_Result utee_hash_final(unsigned long state, const void *chunk,
22772281
if (res != TEE_SUCCESS)
22782282
return res;
22792283

2280-
// res = tee_mmu_check_access_rights(to_user_ta_ctx(sess->ctx),
2281-
// TEE_MEMORY_ACCESS_READ |
2282-
// TEE_MEMORY_ACCESS_ANY_OWNER,
2283-
// (uaddr_t)chunk, chunk_size);
2284-
// if (res != TEE_SUCCESS)
2285-
// return res;
2284+
res = tee_mmu_check_access_rights(to_user_ta_ctx(sess->ctx),
2285+
TEE_MEMORY_ACCESS_READ |
2286+
TEE_MEMORY_ACCESS_ANY_OWNER,
2287+
(uaddr_t)chunk, chunk_size);
2288+
if (res != TEE_SUCCESS)
2289+
return res;
22862290

22872291
res = tee_svc_copy_from_user(&hlen, hash_len, sizeof(hlen));
22882292
if (res != TEE_SUCCESS)
22892293
return res;
22902294

2291-
// res = tee_mmu_check_access_rights(to_user_ta_ctx(sess->ctx),
2292-
// TEE_MEMORY_ACCESS_READ |
2293-
// TEE_MEMORY_ACCESS_WRITE |
2294-
// TEE_MEMORY_ACCESS_ANY_OWNER,
2295-
// (uaddr_t)hash, hlen);
2296-
// if (res != TEE_SUCCESS)
2297-
// return res;
2295+
res = tee_mmu_check_access_rights(to_user_ta_ctx(sess->ctx),
2296+
TEE_MEMORY_ACCESS_READ |
2297+
TEE_MEMORY_ACCESS_WRITE |
2298+
TEE_MEMORY_ACCESS_ANY_OWNER,
2299+
(uaddr_t)hash, hlen);
2300+
if (res != TEE_SUCCESS)
2301+
return res;
22982302

22992303
res = tee_svc_cryp_get_state(sess, tee_svc_uref_to_vaddr(state), &cs);
23002304
if (res != TEE_SUCCESS)
@@ -2373,13 +2377,13 @@ TEE_Result utee_cipher_init(unsigned long state, const void *iv,
23732377
if (res != TEE_SUCCESS)
23742378
return res;
23752379

2376-
// res = tee_mmu_check_access_rights(utc,
2377-
// TEE_MEMORY_ACCESS_READ |
2378-
// TEE_MEMORY_ACCESS_ANY_OWNER,
2379-
// (uaddr_t) iv, iv_len);
2380-
// if (res != TEE_SUCCESS)
2381-
// return res;
2382-
//
2380+
res = tee_mmu_check_access_rights(utc,
2381+
TEE_MEMORY_ACCESS_READ |
2382+
TEE_MEMORY_ACCESS_ANY_OWNER,
2383+
(uaddr_t) iv, iv_len);
2384+
if (res != TEE_SUCCESS)
2385+
return res;
2386+
23832387
res = tee_obj_get(utc, cs->key1, &o);
23842388
if (res != TEE_SUCCESS)
23852389
return res;
@@ -2427,12 +2431,12 @@ static TEE_Result tee_svc_cipher_update_helper(unsigned long state,
24272431
if (res != TEE_SUCCESS)
24282432
return res;
24292433

2430-
// res = tee_mmu_check_access_rights(to_user_ta_ctx(sess->ctx),
2431-
// TEE_MEMORY_ACCESS_READ |
2432-
// TEE_MEMORY_ACCESS_ANY_OWNER,
2433-
// (uaddr_t)src, src_len);
2434-
// if (res != TEE_SUCCESS)
2435-
// return res;
2434+
res = tee_mmu_check_access_rights(to_user_ta_ctx(sess->ctx),
2435+
TEE_MEMORY_ACCESS_READ |
2436+
TEE_MEMORY_ACCESS_ANY_OWNER,
2437+
(uaddr_t)src, src_len);
2438+
if (res != TEE_SUCCESS)
2439+
return res;
24362440

24372441
if (!dst_len) {
24382442
dlen = 0;
@@ -2441,13 +2445,13 @@ static TEE_Result tee_svc_cipher_update_helper(unsigned long state,
24412445
if (res != TEE_SUCCESS)
24422446
return res;
24432447

2444-
// res = tee_mmu_check_access_rights(to_user_ta_ctx(sess->ctx),
2445-
// TEE_MEMORY_ACCESS_READ |
2446-
// TEE_MEMORY_ACCESS_WRITE |
2447-
// TEE_MEMORY_ACCESS_ANY_OWNER,
2448-
// (uaddr_t)dst, dlen);
2449-
// if (res != TEE_SUCCESS)
2450-
// return res;
2448+
res = tee_mmu_check_access_rights(to_user_ta_ctx(sess->ctx),
2449+
TEE_MEMORY_ACCESS_READ |
2450+
TEE_MEMORY_ACCESS_WRITE |
2451+
TEE_MEMORY_ACCESS_ANY_OWNER,
2452+
(uaddr_t)dst, dlen);
2453+
if (res != TEE_SUCCESS)
2454+
return res;
24512455
}
24522456

24532457
if (dlen < src_len) {

0 commit comments

Comments
 (0)