llext: protect the global llext list

Use an existing mutex to also protect the global llext list.

Signed-off-by: Guennadi Liakhovetski <guennadi.liakhovetski@linux.intel.com>
This commit is contained in:
Guennadi Liakhovetski 2023-11-20 15:45:13 +01:00 committed by Anas Nashif
commit 4e3e9a618e

View file

@ -23,6 +23,8 @@ static const char ELF_MAGIC[] = {0x7f, 'E', 'L', 'F'};
static sys_slist_t _llext_list = SYS_SLIST_STATIC_INIT(&_llext_list); static sys_slist_t _llext_list = SYS_SLIST_STATIC_INIT(&_llext_list);
static struct k_mutex llext_lock = Z_MUTEX_INITIALIZER(llext_lock);
ssize_t llext_find_section(struct llext_loader *ldr, const char *search_name) ssize_t llext_find_section(struct llext_loader *ldr, const char *search_name)
{ {
elf_shdr_t *shdr; elf_shdr_t *shdr;
@ -50,19 +52,28 @@ ssize_t llext_find_section(struct llext_loader *ldr, const char *search_name)
return -ENOENT; return -ENOENT;
} }
/*
* Note, that while we protect the global llext list while searching, we release
* the lock before returning the found extension to the caller. Therefore it's
* a responsibility of the caller to protect against races with a freeing
* context when calling this function.
*/
struct llext *llext_by_name(const char *name) struct llext *llext_by_name(const char *name)
{ {
sys_snode_t *node = sys_slist_peek_head(&_llext_list); k_mutex_lock(&llext_lock, K_FOREVER);
for (sys_snode_t *node = sys_slist_peek_head(&_llext_list);
node != NULL;
node = sys_slist_peek_next(node)) {
struct llext *ext = CONTAINER_OF(node, struct llext, _llext_list); struct llext *ext = CONTAINER_OF(node, struct llext, _llext_list);
while (node != NULL) {
if (strncmp(ext->name, name, sizeof(ext->name)) == 0) { if (strncmp(ext->name, name, sizeof(ext->name)) == 0) {
k_mutex_unlock(&llext_lock);
return ext; return ext;
} }
node = sys_slist_peek_next(node);
ext = CONTAINER_OF(node, struct llext, _llext_list);
} }
k_mutex_unlock(&llext_lock);
return NULL; return NULL;
} }
@ -72,6 +83,8 @@ int llext_iterate(int (*fn)(struct llext *ext, void *arg), void *arg)
unsigned int i; unsigned int i;
int ret = 0; int ret = 0;
k_mutex_lock(&llext_lock, K_FOREVER);
for (node = sys_slist_peek_head(&_llext_list), i = 0; for (node = sys_slist_peek_head(&_llext_list), i = 0;
node; node;
node = sys_slist_peek_next(node), i++) { node = sys_slist_peek_next(node), i++) {
@ -83,6 +96,7 @@ int llext_iterate(int (*fn)(struct llext *ext, void *arg), void *arg)
} }
} }
k_mutex_unlock(&llext_lock);
return ret; return ret;
} }
@ -873,8 +887,6 @@ out:
return ret; return ret;
} }
static struct k_mutex llext_lock = Z_MUTEX_INITIALIZER(llext_lock);
int llext_load(struct llext_loader *ldr, const char *name, struct llext **ext, int llext_load(struct llext_loader *ldr, const char *name, struct llext **ext,
struct llext_load_param *ldr_parm) struct llext_load_param *ldr_parm)
{ {
@ -886,28 +898,26 @@ int llext_load(struct llext_loader *ldr, const char *name, struct llext **ext,
if (*ext) { if (*ext) {
/* The use count is at least 1 */ /* The use count is at least 1 */
ret = (*ext)->use_count++; ret = (*ext)->use_count++;
k_mutex_unlock(&llext_lock); goto out;
return ret;
} }
k_mutex_unlock(&llext_lock);
ret = llext_seek(ldr, 0); ret = llext_seek(ldr, 0);
if (ret != 0) { if (ret != 0) {
LOG_ERR("Failed to seek for ELF header"); LOG_ERR("Failed to seek for ELF header");
return ret; goto out;
} }
ret = llext_read(ldr, &ehdr, sizeof(ehdr)); ret = llext_read(ldr, &ehdr, sizeof(ehdr));
if (ret != 0) { if (ret != 0) {
LOG_ERR("Failed to read ELF header"); LOG_ERR("Failed to read ELF header");
return ret; goto out;
} }
/* check whether this is an valid elf file */ /* check whether this is an valid elf file */
if (memcmp(ehdr.e_ident, ELF_MAGIC, sizeof(ELF_MAGIC)) != 0) { if (memcmp(ehdr.e_ident, ELF_MAGIC, sizeof(ELF_MAGIC)) != 0) {
LOG_HEXDUMP_ERR(ehdr.e_ident, 16, "Invalid ELF, magic does not match"); LOG_HEXDUMP_ERR(ehdr.e_ident, 16, "Invalid ELF, magic does not match");
return -EINVAL; ret = -EINVAL;
goto out;
} }
switch (ehdr.e_type) { switch (ehdr.e_type) {
@ -917,7 +927,8 @@ int llext_load(struct llext_loader *ldr, const char *name, struct llext **ext,
*ext = k_heap_alloc(&llext_heap, sizeof(struct llext), K_NO_WAIT); *ext = k_heap_alloc(&llext_heap, sizeof(struct llext), K_NO_WAIT);
if (*ext == NULL) { if (*ext == NULL) {
LOG_ERR("Not enough memory for extension metadata"); LOG_ERR("Not enough memory for extension metadata");
return -ENOMEM; ret = -ENOMEM;
goto out;
} }
memset(*ext, 0, sizeof(struct llext)); memset(*ext, 0, sizeof(struct llext));
@ -926,7 +937,7 @@ int llext_load(struct llext_loader *ldr, const char *name, struct llext **ext,
if (ret < 0) { if (ret < 0) {
k_heap_free(&llext_heap, *ext); k_heap_free(&llext_heap, *ext);
*ext = NULL; *ext = NULL;
return ret; goto out;
} }
strncpy((*ext)->name, name, sizeof((*ext)->name)); strncpy((*ext)->name, name, sizeof((*ext)->name));
@ -939,10 +950,12 @@ int llext_load(struct llext_loader *ldr, const char *name, struct llext **ext,
break; break;
default: default:
LOG_ERR("Unsupported elf file type %x", ehdr.e_type); LOG_ERR("Unsupported elf file type %x", ehdr.e_type);
return -EINVAL; ret = -EINVAL;
} }
return 0; out:
k_mutex_unlock(&llext_lock);
return ret;
} }
int llext_unload(struct llext **ext) int llext_unload(struct llext **ext)