1 // SPDX-License-Identifier: BSD-2-Clause
2 /*
3  * Copyright (c) 2016, Linaro Limited
4  * All rights reserved.
5  */
6 
7 #include <err.h>
8 #include <fcntl.h>
9 #include <pta_invoke_tests.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <sys/ioctl.h>
14 #include <sys/mman.h>
15 #include <tee_client_api.h>
16 #include <tee_client_api_extensions.h>
17 #include <unistd.h>
18 
19 #include "crypto_common.h"
20 #include "sdp_basic.h"
21 #include "xtest_helpers.h"
22 #include "xtest_test.h"
23 
24 /*
25  * SDP basic test setup overview.
26  *
27  * - A dedicated trusted application (SDP basic TA) supports 3 commands:
28  *   - 'inject' data from a nonsecure buffer into a secure buffer
29  *   - 'transform' data inside a secure buffer (bitwise invert + unsigned incr)
30  *   - 'dump' data from a secure buffer into a nonsecure buffer
31 
32  * - This test client application (CA) invokes the TA for these 3 operations,
33  *   inject random value, trasforming them then dump them.
34  *
35  * To do so, CA allocates a 'SDP secure buffer' and invoke the TA for these 3
36  * operations (inject then transform then dump) over the allocate buffer.
37  *
38  * The secure buffer is currently allocation through ION support adn
39  * registered to OP-TEE and as shared memory.
40  *
41  * To enhance test coverage against buffer alignement usecase, the CA invokes
42  * the TA with a variable offset inside the buffer. As CA injects random data
43  * into the buffer, the CA uses one of the random bytes to set the value of the
44  * offset in the accessed secure buffer.
45  *
46  * For debugging support, the CA may map (in nonsecure world) the secure
47  * buffer to read its content. As this is unsafe on a hardened platform, this
48  * operation is default disable. When enable, error only print out a warning
49  * trace but does not actually fail the test. This also give an easy way to
50  * check that some HW complains on access violation when nonsecure accesses
51  * secure data.
52  */
53 
54 struct tee_ctx {
55 	TEEC_Context ctx;
56 	TEEC_Session sess;
57 };
58 
59 #if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 11, 0))
60 /*
61  * Old ION API to allocate and export a buffer
62  */
allocate_ion_buffer_old_api(size_t size,int heap_type_id,int ion)63 static int allocate_ion_buffer_old_api(size_t size, int heap_type_id, int ion)
64 {
65 	struct ion0_allocation_data alloc_data = { };
66 	struct ion0_handle_data hdl_data = { };
67 	struct ion0_fd_data fd_data = { };
68 	int fd = -1;
69 
70 	alloc_data.len = size;
71 	alloc_data.align = 0;
72 	alloc_data.flags = 0;
73 	alloc_data.heap_id_mask = 1 << heap_type_id;
74 	if (ioctl(ion, ION0_IOC_ALLOC, &alloc_data) == -1) {
75 		fprintf(stderr, "Error: old ION allocate API failed\n");
76 		return fd;
77 	}
78 
79 	fd_data.handle = alloc_data.handle;
80 	if (ioctl(ion, ION0_IOC_SHARE, &fd_data) != -1)
81 		fd = fd_data.fd;
82 	else
83 		fprintf(stderr, "Error: old ION share API failed\n");
84 
85 	hdl_data.handle = alloc_data.handle;
86 	(void)ioctl(ion, ION0_IOC_FREE, &hdl_data);
87 
88 	return fd;
89 }
90 
allocate_ion_buffer(size_t size,const char * heap_name,int heap_type_id,int verbosity)91 int allocate_ion_buffer(size_t size, const char *heap_name, int heap_type_id, int verbosity)
92 {
93 	struct ion_heap_query query_data = { };
94 	struct ion_heap_data heap_data[32] = { };
95 	struct ion_allocation_data alloc_data = { };
96 	int ion = 0;
97 	int fd = -1;
98 	unsigned int idx = 0;
99 
100 	ion = open("/dev/ion", O_RDWR);
101 	if (ion < 0) {
102 		fprintf(stderr, "Error: failed to open /dev/ion\n");
103 		verbose("Seems no ION heap is available.\n");
104 		verbose("To test ION allocation you can enable\n");
105 		verbose("CONFIG_ION and CONFIG_ION_DUMMY in your\n");
106 		verbose("linux kernel configuration.\n");
107 		return fd;
108 	}
109 
110 	if (heap_type_id < 0)
111 		heap_type_id = DEFAULT_HEAP_TYPE;
112 
113 	if (ioctl(ion, ION_IOC_HEAP_QUERY, &query_data) < 0) {
114 		fprintf(stderr, "Error: failed to query the number of heaps\n");
115 		goto out;
116 	}
117 
118 	query_data.heaps = (__u64)(unsigned long)&heap_data;
119 	if (ioctl(ion, ION_IOC_HEAP_QUERY, &query_data) < 0) {
120 		fprintf(stderr, "Info: can't query heaps data, try old API\n");
121 		fd = allocate_ion_buffer_old_api(size, heap_type_id, ion);
122 		goto out;
123 	}
124 
125 	for (idx = 0; idx < query_data.cnt; idx++)
126 		if ((heap_data[idx].type == (unsigned int)heap_type_id) &&
127 		    (strcmp(heap_data[idx].name, heap_name) == 0))
128 			break;
129 	if (idx == query_data.cnt) {
130 		fprintf(stderr, "Error: target heap type %d not found\n",
131 				heap_type_id);
132 		goto out;
133 	}
134 
135 	verbose("Allocate in ION heap '%s' (type=%u, id=%u)\n",
136 		heap_data[idx].name, heap_data[idx].type,
137 		heap_data[idx].heap_id);
138 
139 	alloc_data.len = size;
140 	alloc_data.flags = 0;
141 	alloc_data.heap_id_mask = 1 << heap_data[idx].heap_id;
142 	if (ioctl(ion, ION_IOC_ALLOC, &alloc_data) < 0) {
143 		fprintf(stderr, "Error: failed to allocate in target heap\n");
144 		goto out;
145 	}
146 
147 	fd = alloc_data.fd;
148 out:
149 	close(ion);
150 	return fd;
151 }
152 #else // LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)
allocate_dma_buffer(size_t size,const char * heap_name,int verbosity)153 int allocate_dma_buffer(size_t size, const char *heap_name, int verbosity)
154 {
155 	const char *default_dev = DEFAULT_HEAP_NAME;
156 	char *mem_sec_dev = (char *)default_dev;
157 	struct dma_heap_allocation_data data = { 0 };
158 	int fd_mem_sec;
159 	int fd = -1;
160 
161 	if (heap_name != NULL)
162 		mem_sec_dev = (char *)heap_name;
163 
164 	fd_mem_sec = open(mem_sec_dev, O_RDWR | O_SYNC);
165 	if (fd_mem_sec == -1) {
166 		fprintf(stderr, "Error: failed to open %s\n", mem_sec_dev);
167 		verbose("Seems no DMA buf heap is available.\n");
168 		return -1;
169 	}
170 
171 	data.len = size;
172 	data.fd_flags = O_RDWR | O_CLOEXEC;
173 	data.heap_flags = 0;
174 
175 	if (ioctl(fd_mem_sec, DMA_HEAP_IOCTL_ALLOC, &data) == -1) {
176 		fprintf(stderr, "Error: DMA buf allocate API failed\n");
177 		goto out;
178 	}
179 
180 	fd = data.fd;
181 
182 out:
183 	close(fd_mem_sec);
184 	return fd;
185 }
186 #endif
187 
finalize_tee_ctx(struct tee_ctx * ctx)188 static void finalize_tee_ctx(struct tee_ctx *ctx)
189 {
190 	if (!ctx)
191 		return;
192 
193 	TEEC_CloseSession(&ctx->sess);
194 	TEEC_FinalizeContext(&ctx->ctx);
195 }
196 
create_tee_ctx(struct tee_ctx * ctx,enum test_target_ta target_ta)197 static int create_tee_ctx(struct tee_ctx *ctx, enum test_target_ta target_ta)
198 {
199 	TEEC_Result teerc = TEEC_ERROR_GENERIC;
200 	const TEEC_UUID *uuid = NULL;
201 	uint32_t err_origin = 0;
202 
203 	switch (target_ta) {
204 	case TEST_NS_TO_TA:
205 	case TEST_TA_TO_TA:
206 	case TEST_TA_TO_PTA:
207 		uuid = &sdp_basic_ta_uuid;
208 		break;
209 	case TEST_NS_TO_PTA:
210 		uuid = &pta_invoke_tests_ta_uuid;
211 		break;
212 	default:
213 		return -1;
214 	}
215 
216 	teerc = TEEC_InitializeContext(NULL, &ctx->ctx);
217 	if (teerc != TEEC_SUCCESS)
218 		return -1;
219 
220 	teerc = TEEC_OpenSession(&ctx->ctx, &ctx->sess, uuid,
221 			       TEEC_LOGIN_PUBLIC, NULL, NULL, &err_origin);
222 	if (teerc != TEEC_SUCCESS) {
223 		fprintf(stderr, "Error: open session to target test %s failed %x %d\n",
224 			(target_ta == TEST_NS_TO_PTA) ? "pTA" : "TA",
225 			teerc, err_origin);
226 
227 		TEEC_FinalizeContext(&ctx->ctx);
228 	}
229 	return (teerc == TEEC_SUCCESS) ? 0 : -1;
230 }
231 
tee_register_buffer(struct tee_ctx * ctx,void ** shm_ref,int fd)232 static int tee_register_buffer(struct tee_ctx *ctx, void **shm_ref, int fd)
233 {
234 	TEEC_Result teerc = TEEC_ERROR_GENERIC;
235 	TEEC_SharedMemory *shm = malloc(sizeof(*shm));
236 
237 	if (!shm)
238 		return 1;
239 
240 	shm->flags = TEEC_MEM_INPUT | TEEC_MEM_OUTPUT;
241 	teerc = TEEC_RegisterSharedMemoryFileDescriptor(&ctx->ctx, shm, fd);
242 	if (teerc != TEEC_SUCCESS) {
243 		fprintf(stderr, "Error: TEEC_RegisterMemoryFileDescriptor() failed %x\n",
244 			teerc);
245 		return 1;
246 	}
247 
248 	*shm_ref = shm;
249 	return 0;
250 }
251 
tee_deregister_buffer(struct tee_ctx * ctx,void * shm_ref)252 static void tee_deregister_buffer(struct tee_ctx *ctx, void *shm_ref)
253 {
254 	(void)ctx;
255 
256 	if (!shm_ref)
257 		return;
258 
259 	TEEC_ReleaseSharedMemory((TEEC_SharedMemory *)shm_ref);
260 	free(shm_ref);
261 }
262 
inject_sdp_data(struct tee_ctx * ctx,void * in,size_t offset,size_t len,void * shm_ref,int ind)263 static int inject_sdp_data(struct tee_ctx *ctx,
264 		    void *in, size_t offset, size_t len, void *shm_ref, int ind)
265 {
266 	TEEC_SharedMemory *shm = (TEEC_SharedMemory *)shm_ref;
267 	TEEC_Result teerc = TEEC_ERROR_GENERIC;
268 	TEEC_Operation op = TEEC_OPERATION_INITIALIZER;
269 	uint32_t err_origin = 0;
270 	unsigned int cmd = 0;
271 
272 	switch (ind) {
273 	case TEST_NS_TO_TA:
274 		cmd = TA_SDP_BASIC_CMD_INJECT;
275 		break;
276 	case TEST_TA_TO_TA:
277 		cmd = TA_SDP_BASIC_CMD_INVOKE_INJECT;
278 		break;
279 	case TEST_TA_TO_PTA:
280 		cmd = TA_SDP_BASIC_CMD_PTA_INJECT;
281 		break;
282 	case TEST_NS_TO_PTA:
283 		cmd = PTA_INVOKE_TESTS_CMD_COPY_NSEC_TO_SEC;
284 		break;
285 	default:
286 		return -1;
287 	}
288 
289 	op.paramTypes = TEEC_PARAM_TYPES(TEEC_MEMREF_TEMP_INPUT,
290 					 TEEC_MEMREF_PARTIAL_OUTPUT,
291 					 TEEC_NONE, TEEC_NONE);
292 
293 	op.params[0].tmpref.buffer = in;
294 	op.params[0].tmpref.size = len;
295 
296 	op.params[1].memref.parent = shm;
297 	op.params[1].memref.size = len;
298 	op.params[1].memref.offset = offset;
299 
300 	teerc = TEEC_InvokeCommand(&ctx->sess, cmd, &op, &err_origin);
301 	if (teerc != TEEC_SUCCESS)
302 		fprintf(stderr, "Error: invoke SDP test TA (inject) failed %x %d\n",
303 			teerc, err_origin);
304 
305 	return (teerc == TEEC_SUCCESS) ? 0 : -1;
306 }
307 
transform_sdp_data(struct tee_ctx * ctx,size_t offset,size_t len,void * shm_ref,int ind)308 static int transform_sdp_data(struct tee_ctx *ctx,
309 			size_t offset, size_t len, void *shm_ref, int ind)
310 {
311 	TEEC_SharedMemory *shm = (TEEC_SharedMemory *)shm_ref;
312 	TEEC_Result teerc = TEEC_ERROR_GENERIC;
313 	TEEC_Operation op = TEEC_OPERATION_INITIALIZER;
314 	uint32_t err_origin = 0;
315 	unsigned int cmd = 0;
316 
317 	switch (ind) {
318 	case TEST_NS_TO_TA:
319 		cmd = TA_SDP_BASIC_CMD_TRANSFORM;
320 		break;
321 	case TEST_TA_TO_TA:
322 		cmd = TA_SDP_BASIC_CMD_INVOKE_TRANSFORM;
323 		break;
324 	case TEST_TA_TO_PTA:
325 		cmd = TA_SDP_BASIC_CMD_PTA_TRANSFORM;
326 		break;
327 	case TEST_NS_TO_PTA:
328 		cmd = PTA_INVOKE_TESTS_CMD_READ_MODIFY_SEC;
329 		break;
330 	default:
331 		return -1;
332 	}
333 
334 	op.paramTypes = TEEC_PARAM_TYPES(TEEC_MEMREF_PARTIAL_INOUT,
335 					 TEEC_NONE, TEEC_NONE, TEEC_NONE);
336 	op.params[0].memref.parent = shm;
337 	op.params[0].memref.size = len;
338 	op.params[0].memref.offset = offset;
339 
340 	teerc = TEEC_InvokeCommand(&ctx->sess, cmd, &op, &err_origin);
341 	if (teerc != TEEC_SUCCESS)
342 		fprintf(stderr, "Error: invoke SDP test TA (transform) failed %x %d\n",
343 			teerc, err_origin);
344 
345 	return (teerc == TEEC_SUCCESS) ? 0 : -1;
346 }
347 
dump_sdp_data(struct tee_ctx * ctx,void * out,size_t offset,size_t len,void * shm_ref,int ind)348 static int dump_sdp_data(struct tee_ctx *ctx,
349 		  void *out, size_t offset, size_t len, void *shm_ref, int ind)
350 {
351 	TEEC_SharedMemory *shm = (TEEC_SharedMemory *)shm_ref;
352 	TEEC_Result teerc = TEEC_ERROR_GENERIC;
353 	TEEC_Operation op = TEEC_OPERATION_INITIALIZER;
354 	uint32_t err_origin = 0;
355 	unsigned int cmd = 0;
356 
357 	switch (ind) {
358 	case TEST_NS_TO_TA:
359 		cmd = TA_SDP_BASIC_CMD_DUMP;
360 		break;
361 	case TEST_TA_TO_TA:
362 		cmd = TA_SDP_BASIC_CMD_INVOKE_DUMP;
363 		break;
364 	case TEST_TA_TO_PTA:
365 		cmd = TA_SDP_BASIC_CMD_PTA_DUMP;
366 		break;
367 	case TEST_NS_TO_PTA:
368 		cmd = PTA_INVOKE_TESTS_CMD_COPY_SEC_TO_NSEC;
369 		break;
370 	default:
371 		return -1;
372 	}
373 
374 	op.paramTypes = TEEC_PARAM_TYPES(TEEC_MEMREF_PARTIAL_INPUT,
375 					 TEEC_MEMREF_TEMP_OUTPUT,
376 					 TEEC_NONE, TEEC_NONE);
377 	op.params[0].memref.parent = shm;
378 	op.params[0].memref.size = len;
379 	op.params[0].memref.offset = offset;
380 
381 	op.params[1].tmpref.buffer = out;
382 	op.params[1].tmpref.size = len;
383 
384 	teerc = TEEC_InvokeCommand(&ctx->sess, cmd, &op, &err_origin);
385 	if (teerc != TEEC_SUCCESS)
386 		fprintf(stderr, "Error: invoke SDP test TA (dump) failed %x %d\n",
387 			teerc, err_origin);
388 
389 	return (teerc == TEEC_SUCCESS) ? 0 : -1;
390 }
391 
check_sdp_dumped(struct tee_ctx * ctx,void * ref,size_t len,void * out)392 static int check_sdp_dumped(struct tee_ctx *ctx, void *ref, size_t len,
393 								void *out)
394 {
395 	char *bref = (char *)ref;
396 	char *data = (char *)out;
397 	int err = 0;
398 
399 	(void)ctx;
400 
401 	while(len--)
402 		if (*data++ != (unsigned char)(~(*bref++) + 1))
403 			err++;
404 
405 	return err;
406 }
407 
408 /*
409  * Consider 32kByte + 1 of random data is sufficient for an accurate test
410  * whatever the test buffer size is. Random buffer is read as a ring buffer.
411  */
412 #define RANDOM_BUFFER_SIZE	(32 * 1024 + 1)
get_random_bytes(char * out,size_t len)413 static int get_random_bytes(char *out, size_t len)
414 {
415 	static char *rand_buf = NULL;
416 	static size_t rand_idx = 0;
417 	int rc = 0;
418 
419 	if (!rand_buf) {
420 		const char rand_dev[] = "/dev/urandom";
421 		int fd = 0;
422 
423 		rand_buf = malloc(RANDOM_BUFFER_SIZE);
424 		if (!rand_buf) {
425 			fprintf(stderr, "failed to random buffer memory (%d bytes)\n",
426 				RANDOM_BUFFER_SIZE);
427 			return -1;
428 		}
429 
430 		fd = open(rand_dev, O_RDONLY);
431 		if (fd < 0) {
432 			fprintf(stderr, "failed to open %s\n", rand_dev);
433 			return -1;
434 		}
435 
436 		rc = read(fd, rand_buf, RANDOM_BUFFER_SIZE);
437 		if (rc != RANDOM_BUFFER_SIZE) {
438 			fprintf(stderr, "failed to read %d bytes from %s\n",
439 				RANDOM_BUFFER_SIZE, rand_dev);
440 			close(fd);
441 			return -1;
442 		}
443 		close(fd);
444 	}
445 
446 	while (len) {
447 		size_t t_len = (RANDOM_BUFFER_SIZE < len) ? RANDOM_BUFFER_SIZE : len;
448 
449 		if ((rand_idx + t_len) > RANDOM_BUFFER_SIZE) {
450 			int sz_end = RANDOM_BUFFER_SIZE - rand_idx;
451 			int sz_beg = t_len - sz_end;
452 
453 			memcpy(out, rand_buf + rand_idx, sz_end);
454 			memcpy(out + sz_end, rand_buf , sz_beg);
455 			rand_idx = sz_beg;
456 		} else {
457 			memcpy(out, rand_buf + rand_idx, t_len);
458 			rand_idx += t_len;
459 		}
460 		len -= t_len;
461 	}
462 	return 0;
463 }
464 
465 
sdp_basic_test(enum test_target_ta ta,size_t size,size_t loop,const char * heap_name,int ion_heap,int rnd_offset,int verbosity)466 int sdp_basic_test(enum test_target_ta ta, size_t size, size_t loop,
467 		   const char *heap_name, int ion_heap, int rnd_offset, int verbosity)
468 {
469 	struct tee_ctx *ctx = NULL;
470 	unsigned char *test_buf = NULL;
471 	unsigned char *ref_buf = NULL;
472 	void *shm_ref = NULL;
473 	unsigned int err = 1;
474 	int fd = -1;
475 	size_t sdp_size = size;
476 	size_t offset = 0;
477 	size_t loop_cnt = 0;
478 
479 	if (!loop) {
480 		fprintf(stderr, "Error: null loop value\n");
481 		return 1;
482 	}
483 
484 	/* reduce size to enable offset tests (max offset is 255 bytes) */
485 	if (rnd_offset)
486 		size -= 255;
487 
488 	test_buf = malloc(size);
489 	ref_buf = malloc(size);
490 	if (!test_buf || !ref_buf) {
491 		verbose("failed to allocate memory\n");
492 		goto bail1;
493 	}
494 
495 	fd = allocate_buffer(sdp_size, heap_name, ion_heap, verbosity);
496 	if (fd < 0) {
497 		verbose("Failed to allocate SDP buffer (%zu bytes) in %s heap %d: %d\n",
498 				sdp_size, heap_name, ion_heap, fd);
499 		goto bail1;
500 	}
501 
502 	/* register secure buffer to TEE */
503 	ctx = malloc(sizeof(*ctx));
504 	if (!ctx)
505 		goto bail1;
506 	if (create_tee_ctx(ctx, ta))
507 		goto bail1;
508 	if (tee_register_buffer(ctx, &shm_ref, fd))
509 		goto bail2;
510 
511 	/* release registered fd: tee should still hold refcount on resource */
512 	close(fd);
513 	fd = -1;
514 
515 	/* invoke trusted application with secure buffer as memref parameter */
516 	for (loop_cnt = loop; loop_cnt; loop_cnt--) {
517 		/* get an buffer of random-like values */
518 		if (get_random_bytes((char *)ref_buf, size))
519 			goto bail2;
520 		memcpy(test_buf, ref_buf, size);
521 		/* random offset [0 255] */
522 		offset = (unsigned int)*ref_buf;
523 
524 		/* TA writes into SDP buffer */
525 		if (inject_sdp_data(ctx, test_buf, offset, size, shm_ref, ta))
526 			goto bail2;
527 
528 		/* TA reads/writes into SDP buffer */
529 		if (transform_sdp_data(ctx, offset, size, shm_ref, ta))
530 			goto bail2;
531 
532 		/* TA reads into SDP buffer */
533 		if (dump_sdp_data(ctx, test_buf, offset, size, shm_ref, ta))
534 			goto bail2;
535 
536 		/* check dumped data are the expected ones */
537 		if (check_sdp_dumped(ctx, ref_buf, size, test_buf)) {
538 			fprintf(stderr, "check SDP data: %d errors\n", err);
539 			goto bail2;
540 		}
541 	}
542 
543 	err = 0;
544 bail2:
545 	if (fd >= 0)
546 		close(fd);
547 	if (shm_ref)
548 		tee_deregister_buffer(ctx, shm_ref);
549 	finalize_tee_ctx(ctx);
550 bail1:
551 	free(ctx);
552 	free(ref_buf);
553 	free(test_buf);
554 	return err;
555 }
556 
invoke_out_of_bounds(struct tee_ctx * ctx,TEEC_SharedMemory * in,TEEC_SharedMemory * out,size_t offset,size_t size,bool valid_ref,int verbosity)557 static int invoke_out_of_bounds(struct tee_ctx *ctx,
558 				TEEC_SharedMemory *in, TEEC_SharedMemory *out,
559 				size_t offset, size_t size,
560 				bool valid_ref, int verbosity)
561 {
562 	TEEC_Result teerc = TEEC_ERROR_GENERIC;
563 	TEEC_Operation op = TEEC_OPERATION_INITIALIZER;
564 	uint32_t orig = 0;
565 
566 	op.paramTypes = TEEC_PARAM_TYPES(TEEC_MEMREF_PARTIAL_INPUT,
567 					 TEEC_MEMREF_PARTIAL_OUTPUT,
568 					 TEEC_NONE, TEEC_NONE);
569 
570 	op.params[0].memref.parent = in;
571 	op.params[0].memref.offset = 0;
572 	op.params[0].memref.size = size;
573 
574 	op.params[1].memref.parent = out;
575 	op.params[1].memref.offset = offset;
576 	op.params[1].memref.size = size;
577 
578 	teerc = TEEC_InvokeCommand(&ctx->sess, TA_SDP_BASIC_CMD_INJECT,
579 				   &op, &orig);
580 
581 	/*
582 	 * Invocation with invalid references should be nicely rejected by
583 	 * the TEE.
584 	 * Invocation with valid references should reach the TA, whatever
585 	 * result is.
586 	 */
587 	if ((valid_ref && orig != TEEC_ORIGIN_TRUSTED_APP) ||
588 	    (!valid_ref && ((orig == TEEC_ORIGIN_TRUSTED_APP) ||
589 			    (teerc != TEEC_ERROR_GENERIC &&
590 			     teerc != TEEC_ERROR_BAD_PARAMETERS))))
591 		goto error;
592 
593 	verbose("Out of bounds memref test successful:\n");
594 	verbose("Shm size 0x%zx, offset 0x%zx/size 0x%zx: %s/0x%x from %s\n",
595 		out->size, offset, size,
596 		Do_ADBG_GetEnumName(teerc, ADBG_EnumTable_TEEC_Result), teerc,
597 		Do_ADBG_GetEnumName(orig, ADBG_EnumTable_TEEC_ErrorOrigin));
598 	return 0;
599 
600 error:
601 	fprintf(stderr, "Out of bounds memref test FAILURE:\n");
602 	fprintf(stderr,
603 		"Shm size 0x%zx, offset 0x%zx/size 0x%zx: %s/0x%x from %s\n",
604 		out->size, offset, size,
605 		Do_ADBG_GetEnumName(teerc, ADBG_EnumTable_TEEC_Result),	teerc,
606 		Do_ADBG_GetEnumName(orig, ADBG_EnumTable_TEEC_ErrorOrigin));
607 	return 1;
608 }
609 
sdp_out_of_bounds_memref_test(size_t size,const char * heap_name,int ion_heap,int verbosity)610 int sdp_out_of_bounds_memref_test(size_t size, const char *heap_name, int ion_heap, int verbosity)
611 {
612 	struct tee_ctx ctx = { };
613 	int err = 0;
614 	int fd = -1;
615 	TEEC_Result teerc = TEEC_ERROR_GENERIC;
616 	TEEC_SharedMemory in = { };
617 	TEEC_SharedMemory *out = NULL;
618 
619 	if (create_tee_ctx(&ctx, TEST_NS_TO_TA))
620 		return -1;
621 
622 	fd = allocate_buffer(size, heap_name, ion_heap, verbosity);
623 	if (fd < 0) {
624 		verbose("SDP alloc failed (%zu bytes) in %s heap %d: %d\n",
625 			size, heap_name, ion_heap, fd);
626 		err = 1;
627 		goto bail;
628 	}
629 	if (tee_register_buffer(&ctx, (void **)&out, fd)) {
630 		err = 1;
631 		goto bail;
632 	}
633 
634 	/*
635 	 * The ION driver will decide how much SDP memory is being allocated.
636 	 * Rely on this size to test out of bounds reference cases.
637 	 */
638 	size = out->size;
639 
640 	in.size = size;
641 	in.flags = TEEC_MEM_INPUT;
642 	teerc = TEEC_AllocateSharedMemory(&ctx.ctx, &in);
643 	if (teerc) {
644 		verbose("failed to allocate memory\n");
645 		goto bail;
646 	}
647 
648 	if (verbosity) {
649 		/* Valid case: reference inside allocated buffer: last byte */
650 		err += invoke_out_of_bounds(&ctx, &in, out, size - 1, 1,
651 					    true, verbosity);
652 	}
653 
654 	/* Reference overflows allocated buffer by 1 byte */
655 	err += invoke_out_of_bounds(&ctx, &in, out, size - 1, 2,
656 				    false, verbosity);
657 
658 	/* Reference oveflows allocated buffer by more than 4kB byte */
659 	err += invoke_out_of_bounds(&ctx, &in, out, size - 1, 5000,
660 				    false, verbosity);
661 
662 	/* Offset exceeds allocated buffer size value by 1 byte */
663 	err += invoke_out_of_bounds(&ctx, &in, out, size, 1,
664 				    false, verbosity);
665 
666 	/* Offset exceeds allocated size value by 4kByte */
667 	err += invoke_out_of_bounds(&ctx, &in, out, size, 4096,
668 				    false, verbosity);
669 
670 	/* Offset + size overflows offset value */
671 	err += invoke_out_of_bounds(&ctx, &in, out, 2, ~0,
672 				    false, verbosity);
673 
674 	TEEC_ReleaseSharedMemory(&in);
675 bail:
676 	tee_deregister_buffer(&ctx, out);
677 	if (fd >= 0)
678 		close(fd);
679 	finalize_tee_ctx(&ctx);
680 
681 	return err;
682 }
683 
684 #define _TO_STR(x) #x
685 #define TO_STR(x) _TO_STR(x)
686 
usage(const char * progname,size_t size,int loop,const char * heap_name,int ion_heap)687 static void usage(const char *progname, size_t size, int loop, const char *heap_name, int ion_heap)
688 {
689 	fprintf(stderr, "Usage: %s [OPTION]\n", progname);
690 	fprintf(stderr,
691 		"Testing basic accesses to secure buffer (SDP) on OP-TEE.\n"
692 		"Allocates a secure buffer and invoke a TA to access it.\n"
693 		"TA is used to init/transform/dump the secure buffer.\n"
694 		"CA check dumped content.\n\n");
695 
696 	fprintf(stderr, "Options:\n");
697 	fprintf(stderr, " -h|--help         Print this help and exit\n");
698 	fprintf(stderr, " -v                Be verbose\n");
699 	fprintf(stderr, " -s SIZE           SDP buffer byte size [%zu]\n", size);
700 	fprintf(stderr, " -n LOOP           Test loop iterations [%u]\n", loop);
701 #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 11, 0)
702 	fprintf(stderr, " --heap ID         Target heap ID [%d]\n", ion_heap);
703 #else
704 	(void)ion_heap;
705 #endif
706 	fprintf(stderr, " --heap-name NAME  Target heap name [%s]\n", heap_name);
707 	fprintf(stderr, " --no-offset       No random offset [0 255] in buffer\n");
708 }
709 
710 #define NEXT_ARG(i) \
711 	do { \
712 		if (++i == argc) { \
713 			fprintf(stderr, "%s: %s: missing argument\n", \
714 				argv[0], argv[i-1]); \
715 			return 1; \
716 		} \
717 	} while (0);
718 
719 #define CHECK_RESULT(_res, _exp, _action) \
720 	if ((_res) == (_exp)) { \
721 		verbose("Test passed\n"); \
722 	} else { \
723 		verbose("Test failed!\n"); \
724 		_action; \
725 	}
726 
sdp_basic_runner_cmd_parser(int argc,char * argv[])727 int sdp_basic_runner_cmd_parser(int argc, char *argv[])
728 {
729 	size_t test_size = 5000;
730 	size_t test_loop = 1000;
731 	int ion_heap = DEFAULT_HEAP_TYPE;
732 	const char *heap_name = DEFAULT_HEAP_NAME;
733 	int rnd_offset = 1;
734 	int verbosity = 1;
735 	int err = 0;
736 	int i = 0;
737 
738 	/* Parse command line */
739 	for (i = 1; i < argc; i++) {
740 		if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
741 			usage(argv[0], test_size, test_loop, heap_name, ion_heap);
742 			return 0;
743 		}
744 	}
745 	for (i = 1; i < argc; i++) {
746 		if (!strcmp(argv[i], "-v")) {
747 			verbosity++;
748 		} else if (!strcmp(argv[i], "-s")) {
749 			NEXT_ARG(i);
750 			test_size = atoi(argv[i]);
751 		} else if (!strcmp(argv[i], "-n")) {
752 			NEXT_ARG(i);
753 			test_loop = atoi(argv[i]);
754 #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 11, 0)
755 		} else if (!strcmp(argv[i], "--ion-heap")) {
756 			NEXT_ARG(i);
757 			ion_heap = atoi(argv[i]);
758 #endif
759 		} else if (!strcmp(argv[i], "--heap-name")) {
760 			NEXT_ARG(i);
761 			heap_name = argv[i];
762 		} else if (!strcmp(argv[i], "--no-offset")) {
763 			rnd_offset = 0;
764 		} else {
765 			fprintf(stderr, "%s: invalid argument: %s\n",
766 				argv[0], argv[i]);
767 			usage(argv[0], test_size, test_loop, heap_name, ion_heap);
768 			return 1;
769 		}
770 	}
771 
772 	verbose("\nSecure Data Path basic access: "
773 		"NS invokes SDP TA\n");
774 	err = sdp_basic_test(TEST_NS_TO_TA, test_size, test_loop, heap_name, ion_heap,
775 			     rnd_offset, verbosity);
776 	CHECK_RESULT(err, 0, return 1);
777 
778 	verbose("\nSecure Data Path basic access: "
779 		"SDP TA invokes SDP TA\n");
780 	err = sdp_basic_test(TEST_TA_TO_TA, test_size, test_loop, heap_name, ion_heap,
781 			     rnd_offset, verbosity);
782 	CHECK_RESULT(err, 0, return 1);
783 
784 	verbose("\nSecure Data Path basic access: "
785 		"SDP TA invokes SDP pTA\n");
786 	err = sdp_basic_test(TEST_TA_TO_PTA, test_size, test_loop, heap_name, ion_heap,
787 			     rnd_offset, verbosity);
788 	CHECK_RESULT(err, 0, return 1);
789 
790 	verbose("\nSecure Data Path basic access: "
791 		"NS invokes SDP pTA (shall fail)\n");
792 	err = sdp_basic_test(TEST_NS_TO_PTA, test_size, test_loop, heap_name, ion_heap,
793 			     rnd_offset, verbosity);
794 	CHECK_RESULT(err, 1, return 1);
795 
796 	verbose("\nSecure Data Path basic access: "
797 		"Invoke TA with out of bounds buffer references\n");
798 	err = sdp_out_of_bounds_memref_test(test_size, heap_name, ion_heap, verbosity);
799 	CHECK_RESULT(err, 0, return 1);
800 
801 	return 0;
802 }
803