From 03193248a170ea03ddf4fae1b2ec23b08e278ac0 Mon Sep 17 00:00:00 2001 From: Luca Burelli Date: Tue, 1 Oct 2024 11:14:12 +0200 Subject: [PATCH] llext: fix handling of unimplemented syscalls When building an LLEXT-enabled kernel, 62b19ef65c0b added weak aliases of all syscall implementation functions to a pointer to NULL, with the assumption that LLEXT would check the required symbols at link time and fail if any of them were found. This check, however, is ineffective in the current implementation: the actual address that is exported is the rather normal-looking location of the variable containing the NULL pointer. This defeats the NULL symbol validity checks in llext_link.c and causes the extension to crash at runtime by jumping to a location containing a few zeroes in read-only data memory. This commit makes sure the alias target is actually placed at address 0 using the llext-sections.ld linker fragment, so that undefined syscall implementations are exported as NULLs and as such properly flagged at link time. The test for this functionality is also updated to reflect the change. Signed-off-by: Luca Burelli --- include/zephyr/linker/llext-sections.ld | 12 ++++++++++++ scripts/build/gen_syscalls.py | 15 ++++++++++----- tests/subsys/llext/simple/src/test_llext_simple.c | 11 +++++------ 3 files changed, 27 insertions(+), 11 deletions(-) diff --git a/include/zephyr/linker/llext-sections.ld b/include/zephyr/linker/llext-sections.ld index b8cc32e3d1bf1a..3dcfc3e8f0cd80 100644 --- a/include/zephyr/linker/llext-sections.ld +++ b/include/zephyr/linker/llext-sections.ld @@ -1,5 +1,17 @@ /* SPDX-License-Identifier: Apache-2.0 */ + /* + * Map the no_syscall_impl symbol in llext_export_syscalls.c to + * absolute address 0 so other weak symbols are exported as NULL. + * This section is used for mapping that symbol only and is not + * to be included in the final binary. + */ + + SECTION_PROLOGUE(llext_no_syscall_impl, 0 (COPY), ) + { + *(llext_no_syscall_impl) + } + /* * Special section used by LLEXT if CONFIG_LLEXT_EXPORT_BUILTINS_BY_SLID * is enabled. Declare this section to prevent it from being considered orphan. diff --git a/scripts/build/gen_syscalls.py b/scripts/build/gen_syscalls.py index 84f6b546bde588..44e0b793a124d9 100755 --- a/scripts/build/gen_syscalls.py +++ b/scripts/build/gen_syscalls.py @@ -159,11 +159,16 @@ exported_template = """ -/* Export syscalls for extensions */ -static void * const no_handler = NULL; +/* + * This symbol is placed at address 0 by llext-sections.ld. Its value and + * type is not important, we are only interested in its location + */ +static void * const no_syscall_impl Z_GENERIC_SECTION(llext_no_syscall_impl); -/* Weak references, if something is not found by the linker, it will be NULL - * and simply fail during extension load +/* + * Weak references to all syscall implementations. Those not found by the + * linker outside this file will be exported as NULL and simply fail when + * an extension requiring them is loaded. */ %s @@ -495,7 +500,7 @@ def main(): if args.syscall_export_llext: with open(args.syscall_export_llext, "w") as fp: # Export symbols for emitted syscalls - weak_refs = "\n".join("extern __weak ALIAS_OF(no_handler) void * const %s;" + weak_refs = "\n".join("extern __weak ALIAS_OF(no_syscall_impl) void * const %s;" % e for e in exported) exported_symbols = "\n".join("EXPORT_SYMBOL(%s);" % e for e in exported) diff --git a/tests/subsys/llext/simple/src/test_llext_simple.c b/tests/subsys/llext/simple/src/test_llext_simple.c index 577fda3969d718..48626f54bc0a95 100644 --- a/tests/subsys/llext/simple/src/test_llext_simple.c +++ b/tests/subsys/llext/simple/src/test_llext_simple.c @@ -490,17 +490,16 @@ ZTEST(llext, test_printk_exported) } /* - * Ensure ext_syscall_fail is exported - as it is picked up by the syscall - * build machinery - but points to NULL as it is not implemented. + * The syscalls test above verifies that custom syscalls defined by extensions + * are properly exported. Since `ext_syscalls.h` declares ext_syscall_fail, we + * know it is picked up by the syscall build machinery, but the implementation + * for it is missing. Make sure the exported symbol for it is NULL. */ ZTEST(llext, test_ext_syscall_fail) { const void * const esf_fn = LLEXT_FIND_BUILTIN_SYM(z_impl_ext_syscall_fail); - zassert_not_null(esf_fn, "est_fn should not be NULL"); - - zassert_is_null(*(uintptr_t **)esf_fn, NULL, - "ext_syscall_fail should be NULL"); + zassert_is_null(esf_fn, "est_fn should be NULL"); } ZTEST_SUITE(llext, NULL, NULL, NULL, NULL, NULL);