sched/signal: validate a sigevent where it is registered

A SIGEV_SIGNAL | SIGEV_THREAD_ID timer skipped timer_create's signal
check, and nxsig_notification honoured any thread ID. Add
nxsig_event_valid(): the signal must exist and a SIGEV_THREAD_ID target
must be one of the caller's own threads, as on Linux. timer_create,
gpio, button and phy_notify call it before storing an event, so the
caller gets EINVAL and an expiring timer never fails its DEBUGVERIFY.

Signed-off-by: Royyan Zahir <royzah@gmail.com>
This commit is contained in:
Royyan Zahir 2026-09-30 22:04:23 +04:00 • committed by Xiang Xiao
parent bcff4ae00d
commit 87998e3f61
6 changed files with 77 additions and 3 deletions

View file

@ -638,7 +638,7 @@ static int btn_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
FAR struct btn_notify_s *notify =
(FAR struct btn_notify_s *)((uintptr_t)arg);
if (notify)
if (notify && nxsig_event_valid(&notify->bn_event) == OK)
{
/* Save the notification events */

View file

@ -829,6 +829,13 @@ static int gpio_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
#if CONFIG_DEV_GPIO_NSIGNALS > 0
if (arg)
{
ret = nxsig_event_valid((FAR struct sigevent *)arg);
if (ret < 0)
{
leave_critical_section(flags);
break;
}
pid = nxsched_getpid();
for (i = 0; i < CONFIG_DEV_GPIO_NSIGNALS; i++)
{

View file

@ -290,6 +290,12 @@ int phy_notify_subscribe(FAR const char *intf, pid_t pid,
return phy_notify_unsubscribe(intf, pid);
}
ret = nxsig_event_valid(event);
if (ret < 0)
{
return ret;
}
/* Check if this client already exists */
client = phy_find_assigned(intf, pid);

View file

@ -691,6 +691,24 @@ unsigned int nxsig_sleep(unsigned int seconds);
int nxsig_usleep(useconds_t usec);
/****************************************************************************
* Name: nxsig_event_valid
*
* Description:
* Check a sigevent handed in by the calling task before it is stored:
* its signal must exist and a SIGEV_THREAD_ID target must be one of the
* caller's own threads.
*
* Input Parameters:
* event - The instance of struct sigevent to check.
*
* Returned Value:
* Zero (OK) if the event may be stored; -EINVAL if not.
*
****************************************************************************/
int nxsig_event_valid(FAR const struct sigevent *event);
/****************************************************************************
* Name: nxsig_notification
*

View file

@ -92,6 +92,50 @@ static void nxsig_notification_worker(FAR void *arg)
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: nxsig_event_valid
*
* Description:
* Check a sigevent handed in by the calling task before it is stored:
* its signal must exist and a SIGEV_THREAD_ID target must be one of the
* caller's own threads.
*
* Input Parameters:
* event - The instance of struct sigevent to check.
*
* Returned Value:
* Zero (OK) if the event may be stored; -EINVAL if not.
*
****************************************************************************/
int nxsig_event_valid(FAR const struct sigevent *event)
{
FAR struct tcb_s *target;
if ((event->sigev_notify & SIGEV_SIGNAL) == 0)
{
return OK;
}
if (!GOOD_SIGNO(event->sigev_signo))
{
return -EINVAL;
}
if ((event->sigev_notify & SIGEV_THREAD_ID) == 0)
{
return OK;
}
target = nxsched_get_tcb(event->sigev_notify_thread_id);
if (target == NULL || target->group != nxsched_self()->group)
{
return -EINVAL;
}
return OK;
}
/****************************************************************************
* Name: nxsig_notification
*

View file

@ -169,8 +169,7 @@ int timer_create(clockid_t clockid, FAR struct sigevent *evp,
if (timerid == NULL || (clockid != CLOCK_REALTIME &&
clockid != CLOCK_MONOTONIC && clockid != CLOCK_BOOTTIME) ||
(evp != NULL && evp->sigev_notify == SIGEV_SIGNAL &&
!GOOD_SIGNO(evp->sigev_signo)))
(evp != NULL && nxsig_event_valid(evp) < 0))
{
set_errno(EINVAL);
}