xref: /optee_os/core/kernel/user_access.c (revision e59bc1dbfdd496810cf8afef4a998ee06af1cc92)
1c40a6505SJens Wiklander // SPDX-License-Identifier: BSD-2-Clause
2c40a6505SJens Wiklander /*
3c40a6505SJens Wiklander  * Copyright (c) 2014, STMicroelectronics International N.V.
4ef142203SJens Wiklander  * Copyright (c) 2015-2020, 2022 Linaro Limited
5c40a6505SJens Wiklander  */
6c40a6505SJens Wiklander 
7c40a6505SJens Wiklander #include <initcall.h>
8c40a6505SJens Wiklander #include <kernel/linker.h>
9c40a6505SJens Wiklander #include <kernel/user_access.h>
10c185655eSJelle Sels #include <kernel/user_mode_ctx.h>
11a0e8ffe9SJens Wiklander #include <memtag.h>
1289c9728dSJens Wiklander #include <mm/vm.h>
13c40a6505SJens Wiklander #include <string.h>
14c40a6505SJens Wiklander #include <tee_api_types.h>
15c40a6505SJens Wiklander #include <types_ext.h>
16c40a6505SJens Wiklander 
17c5a0db99SJens Wiklander #define BB_ALIGNMENT	(sizeof(long) * 2)
18c5a0db99SJens Wiklander 
19c5a0db99SJens Wiklander static struct user_mode_ctx *get_current_uctx(void)
20c40a6505SJens Wiklander {
2100b3b9a2SJens Wiklander 	struct ts_session *s = ts_get_current_session();
227e4100f3SJens Wiklander 
23c5a0db99SJens Wiklander 	if (!is_user_mode_ctx(s->ctx)) {
24c5a0db99SJens Wiklander 		/*
25c5a0db99SJens Wiklander 		 * We may be called within a PTA session, which doesn't
26c5a0db99SJens Wiklander 		 * have a user_mode_ctx. Here, try to retrieve the
27c5a0db99SJens Wiklander 		 * user_mode_ctx associated with the calling session.
28c5a0db99SJens Wiklander 		 */
29c5a0db99SJens Wiklander 		s = TAILQ_NEXT(s, link_tsd);
30c5a0db99SJens Wiklander 		if (!s || !is_user_mode_ctx(s->ctx))
31c5a0db99SJens Wiklander 			return NULL;
32c5a0db99SJens Wiklander 	}
33c5a0db99SJens Wiklander 
34c5a0db99SJens Wiklander 	return to_user_mode_ctx(s->ctx);
35c5a0db99SJens Wiklander }
36c5a0db99SJens Wiklander 
37*e59bc1dbSJens Wiklander TEE_Result check_user_access(uint32_t flags, const void *uaddr, size_t len)
38c5a0db99SJens Wiklander {
39c5a0db99SJens Wiklander 	struct user_mode_ctx *uctx = get_current_uctx();
40c5a0db99SJens Wiklander 
41c5a0db99SJens Wiklander 	if (!uctx)
42c5a0db99SJens Wiklander 		return TEE_ERROR_GENERIC;
43c5a0db99SJens Wiklander 
44c5a0db99SJens Wiklander 	return vm_check_access_rights(uctx, flags, (vaddr_t)uaddr, len);
45c40a6505SJens Wiklander }
46c40a6505SJens Wiklander 
47c40a6505SJens Wiklander TEE_Result copy_from_user(void *kaddr, const void *uaddr, size_t len)
48c40a6505SJens Wiklander {
497e4100f3SJens Wiklander 	uint32_t flags = TEE_MEMORY_ACCESS_READ | TEE_MEMORY_ACCESS_ANY_OWNER;
50ef142203SJens Wiklander 	TEE_Result res = TEE_SUCCESS;
51c40a6505SJens Wiklander 
52ef142203SJens Wiklander 	uaddr = memtag_strip_tag_const(uaddr);
53*e59bc1dbSJens Wiklander 	res = check_user_access(flags, uaddr, len);
544e154320SSeonghyun Park 	if (!res) {
554e154320SSeonghyun Park 		enter_user_access();
56c40a6505SJens Wiklander 		memcpy(kaddr, uaddr, len);
574e154320SSeonghyun Park 		exit_user_access();
584e154320SSeonghyun Park 	}
597e4100f3SJens Wiklander 
607e4100f3SJens Wiklander 	return res;
61c40a6505SJens Wiklander }
62c40a6505SJens Wiklander 
63c40a6505SJens Wiklander TEE_Result copy_to_user(void *uaddr, const void *kaddr, size_t len)
64c40a6505SJens Wiklander {
657e4100f3SJens Wiklander 	uint32_t flags = TEE_MEMORY_ACCESS_WRITE | TEE_MEMORY_ACCESS_ANY_OWNER;
66ef142203SJens Wiklander 	TEE_Result res = TEE_SUCCESS;
67c40a6505SJens Wiklander 
68ef142203SJens Wiklander 	uaddr = memtag_strip_tag(uaddr);
69*e59bc1dbSJens Wiklander 	res = check_user_access(flags, uaddr, len);
704e154320SSeonghyun Park 	if (!res) {
714e154320SSeonghyun Park 		enter_user_access();
72c40a6505SJens Wiklander 		memcpy(uaddr, kaddr, len);
734e154320SSeonghyun Park 		exit_user_access();
744e154320SSeonghyun Park 	}
757e4100f3SJens Wiklander 
767e4100f3SJens Wiklander 	return res;
777e4100f3SJens Wiklander }
787e4100f3SJens Wiklander 
797e4100f3SJens Wiklander TEE_Result copy_from_user_private(void *kaddr, const void *uaddr, size_t len)
807e4100f3SJens Wiklander {
817e4100f3SJens Wiklander 	uint32_t flags = TEE_MEMORY_ACCESS_READ;
82ef142203SJens Wiklander 	TEE_Result res = TEE_SUCCESS;
837e4100f3SJens Wiklander 
84ef142203SJens Wiklander 	uaddr = memtag_strip_tag_const(uaddr);
85*e59bc1dbSJens Wiklander 	res = check_user_access(flags, uaddr, len);
864e154320SSeonghyun Park 	if (!res) {
874e154320SSeonghyun Park 		enter_user_access();
887e4100f3SJens Wiklander 		memcpy(kaddr, uaddr, len);
894e154320SSeonghyun Park 		exit_user_access();
904e154320SSeonghyun Park 	}
917e4100f3SJens Wiklander 
927e4100f3SJens Wiklander 	return res;
937e4100f3SJens Wiklander }
947e4100f3SJens Wiklander 
957e4100f3SJens Wiklander TEE_Result copy_to_user_private(void *uaddr, const void *kaddr, size_t len)
967e4100f3SJens Wiklander {
977e4100f3SJens Wiklander 	uint32_t flags = TEE_MEMORY_ACCESS_WRITE;
98ef142203SJens Wiklander 	TEE_Result res = TEE_SUCCESS;
997e4100f3SJens Wiklander 
100ef142203SJens Wiklander 	uaddr = memtag_strip_tag(uaddr);
101*e59bc1dbSJens Wiklander 	res = check_user_access(flags, uaddr, len);
1024e154320SSeonghyun Park 	if (!res) {
1034e154320SSeonghyun Park 		enter_user_access();
1047e4100f3SJens Wiklander 		memcpy(uaddr, kaddr, len);
1054e154320SSeonghyun Park 		exit_user_access();
1064e154320SSeonghyun Park 	}
1077e4100f3SJens Wiklander 
1087e4100f3SJens Wiklander 	return res;
109c40a6505SJens Wiklander }
110c40a6505SJens Wiklander 
111c5a0db99SJens Wiklander void *bb_alloc(size_t len)
112c5a0db99SJens Wiklander {
113c5a0db99SJens Wiklander 	struct user_mode_ctx *uctx = get_current_uctx();
114c5a0db99SJens Wiklander 	size_t offs = 0;
115c5a0db99SJens Wiklander 	void *bb = NULL;
116c5a0db99SJens Wiklander 
117c5a0db99SJens Wiklander 	if (uctx && !ADD_OVERFLOW(uctx->bbuf_offs, len, &offs) &&
118c5a0db99SJens Wiklander 	    offs <= uctx->bbuf_size) {
119c5a0db99SJens Wiklander 		bb = uctx->bbuf + uctx->bbuf_offs;
120c5a0db99SJens Wiklander 		uctx->bbuf_offs = ROUNDUP(offs, BB_ALIGNMENT);
121c5a0db99SJens Wiklander 	}
122c5a0db99SJens Wiklander 	return bb;
123c5a0db99SJens Wiklander }
124c5a0db99SJens Wiklander 
125c5a0db99SJens Wiklander static void bb_free_helper(struct user_mode_ctx *uctx, vaddr_t bb, size_t len)
126c5a0db99SJens Wiklander {
127c5a0db99SJens Wiklander 	vaddr_t bbuf = (vaddr_t)uctx->bbuf;
128c5a0db99SJens Wiklander 
129c5a0db99SJens Wiklander 	if (bb >= bbuf && IS_ALIGNED(bb, BB_ALIGNMENT)) {
130c5a0db99SJens Wiklander 		size_t prev_offs = bb - bbuf;
131c5a0db99SJens Wiklander 
132c5a0db99SJens Wiklander 		if (prev_offs + ROUNDUP(len, BB_ALIGNMENT) == uctx->bbuf_offs)
133c5a0db99SJens Wiklander 			uctx->bbuf_offs = prev_offs;
134c5a0db99SJens Wiklander 	}
135c5a0db99SJens Wiklander }
136c5a0db99SJens Wiklander 
137c5a0db99SJens Wiklander void bb_free(void *bb, size_t len)
138c5a0db99SJens Wiklander {
139c5a0db99SJens Wiklander 	struct user_mode_ctx *uctx = get_current_uctx();
140c5a0db99SJens Wiklander 
141c5a0db99SJens Wiklander 	if (uctx)
142c5a0db99SJens Wiklander 		bb_free_helper(uctx, (vaddr_t)bb, len);
143c5a0db99SJens Wiklander }
144c5a0db99SJens Wiklander 
145c5a0db99SJens Wiklander void bb_reset(void)
146c5a0db99SJens Wiklander {
147c5a0db99SJens Wiklander 	struct user_mode_ctx *uctx = get_current_uctx();
148c5a0db99SJens Wiklander 
149c5a0db99SJens Wiklander 	if (uctx)
150c5a0db99SJens Wiklander 		uctx->bbuf_offs = 0;
151c5a0db99SJens Wiklander }
152c5a0db99SJens Wiklander 
153e5aa0f8cSSeonghyun Park TEE_Result clear_user(void *uaddr, size_t n)
154e5aa0f8cSSeonghyun Park {
155e5aa0f8cSSeonghyun Park 	uint32_t flags = TEE_MEMORY_ACCESS_WRITE | TEE_MEMORY_ACCESS_ANY_OWNER;
156e5aa0f8cSSeonghyun Park 	TEE_Result res = TEE_SUCCESS;
157e5aa0f8cSSeonghyun Park 
158e5aa0f8cSSeonghyun Park 	uaddr = memtag_strip_tag(uaddr);
159*e59bc1dbSJens Wiklander 	res = check_user_access(flags, uaddr, n);
160e5aa0f8cSSeonghyun Park 	if (res)
161e5aa0f8cSSeonghyun Park 		return res;
162e5aa0f8cSSeonghyun Park 
1634e154320SSeonghyun Park 	enter_user_access();
164e5aa0f8cSSeonghyun Park 	memset(uaddr, 0, n);
1654e154320SSeonghyun Park 	exit_user_access();
166e5aa0f8cSSeonghyun Park 
167e5aa0f8cSSeonghyun Park 	return TEE_SUCCESS;
168e5aa0f8cSSeonghyun Park }
169e5aa0f8cSSeonghyun Park 
170e5aa0f8cSSeonghyun Park size_t strnlen_user(const void *uaddr, size_t len)
171e5aa0f8cSSeonghyun Park {
172e5aa0f8cSSeonghyun Park 	uint32_t flags = TEE_MEMORY_ACCESS_READ | TEE_MEMORY_ACCESS_ANY_OWNER;
173e5aa0f8cSSeonghyun Park 	TEE_Result res = TEE_SUCCESS;
174e5aa0f8cSSeonghyun Park 	size_t n = 0;
175e5aa0f8cSSeonghyun Park 
176e5aa0f8cSSeonghyun Park 	uaddr = memtag_strip_tag_const(uaddr);
177*e59bc1dbSJens Wiklander 	res = check_user_access(flags, uaddr, len);
1784e154320SSeonghyun Park 	if (!res) {
1794e154320SSeonghyun Park 		enter_user_access();
180e5aa0f8cSSeonghyun Park 		n = strnlen(uaddr, len);
1814e154320SSeonghyun Park 		exit_user_access();
1824e154320SSeonghyun Park 	}
183e5aa0f8cSSeonghyun Park 
184e5aa0f8cSSeonghyun Park 	return n;
185e5aa0f8cSSeonghyun Park }
186e5aa0f8cSSeonghyun Park 
187e5aa0f8cSSeonghyun Park TEE_Result bb_memdup_user(const void *src, size_t len, void **p)
188e5aa0f8cSSeonghyun Park {
189e5aa0f8cSSeonghyun Park 	TEE_Result res = TEE_SUCCESS;
190e5aa0f8cSSeonghyun Park 	void *buf = NULL;
191e5aa0f8cSSeonghyun Park 
192e5aa0f8cSSeonghyun Park 	buf = bb_alloc(len);
193e5aa0f8cSSeonghyun Park 	if (!buf)
194e5aa0f8cSSeonghyun Park 		return TEE_ERROR_OUT_OF_MEMORY;
195e5aa0f8cSSeonghyun Park 
196e5aa0f8cSSeonghyun Park 	res = copy_from_user(buf, src, len);
197e5aa0f8cSSeonghyun Park 	if (res)
198e5aa0f8cSSeonghyun Park 		bb_free(buf, len);
199e5aa0f8cSSeonghyun Park 	else
200e5aa0f8cSSeonghyun Park 		*p = buf;
201e5aa0f8cSSeonghyun Park 
202e5aa0f8cSSeonghyun Park 	return res;
203e5aa0f8cSSeonghyun Park }
204e5aa0f8cSSeonghyun Park 
205e5aa0f8cSSeonghyun Park TEE_Result bb_memdup_user_private(const void *src, size_t len, void **p)
206e5aa0f8cSSeonghyun Park {
207e5aa0f8cSSeonghyun Park 	TEE_Result res = TEE_SUCCESS;
208e5aa0f8cSSeonghyun Park 	void *buf = NULL;
209e5aa0f8cSSeonghyun Park 
210e5aa0f8cSSeonghyun Park 	buf = bb_alloc(len);
211e5aa0f8cSSeonghyun Park 	if (!buf)
212e5aa0f8cSSeonghyun Park 		return TEE_ERROR_OUT_OF_MEMORY;
213e5aa0f8cSSeonghyun Park 
214e5aa0f8cSSeonghyun Park 	res = copy_from_user_private(buf, src, len);
215e5aa0f8cSSeonghyun Park 	if (res)
216e5aa0f8cSSeonghyun Park 		bb_free(buf, len);
217e5aa0f8cSSeonghyun Park 	else
218e5aa0f8cSSeonghyun Park 		*p = buf;
219e5aa0f8cSSeonghyun Park 
220e5aa0f8cSSeonghyun Park 	return res;
221e5aa0f8cSSeonghyun Park }
222e5aa0f8cSSeonghyun Park 
2239c99bb1dSJens Wiklander TEE_Result bb_strndup_user(const char *src, size_t maxlen, char **dst,
2249c99bb1dSJens Wiklander 			   size_t *dstlen)
2259c99bb1dSJens Wiklander {
2269c99bb1dSJens Wiklander 	uint32_t flags = TEE_MEMORY_ACCESS_READ | TEE_MEMORY_ACCESS_ANY_OWNER;
2279c99bb1dSJens Wiklander 	TEE_Result res = TEE_SUCCESS;
2289c99bb1dSJens Wiklander 	size_t l = 0;
2299c99bb1dSJens Wiklander 	char *d = NULL;
2309c99bb1dSJens Wiklander 
2319c99bb1dSJens Wiklander 	src = memtag_strip_tag_const(src);
232*e59bc1dbSJens Wiklander 	res = check_user_access(flags, src, maxlen);
2339c99bb1dSJens Wiklander 	if (res)
2349c99bb1dSJens Wiklander 		return res;
2354e154320SSeonghyun Park 
2364e154320SSeonghyun Park 	enter_user_access();
2379c99bb1dSJens Wiklander 	l = strnlen(src, maxlen);
2384e154320SSeonghyun Park 	exit_user_access();
2394e154320SSeonghyun Park 
2409c99bb1dSJens Wiklander 	d = bb_alloc(l + 1);
2419c99bb1dSJens Wiklander 	if (!d)
2429c99bb1dSJens Wiklander 		return TEE_ERROR_OUT_OF_MEMORY;
2434e154320SSeonghyun Park 
2444e154320SSeonghyun Park 	enter_user_access();
2459c99bb1dSJens Wiklander 	memcpy(d, src, l);
2464e154320SSeonghyun Park 	exit_user_access();
2474e154320SSeonghyun Park 
2489c99bb1dSJens Wiklander 	d[l] = 0;
2499c99bb1dSJens Wiklander 
2509c99bb1dSJens Wiklander 	*dst = d;
2519c99bb1dSJens Wiklander 	*dstlen = l;
2529c99bb1dSJens Wiklander 	return TEE_SUCCESS;
2539c99bb1dSJens Wiklander }
2549c99bb1dSJens Wiklander 
255c40a6505SJens Wiklander TEE_Result copy_kaddr_to_uref(uint32_t *uref, void *kaddr)
256c40a6505SJens Wiklander {
257c40a6505SJens Wiklander 	uint32_t ref = kaddr_to_uref(kaddr);
258c40a6505SJens Wiklander 
2597e4100f3SJens Wiklander 	return copy_to_user_private(uref, &ref, sizeof(ref));
260c40a6505SJens Wiklander }
261c40a6505SJens Wiklander 
262c40a6505SJens Wiklander uint32_t kaddr_to_uref(void *kaddr)
263c40a6505SJens Wiklander {
264a0e8ffe9SJens Wiklander 	if (MEMTAG_IS_ENABLED) {
265a0e8ffe9SJens Wiklander 		unsigned int uref_tag_shift = 32 - MEMTAG_TAG_WIDTH;
266a0e8ffe9SJens Wiklander 		vaddr_t uref = memtag_strip_tag_vaddr(kaddr);
267a0e8ffe9SJens Wiklander 
268a0e8ffe9SJens Wiklander 		uref -= VCORE_START_VA;
269a0e8ffe9SJens Wiklander 		assert(uref < (UINT32_MAX >> MEMTAG_TAG_WIDTH));
270a0e8ffe9SJens Wiklander 		uref |= memtag_get_tag(kaddr) << uref_tag_shift;
271a0e8ffe9SJens Wiklander 		return uref;
272a0e8ffe9SJens Wiklander 	}
273a0e8ffe9SJens Wiklander 
274c40a6505SJens Wiklander 	assert(((vaddr_t)kaddr - VCORE_START_VA) < UINT32_MAX);
275c40a6505SJens Wiklander 	return (vaddr_t)kaddr - VCORE_START_VA;
276c40a6505SJens Wiklander }
277c40a6505SJens Wiklander 
278c40a6505SJens Wiklander vaddr_t uref_to_vaddr(uint32_t uref)
279c40a6505SJens Wiklander {
280a0e8ffe9SJens Wiklander 	if (MEMTAG_IS_ENABLED) {
281a0e8ffe9SJens Wiklander 		vaddr_t u = uref & (UINT32_MAX >> MEMTAG_TAG_WIDTH);
282a0e8ffe9SJens Wiklander 		unsigned int uref_tag_shift = 32 - MEMTAG_TAG_WIDTH;
283a0e8ffe9SJens Wiklander 		uint8_t tag = uref >> uref_tag_shift;
284a0e8ffe9SJens Wiklander 
285a0e8ffe9SJens Wiklander 		return memtag_insert_tag_vaddr(VCORE_START_VA + u, tag);
286a0e8ffe9SJens Wiklander 	}
287a0e8ffe9SJens Wiklander 
288c40a6505SJens Wiklander 	return VCORE_START_VA + uref;
289c40a6505SJens Wiklander }
290