Skip to content

Commit

Permalink
llext: fix handling of unimplemented syscalls
Browse files Browse the repository at this point in the history
When building an LLEXT-enabled kernel, 62b19ef 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 <[email protected]>
  • Loading branch information
pillo79 committed Oct 2, 2024
1 parent 9c0f92d commit 0319324
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 11 deletions.
12 changes: 12 additions & 0 deletions include/zephyr/linker/llext-sections.ld
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
15 changes: 10 additions & 5 deletions scripts/build/gen_syscalls.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
11 changes: 5 additions & 6 deletions tests/subsys/llext/simple/src/test_llext_simple.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);

0 comments on commit 0319324

Please sign in to comment.