1 /*
2  * Copyright (c) 2024 Arduino SA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <string.h>
8 #include <zephyr/llext/llext.h>
9 #include <zephyr/llext/loader.h>
10 #include <zephyr/internal/syscall_handler.h>
11 #include <zephyr/kernel.h>
12 
13 #include <zephyr/logging/log.h>
14 LOG_MODULE_DECLARE(llext, CONFIG_LLEXT_LOG_LEVEL);
15 
16 #include "llext_priv.h"
17 
z_impl_llext_get_fn_table(struct llext * ext,bool is_init,void * buf,size_t buf_size)18 ssize_t z_impl_llext_get_fn_table(struct llext *ext, bool is_init, void *buf, size_t buf_size)
19 {
20 	size_t table_size;
21 
22 	if (!ext) {
23 		return -EINVAL;
24 	}
25 
26 	if (is_init) {
27 		table_size = ext->mem_size[LLEXT_MEM_PREINIT] +
28 			     ext->mem_size[LLEXT_MEM_INIT];
29 	} else {
30 		table_size = ext->mem_size[LLEXT_MEM_FINI];
31 	}
32 
33 	if (buf) {
34 		char *byte_ptr = buf;
35 
36 		if (buf_size < table_size) {
37 			return -ENOMEM;
38 		}
39 
40 		if (is_init) {
41 			/* setup functions from preinit_array and init_array */
42 			memcpy(byte_ptr,
43 			       ext->mem[LLEXT_MEM_PREINIT], ext->mem_size[LLEXT_MEM_PREINIT]);
44 			memcpy(byte_ptr + ext->mem_size[LLEXT_MEM_PREINIT],
45 			       ext->mem[LLEXT_MEM_INIT], ext->mem_size[LLEXT_MEM_INIT]);
46 		} else {
47 			/* cleanup functions from fini_array */
48 			memcpy(byte_ptr,
49 			       ext->mem[LLEXT_MEM_FINI], ext->mem_size[LLEXT_MEM_FINI]);
50 		}
51 
52 		/* Sanity check: pointers in this table must map inside the
53 		 * text region of the extension. If this fails, something went
54 		 * wrong during the relocation process.
55 		 * Using "char *" for these simplifies pointer arithmetic.
56 		 */
57 		const char *text_start = ext->mem[LLEXT_MEM_TEXT];
58 		const char *text_end = text_start + ext->mem_size[LLEXT_MEM_TEXT];
59 		const char **fn_ptrs = buf;
60 
61 		for (int i = 0; i < table_size / sizeof(void *); i++) {
62 			if (fn_ptrs[i] < text_start || fn_ptrs[i] >= text_end) {
63 				LOG_ERR("%s function %i (%p) outside text region",
64 					is_init ? "bringup" : "teardown",
65 					i, fn_ptrs[i]);
66 				return -EFAULT;
67 			}
68 		}
69 	}
70 
71 	return table_size;
72 }
73 
74 #ifdef CONFIG_USERSPACE
75 
ext_is_valid(struct llext * ext,void * arg)76 static int ext_is_valid(struct llext *ext, void *arg)
77 {
78 	return ext == arg;
79 }
80 
z_vrfy_llext_get_fn_table(struct llext * ext,bool is_init,void * buf,size_t size)81 static inline ssize_t z_vrfy_llext_get_fn_table(struct llext *ext, bool is_init,
82 						void *buf, size_t size)
83 {
84 	/* Test that ext matches a loaded extension */
85 	K_OOPS(llext_iterate(ext_is_valid, ext) == 0);
86 
87 	if (buf) {
88 		/* Test that buf is a valid user-accessible pointer */
89 		K_OOPS(K_SYSCALL_MEMORY_WRITE(buf, size));
90 	}
91 
92 	return z_impl_llext_get_fn_table(ext, is_init, buf, size);
93 }
94 #include <zephyr/syscalls/llext_get_fn_table_mrsh.c>
95 
96 #endif /* CONFIG_USERSPACE */
97