xref: /optee_os/core/kernel/user_access.c (revision 0bba39ebe9bfa1f99bb2eeb0e749c3232a4908cf)
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 
37e59bc1dbSJens 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);
53e59bc1dbSJens 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);
69e59bc1dbSJens 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);
85e59bc1dbSJens 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);
101e59bc1dbSJens 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 
111e980c685SJens Wiklander static void *maybe_tag_bb(void *buf, size_t sz)
112e980c685SJens Wiklander {
113e980c685SJens Wiklander 	static_assert(MEMTAG_GRANULE_SIZE <= BB_ALIGNMENT);
114e980c685SJens Wiklander 
115e980c685SJens Wiklander 	if (!MEMTAG_IS_ENABLED)
116e980c685SJens Wiklander 		return buf;
117e980c685SJens Wiklander 
118e980c685SJens Wiklander 	assert(!((vaddr_t)buf % MEMTAG_GRANULE_SIZE));
119e980c685SJens Wiklander 	return memtag_set_random_tags(buf, ROUNDUP(sz, MEMTAG_GRANULE_SIZE));
120e980c685SJens Wiklander }
121e980c685SJens Wiklander 
122e980c685SJens Wiklander static void maybe_untag_bb(void *buf, size_t sz)
123e980c685SJens Wiklander {
124e980c685SJens Wiklander 	if (MEMTAG_IS_ENABLED) {
125e980c685SJens Wiklander 		assert(!((vaddr_t)buf % MEMTAG_GRANULE_SIZE));
126e980c685SJens Wiklander 		memtag_set_tags(buf, ROUNDUP(sz, MEMTAG_GRANULE_SIZE), 0);
127e980c685SJens Wiklander 	}
128e980c685SJens Wiklander }
129e980c685SJens Wiklander 
130c5a0db99SJens Wiklander void *bb_alloc(size_t len)
131c5a0db99SJens Wiklander {
132c5a0db99SJens Wiklander 	struct user_mode_ctx *uctx = get_current_uctx();
133c5a0db99SJens Wiklander 	size_t offs = 0;
134c5a0db99SJens Wiklander 	void *bb = NULL;
135c5a0db99SJens Wiklander 
136c5a0db99SJens Wiklander 	if (uctx && !ADD_OVERFLOW(uctx->bbuf_offs, len, &offs) &&
137c5a0db99SJens Wiklander 	    offs <= uctx->bbuf_size) {
138e980c685SJens Wiklander 		bb = maybe_tag_bb(uctx->bbuf + uctx->bbuf_offs, len);
139c5a0db99SJens Wiklander 		uctx->bbuf_offs = ROUNDUP(offs, BB_ALIGNMENT);
140c5a0db99SJens Wiklander 	}
141c5a0db99SJens Wiklander 	return bb;
142c5a0db99SJens Wiklander }
143c5a0db99SJens Wiklander 
144c5a0db99SJens Wiklander static void bb_free_helper(struct user_mode_ctx *uctx, vaddr_t bb, size_t len)
145c5a0db99SJens Wiklander {
146c5a0db99SJens Wiklander 	vaddr_t bbuf = (vaddr_t)uctx->bbuf;
147c5a0db99SJens Wiklander 
148c5a0db99SJens Wiklander 	if (bb >= bbuf && IS_ALIGNED(bb, BB_ALIGNMENT)) {
149c5a0db99SJens Wiklander 		size_t prev_offs = bb - bbuf;
150c5a0db99SJens Wiklander 
151e980c685SJens Wiklander 		/*
152e980c685SJens Wiklander 		 * Even if we can't update offset we can still invalidate
153e980c685SJens Wiklander 		 * the memory allocation.
154e980c685SJens Wiklander 		 */
155e980c685SJens Wiklander 		maybe_untag_bb((void *)bb, len);
156e980c685SJens Wiklander 
157c5a0db99SJens Wiklander 		if (prev_offs + ROUNDUP(len, BB_ALIGNMENT) == uctx->bbuf_offs)
158c5a0db99SJens Wiklander 			uctx->bbuf_offs = prev_offs;
159c5a0db99SJens Wiklander 	}
160c5a0db99SJens Wiklander }
161c5a0db99SJens Wiklander 
162c5a0db99SJens Wiklander void bb_free(void *bb, size_t len)
163c5a0db99SJens Wiklander {
164c5a0db99SJens Wiklander 	struct user_mode_ctx *uctx = get_current_uctx();
165c5a0db99SJens Wiklander 
166c5a0db99SJens Wiklander 	if (uctx)
167e980c685SJens Wiklander 		bb_free_helper(uctx, memtag_strip_tag_vaddr(bb), len);
168c5a0db99SJens Wiklander }
169c5a0db99SJens Wiklander 
170b39fcd95SJens Wiklander void bb_free_wipe(void *bb, size_t len)
171b39fcd95SJens Wiklander {
172b39fcd95SJens Wiklander 	if (bb)
173b39fcd95SJens Wiklander 		memset(bb, 0, len);
174b39fcd95SJens Wiklander 	bb_free(bb, len);
175b39fcd95SJens Wiklander }
176b39fcd95SJens Wiklander 
177c5a0db99SJens Wiklander void bb_reset(void)
178c5a0db99SJens Wiklander {
179c5a0db99SJens Wiklander 	struct user_mode_ctx *uctx = get_current_uctx();
180c5a0db99SJens Wiklander 
181e980c685SJens Wiklander 	if (uctx) {
182e980c685SJens Wiklander 		/*
183e980c685SJens Wiklander 		 * Only the part up to the offset have been allocated, so
184e980c685SJens Wiklander 		 * no need to clear tags beyond that.
185e980c685SJens Wiklander 		 */
186e980c685SJens Wiklander 		maybe_untag_bb(uctx->bbuf, uctx->bbuf_offs);
187e980c685SJens Wiklander 
188c5a0db99SJens Wiklander 		uctx->bbuf_offs = 0;
189c5a0db99SJens Wiklander 	}
190e980c685SJens Wiklander }
191c5a0db99SJens Wiklander 
192e5aa0f8cSSeonghyun Park TEE_Result clear_user(void *uaddr, size_t n)
193e5aa0f8cSSeonghyun Park {
194e5aa0f8cSSeonghyun Park 	uint32_t flags = TEE_MEMORY_ACCESS_WRITE | TEE_MEMORY_ACCESS_ANY_OWNER;
195e5aa0f8cSSeonghyun Park 	TEE_Result res = TEE_SUCCESS;
196e5aa0f8cSSeonghyun Park 
197e5aa0f8cSSeonghyun Park 	uaddr = memtag_strip_tag(uaddr);
198e59bc1dbSJens Wiklander 	res = check_user_access(flags, uaddr, n);
199e5aa0f8cSSeonghyun Park 	if (res)
200e5aa0f8cSSeonghyun Park 		return res;
201e5aa0f8cSSeonghyun Park 
2024e154320SSeonghyun Park 	enter_user_access();
203e5aa0f8cSSeonghyun Park 	memset(uaddr, 0, n);
2044e154320SSeonghyun Park 	exit_user_access();
205e5aa0f8cSSeonghyun Park 
206e5aa0f8cSSeonghyun Park 	return TEE_SUCCESS;
207e5aa0f8cSSeonghyun Park }
208e5aa0f8cSSeonghyun Park 
209e5aa0f8cSSeonghyun Park size_t strnlen_user(const void *uaddr, size_t len)
210e5aa0f8cSSeonghyun Park {
211e5aa0f8cSSeonghyun Park 	uint32_t flags = TEE_MEMORY_ACCESS_READ | TEE_MEMORY_ACCESS_ANY_OWNER;
212e5aa0f8cSSeonghyun Park 	TEE_Result res = TEE_SUCCESS;
213e5aa0f8cSSeonghyun Park 	size_t n = 0;
214e5aa0f8cSSeonghyun Park 
215*0bba39ebSJens Wiklander 	if (!len)
216*0bba39ebSJens Wiklander 		return 0;
217*0bba39ebSJens Wiklander 
218e5aa0f8cSSeonghyun Park 	uaddr = memtag_strip_tag_const(uaddr);
219e59bc1dbSJens Wiklander 	res = check_user_access(flags, uaddr, len);
2204e154320SSeonghyun Park 	if (!res) {
2214e154320SSeonghyun Park 		enter_user_access();
222e5aa0f8cSSeonghyun Park 		n = strnlen(uaddr, len);
2234e154320SSeonghyun Park 		exit_user_access();
2244e154320SSeonghyun Park 	}
225e5aa0f8cSSeonghyun Park 
226e5aa0f8cSSeonghyun Park 	return n;
227e5aa0f8cSSeonghyun Park }
228e5aa0f8cSSeonghyun Park 
229*0bba39ebSJens Wiklander static TEE_Result __bb_memdup_user(TEE_Result (*copy_func)(void *uaddr,
230*0bba39ebSJens Wiklander 							   const void *kaddr,
231*0bba39ebSJens Wiklander 							   size_t len),
232*0bba39ebSJens Wiklander 				   const void *src, size_t len, void **p)
233e5aa0f8cSSeonghyun Park {
234e5aa0f8cSSeonghyun Park 	TEE_Result res = TEE_SUCCESS;
235e5aa0f8cSSeonghyun Park 	void *buf = NULL;
236e5aa0f8cSSeonghyun Park 
237e5aa0f8cSSeonghyun Park 	buf = bb_alloc(len);
238e5aa0f8cSSeonghyun Park 	if (!buf)
239e5aa0f8cSSeonghyun Park 		return TEE_ERROR_OUT_OF_MEMORY;
240e5aa0f8cSSeonghyun Park 
241*0bba39ebSJens Wiklander 	if (len)
242*0bba39ebSJens Wiklander 		res = copy_func(buf, src, len);
243*0bba39ebSJens Wiklander 
244e5aa0f8cSSeonghyun Park 	if (res)
245e5aa0f8cSSeonghyun Park 		bb_free(buf, len);
246e5aa0f8cSSeonghyun Park 	else
247e5aa0f8cSSeonghyun Park 		*p = buf;
248e5aa0f8cSSeonghyun Park 
249e5aa0f8cSSeonghyun Park 	return res;
250e5aa0f8cSSeonghyun Park }
251e5aa0f8cSSeonghyun Park 
252*0bba39ebSJens Wiklander TEE_Result bb_memdup_user(const void *src, size_t len, void **p)
253*0bba39ebSJens Wiklander {
254*0bba39ebSJens Wiklander 	return __bb_memdup_user(copy_from_user, src, len, p);
255*0bba39ebSJens Wiklander }
256*0bba39ebSJens Wiklander 
257e5aa0f8cSSeonghyun Park TEE_Result bb_memdup_user_private(const void *src, size_t len, void **p)
258e5aa0f8cSSeonghyun Park {
259*0bba39ebSJens Wiklander 	return __bb_memdup_user(copy_from_user_private, src, len, p);
260e5aa0f8cSSeonghyun Park }
261e5aa0f8cSSeonghyun Park 
2629c99bb1dSJens Wiklander TEE_Result bb_strndup_user(const char *src, size_t maxlen, char **dst,
2639c99bb1dSJens Wiklander 			   size_t *dstlen)
2649c99bb1dSJens Wiklander {
2659c99bb1dSJens Wiklander 	uint32_t flags = TEE_MEMORY_ACCESS_READ | TEE_MEMORY_ACCESS_ANY_OWNER;
2669c99bb1dSJens Wiklander 	TEE_Result res = TEE_SUCCESS;
2679c99bb1dSJens Wiklander 	size_t l = 0;
2689c99bb1dSJens Wiklander 	char *d = NULL;
2699c99bb1dSJens Wiklander 
2709c99bb1dSJens Wiklander 	src = memtag_strip_tag_const(src);
271*0bba39ebSJens Wiklander 	if (maxlen) {
272e59bc1dbSJens Wiklander 		res = check_user_access(flags, src, maxlen);
2739c99bb1dSJens Wiklander 		if (res)
2749c99bb1dSJens Wiklander 			return res;
2754e154320SSeonghyun Park 
2764e154320SSeonghyun Park 		enter_user_access();
2779c99bb1dSJens Wiklander 		l = strnlen(src, maxlen);
2784e154320SSeonghyun Park 		exit_user_access();
279*0bba39ebSJens Wiklander 	}
2804e154320SSeonghyun Park 
2819c99bb1dSJens Wiklander 	d = bb_alloc(l + 1);
2829c99bb1dSJens Wiklander 	if (!d)
2839c99bb1dSJens Wiklander 		return TEE_ERROR_OUT_OF_MEMORY;
2844e154320SSeonghyun Park 
285*0bba39ebSJens Wiklander 	if (l) {
2864e154320SSeonghyun Park 		enter_user_access();
2879c99bb1dSJens Wiklander 		memcpy(d, src, l);
2884e154320SSeonghyun Park 		exit_user_access();
289*0bba39ebSJens Wiklander 	}
2904e154320SSeonghyun Park 
2919c99bb1dSJens Wiklander 	d[l] = 0;
2929c99bb1dSJens Wiklander 
2939c99bb1dSJens Wiklander 	*dst = d;
2949c99bb1dSJens Wiklander 	*dstlen = l;
2959c99bb1dSJens Wiklander 	return TEE_SUCCESS;
2969c99bb1dSJens Wiklander }
2979c99bb1dSJens Wiklander 
298c40a6505SJens Wiklander TEE_Result copy_kaddr_to_uref(uint32_t *uref, void *kaddr)
299c40a6505SJens Wiklander {
300c40a6505SJens Wiklander 	uint32_t ref = kaddr_to_uref(kaddr);
301c40a6505SJens Wiklander 
3027e4100f3SJens Wiklander 	return copy_to_user_private(uref, &ref, sizeof(ref));
303c40a6505SJens Wiklander }
304c40a6505SJens Wiklander 
305c40a6505SJens Wiklander uint32_t kaddr_to_uref(void *kaddr)
306c40a6505SJens Wiklander {
307a0e8ffe9SJens Wiklander 	if (MEMTAG_IS_ENABLED) {
308a0e8ffe9SJens Wiklander 		unsigned int uref_tag_shift = 32 - MEMTAG_TAG_WIDTH;
309a0e8ffe9SJens Wiklander 		vaddr_t uref = memtag_strip_tag_vaddr(kaddr);
310a0e8ffe9SJens Wiklander 
311a0e8ffe9SJens Wiklander 		uref -= VCORE_START_VA;
312a0e8ffe9SJens Wiklander 		assert(uref < (UINT32_MAX >> MEMTAG_TAG_WIDTH));
313a0e8ffe9SJens Wiklander 		uref |= memtag_get_tag(kaddr) << uref_tag_shift;
314a0e8ffe9SJens Wiklander 		return uref;
315a0e8ffe9SJens Wiklander 	}
316a0e8ffe9SJens Wiklander 
317c40a6505SJens Wiklander 	assert(((vaddr_t)kaddr - VCORE_START_VA) < UINT32_MAX);
318c40a6505SJens Wiklander 	return (vaddr_t)kaddr - VCORE_START_VA;
319c40a6505SJens Wiklander }
320c40a6505SJens Wiklander 
321c40a6505SJens Wiklander vaddr_t uref_to_vaddr(uint32_t uref)
322c40a6505SJens Wiklander {
323a0e8ffe9SJens Wiklander 	if (MEMTAG_IS_ENABLED) {
324a0e8ffe9SJens Wiklander 		vaddr_t u = uref & (UINT32_MAX >> MEMTAG_TAG_WIDTH);
325a0e8ffe9SJens Wiklander 		unsigned int uref_tag_shift = 32 - MEMTAG_TAG_WIDTH;
326a0e8ffe9SJens Wiklander 		uint8_t tag = uref >> uref_tag_shift;
327a0e8ffe9SJens Wiklander 
328a0e8ffe9SJens Wiklander 		return memtag_insert_tag_vaddr(VCORE_START_VA + u, tag);
329a0e8ffe9SJens Wiklander 	}
330a0e8ffe9SJens Wiklander 
331c40a6505SJens Wiklander 	return VCORE_START_VA + uref;
332c40a6505SJens Wiklander }
333