mirror of
https://github.com/apache/nuttx.git
synced 2026-08-01 20:28:58 +00:00
A module's function pointer is the address of a two-word descriptor in its writable segment, not a code address. Firmware that accepts one, stores it, and later branches to it therefore jumps into the module's RAM data. There is no diagnostic: the board takes a HardFault with a dead console. qsort and bsearch have resolved the descriptor since FDPIC support landed. Nothing else did, and nothing noticed, because qsort's comparison function was the only callback anything exercised. That left every other entry point looking perfectly usable -- a module could call one, it would return success, and the fault arrived later from somewhere else. Resolution is opt-in per call site via fdpic_callback(), and this covers the rest of them: pthread_create, signal, task_create, pthread_once, task_spawn, scandir and sigaction. Resolution happens once, in the innermost routine the paths share, for the reason qsort already documents: resolving twice would treat a code address as a descriptor. task_create_with_stack resolves and task_create forwards to it; nxsig_action() resolves for both signal() and a direct sigaction(), on the local copy it already makes, so the caller's const struct is untouched and the SA_SIGINFO form is covered through the union. Two entry points need care beyond that pattern: signal() has to exclude the dispositions by hand. SIG_IGN, SIG_DFL, SIG_HOLD and SIG_ERR are the integers 0, 1, 2 and -1 rather than addresses, and fdpic_callback() declines to dereference NULL and nothing else -- handing it SIG_ERR would read through (void *)-1. scandir takes two pointers. Its filter is called by scandir itself and is resolved here; its comparison function is handed to qsort(), whose own entry point resolves it, so resolving it here as well would resolve twice. The test passes both at once, which is what exercises that distinction. Resolving the code address is sufficient at all of these. A thread or task created from a module inherits its D-Space -- nxtask_dup_dspace() runs before up_initial_state() installs it in the FDPIC register -- and a signal handler or pthread_once init routine runs in a context that already holds the module's data base. Verified on an RP2350 with a module that hands its own function to each entry point. Each assertion was also checked against a deliberate breakage: removing the scandir filter resolution, and adding a second resolution of the comparison function, each take the board down with a HardFault rather than printing a FAIL line -- which is how a broken callback resolution manifests on Cortex-M, and confirms the assertions test what they name. mq_notify and timer_create with SIGEV_THREAD remain, and are harder: their callback runs on a work-queue worker that carries no module data base at all. Assisted-by: Claude Code:claude-opus-5 Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
111 lines
3.8 KiB
C
111 lines
3.8 KiB
C
/****************************************************************************
|
|
* libs/libc/pthread/pthread_create.c
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed with
|
|
* this work for additional information regarding copyright ownership. The
|
|
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
|
* "License"); you may not use this file except in compliance with the
|
|
* License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
* License for the specific language governing permissions and limitations
|
|
* under the License.
|
|
*
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Included Files
|
|
****************************************************************************/
|
|
|
|
#include <nuttx/config.h>
|
|
|
|
#include <assert.h>
|
|
|
|
#include <nuttx/pthread.h>
|
|
|
|
#ifdef CONFIG_FDPIC
|
|
# include <nuttx/binfmt/fdpic.h>
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: pthread_startup
|
|
*
|
|
* Description:
|
|
* This function is the user space pthread startup function. Its purpose
|
|
* is to catch the return from the pthread main function so that
|
|
* pthread_exit() can be called from user space
|
|
*
|
|
* Input Parameters:
|
|
* entry - The user-space address of the pthread entry point
|
|
* arg - Standard argument for the pthread entry point
|
|
*
|
|
* Returned Value:
|
|
* None. This function does not return.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void pthread_startup(pthread_startroutine_t entry,
|
|
pthread_addr_t arg)
|
|
{
|
|
DEBUGASSERT(entry != NULL);
|
|
|
|
/* Pass control to the thread entry point. Handle any returned value. */
|
|
|
|
pthread_exit(entry(arg));
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: pthread_create
|
|
*
|
|
* Description:
|
|
* This function creates and activates a new thread with specified
|
|
* attributes. It is simply a wrapper around the nx_pthread_create system
|
|
* call.
|
|
*
|
|
* Input Parameters:
|
|
* thread
|
|
* attr
|
|
* pthread_entry
|
|
* arg
|
|
*
|
|
* Returned Value:
|
|
* OK (0) on success; a (non-negated) errno value on failure. The errno
|
|
* variable is not set.
|
|
*
|
|
****************************************************************************/
|
|
|
|
int pthread_create(FAR pthread_t *thread, FAR const pthread_attr_t *attr,
|
|
pthread_startroutine_t pthread_entry, pthread_addr_t arg)
|
|
{
|
|
#ifdef CONFIG_FDPIC
|
|
/* An FDPIC module passes the address of a function descriptor, not a code
|
|
* address. Resolve it here, once, in the public entry point.
|
|
*
|
|
* The new thread inherits the creator's D-Space -- nxtask_dup_dspace()
|
|
* runs before up_initial_state() installs it in the FDPIC register -- so
|
|
* it starts with the module's own data base already in place, and needs
|
|
* only the code address.
|
|
*/
|
|
|
|
pthread_entry = (pthread_startroutine_t)
|
|
fdpic_callback((FAR void *)pthread_entry);
|
|
#endif
|
|
|
|
return nx_pthread_create(pthread_startup, thread, attr, pthread_entry,
|
|
arg);
|
|
}
|