Skip to content

sched/signal: validate a sigevent's signal and target thread - #20424

Open
royzah wants to merge 1 commit into
apache:masterfrom
royzah:sigevent
Open

royzah wants to merge 1 commit into
apache:masterfrom
royzah:sigevent

Conversation

@royzah

@royzah royzah commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Note: Please adhere to Contributing Guidelines.

Summary

timer_create() checked the signal number only when sigev_notify was exactly SIGEV_SIGNAL, so SIGEV_SIGNAL | SIGEV_THREAD_ID let any number through to dispatch, and an expiring timer then failed its DEBUGVERIFY. nxsig_notification() also honoured any thread ID, letting a process aim a timer, message queue or driver notification at a thread of another process.

File Does
sig_notification.c refuses a bad signal number; a SIGEV_THREAD_ID target must be in the owner's process, as on Linux
timer_create.c refuses both up front with EINVAL

Impact

Every build. A sigevent with a bad signal number, or aimed at another process's thread, is refused with EINVAL.

Testing

Host: Ubuntu 24.04, x86_64. Builds and QEMU runs in ghcr.io/apache/nuttx/apache-nuttx-ci-linux (QEMU 6.2, Arm GNU GCC 13.2, xPack RISC-V GCC 14.3). Hardware: i.MX93 (Cortex-A55), PX4 kernel build.

Where Result
qemu-armv8a:knsh ostest passes, its timer tests included; hello
rv-virt:knsh64 hello; ostest stops after Started user_main at PID=6, as master does
sim:ostest, qemu-armv8a:nsh build
i.MX93, PX4 kernel build timer_create() with SIGEV_THREAD_ID at another process's thread: EINVAL
qemu-armv8a:knsh: hello, then ostest
- Ready to Boot Primary CPU
- Boot from EL2
- Boot from EL1
- Boot to C runtime for OS Initialize

NuttShell (NSH)
nsh> /system/bin/hello
Hello, World!!
nsh> ostest
stdio_test: write fd=1
stdio_test: Standard I/O Check: printf
stdio_test: write fd=2
stdio_test: Standard I/O Check: fprintf to stderr
ostest_main: putenv(Variable1=BadValue3)
ostest_main: setenv(Variable1, GoodValue1, TRUE)
ostest_main: setenv(Variable2, BadValue1, FALSE)
ostest_main: setenv(Variable2, GoodValue2, TRUE)
ostest_main: setenv(Variable3, GoodValue3, FALSE)
ostest_main: setenv(Variable3, BadValue2, FALSE)
show_variable: Variable=Variable1 has value=GoodValue1
show_variable: Variable=Variable2 has value=GoodValue2
show_variable: Variable=Variable3 has value=GoodValue3
ostest_main: Started user_main at PID=7

user_main: vfork() test
vfork_test: Started
vfork_test: Child 8 ran and exited before the parent resumed

End of test memory usage:
VARIABLE  BEFORE   AFTER
======== ======== ========
arena        a000     c000
ordblks         2        3
mxordblk     5ff8     3ff8
uordblks     27d8     47e0
fordblks     7828     7820

user_main: Begin argument test
user_main: Started with argc=5
user_main: argv[0]="user_main"
user_main: argv[1]="Arg1"
user_main: argv[2]="Arg2"
user_main: argv[3]="Arg3"
user_main: argv[4]="Arg4"

End of test memory usage:
VARIABLE  BEFORE   AFTER
======== ======== ========
arena        c000     c000
ordblks         3        3
mxordblk     3ff8     3ff8
uordblks     47e0     47e0
fordblks     7820     7820

user_main: getopt() test
getopt():  Simple test
getopt():  Invalid argument
getopt():  Missing optional argument
getopt_long():  Simple test
getopt_long():  No short options
getopt_long():  Argument for --option=argument
getopt_long():  Invalid long option
getopt_long():  Mixed long and short options
getopt_long():  Invalid short option
getopt_long():  Missing optional arguments
getopt_long_only():  Mixed long and short options
getopt_long_only():  Single hyphen long options

End of test memory usage:
VARIABLE  BEFORE   AFTER
======== ======== ========
arena        c000     c000
ordblks         3        3
mxordblk     3ff8     3ff8
uordblks     47e0     47e0
fordblks     7820     7820

user_main: libc tests

End of test memory usage:
VARIABLE  BEFORE   AFTER
======== ======== ========
arena        c000     c000
ordblks         3        3
mxordblk     3ff8     3ff8
uordblks     47e0     47e0
fordblks     7820     7820
show_variable: Variable=Variable1 has value=GoodValue1
show_variable: Variable=Variable2 has value=GoodValue2
show_variable: Variable=Variable3 has value=GoodValue3
show_variable: Variable=Variable1 has no value
show_variable: Variable=Variable2 has value=GoodValue2
show_variable: Variable=Variable3 has value=GoodValue3

End of test memory usage:
VARIABLE  BEFORE   AFTER
======== ======== ========
arena        c000     c000
ordblks         3        4
mxordblk     3ff8     3ff8
uordblks     47e0     47c0
fordblks     7820     7840
show_variable: Variable=Variable1 has no value
show_variable: Variable=Variable2 has no value
show_variable: Variable=Variable3 has no value

End of test memory usage:
VARIABLE  BEFORE   AFTER
======== ======== ========
arena        c000     c000
ordblks         4        3
mxordblk     3ff8     3ff8
uordblks     47c0     46e8
fordblks     7840     7918

user_main: setvbuf test
setvbuf_test: Test NO buffering
setvbuf_test: Using NO buffering

setvbuf_test: Test default FULL buffering
setvbuf_test: Using default FULL buffering

setvbuf_test: Test FULL buffering, buffer size 64
setvbuf_test: Using FULL buffering, buffer size 64

setvbuf_test: Test FULL buffering, pre-allocated buffer
setvbuf_test: Using FULL buffering, pre-allocated buffer

setvbuf_test: Test LINE buffering, buffer size 64
setvbuf_test: Using LINE buffering, buffer size 64

setvbuf_test: Test FULL buffering, pre-allocated buffer
setvbuf_test: Using FULL buffering, pre-allocated buffer

End of test memory usage:
VARIABLE  BEFORE   AFTER
======== ======== ========
arena        c000     c000
ordblks         3        3
mxordblk     3ff8     3ff8
uordblks     46e8     46e8
fordblks     7918     7918

user_main: /dev/null test
dev_null: Read 0 bytes from /dev/null
dev_null: Wrote 1024 bytes to /dev/null

End of test memory usage:
VARIABLE  BEFORE   AFTER
======== ======== ========
arena        c000     c000
ordblks         3        3
mxordblk     3ff8     3ff8
uordblks     46e8     46e8
fordblks     7918     7918

user_main: mutex test
Initializing mutex
Starting thread 1
Starting thread 2
		Thread1	Thread2
	Loops	32	32
	Errors	0	0

Testing moved mutex
Starting moved mutex thread 1
Starting moved mutex thread 2
		Thread1	Thread2
	Moved Loops	32	32
	Moved Errors	0	0

End of test memory usage:
VARIABLE  BEFORE   AFTER
======== ======== ========
arena        c000    10000
ordblks         3        4
mxordblk     3ff8     3ff8
uordblks     46e8     66f0
fordblks     7918     9910

user_main: timed mutex test
mutex_test: Initializing mutex
mutex_test: Starting thread
pthread:  Started
pthread:  Waiting for lock or timeout
mutex_test: Unlocking
pthread:  Got the lock
pthread:  Waiting for lock or timeout
pthread:  Got the timeout.  Terminating
mutex_test: PASSED
timedmutex regression test: PASSED

End of test memory usage:
VARIABLE  BEFORE   AFTER
======== ======== ========
arena       10000    10000
ordblks         4        4
mxordblk     3ff8     3ff8
uordblks     66f0     66f0
fordblks     9910     9910

user_main: cancel test
cancel_test: Test 1a: Normal Cancellation
cancel_test: Starting thread
start_thread: Initializing mutex
start_thread: Initializing cond
start_thread: Starting thread
start_thread: Yielding
sem_waiter: Taking mutex
sem_waiter: Starting wait for condition
cancel_test: Canceling thread
cancel_test: Joining
cancel_test: waiter exited with result=0xffffffffffffffff
cancel_test: PASS thread terminated with PTHREAD_CANCELED
cancel_test: Test 2: Asynchronous Cancellation
... Skipped
cancel_test: Test 3: Cancellation of detached thread
cancel_test: Re-starting thread
restart_thread: Destroying cond
restart_thread: Destroying mutex
restart_thread: Re-starting thread
start_thread: Initializing mutex
start_thread: Initializing cond
start_thread: Starting thread
start_thread: Yielding
sem_waiter: Taking mutex
sem_waiter: Starting wait for condition
cancel_test: Canceling thread
cancel_test: Joining
cancel_test: PASS pthread_join failed with status=ESRCH
cancel_test: Test 5: Non-cancelable threads
cancel_test: Re-starting thread (non-cancelable)
restart_thread: Destroying cond
restart_thread: Destroying mutex
restart_thread: Re-starting thread
start_thread: Initializing mutex
start_thread: Initializing cond
start_thread: Starting thread
start_thread: Yielding
sem_waiter: Taking mutex
sem_waiter: Starting wait for condition
sem_waiter: Setting non-cancelable
cancel_test: Canceling thread
cancel_test: Joining
sem_waiter: Releasing mutex
sem_waiter: Setting cancelable
cancel_test: waiter exited with result=0xffffffffffffffff
cancel_test: PASS thread terminated with PTHREAD_CANCELED
cancel_test: Test 6: Cancel message queue wait
cancel_test: Starting thread (cancelable)
Skipped
cancel_test: Test 7: Cancel signal wait
cancel_test: Starting thread (cancelable)
Skipped

End of test memory usage:
VARIABLE  BEFORE   AFTER
======== ======== ========
arena       10000    10000
ordblks         4        3
mxordblk     3ff8     7ff8
uordblks     66f0     46e8
fordblks     9910     b918

user_main: robust test
robust_test: Initializing mutex
robust_test: Starting thread
robust_waiter: Taking mutex
robust_waiter: Exiting with mutex
robust_test: Take the lock again
robust_test: Make the mutex consistent again.
robust_test: Take the lock again
robust_test: Joining
robust_test: waiter exited with result=0
robust_test: Test complete with nerrors=0

End of test memory usage:
VARIABLE  BEFORE   AFTER
======== ======== ========
arena       10000    10000
ordblks         3        3
mxordblk     7ff8     7ff8
uordblks     46e8     46e8
fordblks     b918     b918

user_main: semaphore test
sem_test: Initializing semaphore to 0
sem_test: Starting waiter thread 1
sem_test: Set thread 1 priority to 191
waiter_func: Thread 1 Started
waiter_func: Thread 1 initial semaphore value = 0
waiter_func: Thread 1 waiting on semaphore
sem_test: Starting waiter thread 2
sem_test: Set thread 2 priority to 128
waiter_func: Thread 2 Started
waiter_func: Thread 2 initial semaphore value = -1
waiter_func: Thread 2 waiting on semaphore
sem_test: Starting poster thread 3
sem_test: Set thread 3 priority to 64
poster_func: Thread 3 started
poster_func: Thread 3 semaphore value = -2
poster_func: Thread 3 posting semaphore
waiter_func: Thread 1 awakened
waiter_func: Thread 1 new semaphore value = -1
waiter_func: Thread 1 done
poster_func: Thread 3 new semaphore value = -1
poster_func: Thread 3 semaphore value = -1
poster_func: Thread 3 posting semaphore
waiter_func: Thread 2 awakened
waiter_func: Thread 2 new semaphore value = 0
waiter_func: Thread 2 done
poster_func: Thread 3 new semaphore value = 0
poster_func: Thread 3 done

End of test memory usage:
VARIABLE  BEFORE   AFTER
======== ======== ========
arena       10000    14000
ordblks         3        5
mxordblk     7ff8     3ff8
uordblks     46e8     86f8
fordblks     b918     b908

user_main: timed semaphore test
semtimed_test: Initializing semaphore to 0
semtimed_test: Waiting for two second timeout
semtimed_test: PASS: first test returned timeout
BEFORE: (1646092835 sec, 452135344 nsec)
AFTER:  (1646092837 sec, 452908704 nsec)
semtimed_test: Starting poster thread
semtimed_test: Set thread 1 priority to 191
semtimed_test: Starting poster thread 3
semtimed_test: Set thread 3 priority to 64
semtimed_test: Waiting for two second timeout
poster_func: Waiting for 1 second
poster_func: Posting
semtimed_test: PASS: sem_timedwait succeeded
BEFORE: (1646092837 sec, 453214576 nsec)
AFTER:  (1646092838 sec, 453929296 nsec)

End of test memory usage:
VARIABLE  BEFORE   AFTER
======== ======== ========
arena       14000    14000
ordblks         5        3
mxordblk     3ff8     bff8
uordblks     86f8     46e8
fordblks     b908     f918

user_main: condition variable test
cond_test: Initializing mutex
cond_test: Initializing cond
cond_test: Starting waiter
cond_test: Set thread 1 priority to 128
waiter_thread: Started
cond_test: Starting signaler
cond_test: Set thread 2 priority to 64
thread_signaler: Started
thread_signaler: Terminating
cond_test: signaler terminated, now cancel the waiter
cond_test: 	Waiter	Signaler
cond_test: Loops	32	32
cond_test: Errors	0	0
cond_test:
cond_test: 0 times, waiter did not have to wait for data
cond_test: 0 times, data was already available when the signaler run
cond_test: 0 times, the waiter was in an unexpected state when the signaler ran

End of test memory usage:
VARIABLE  BEFORE   AFTER
======== ======== ========
arena       14000    14000
ordblks         3        3
mxordblk     bff8     7ff8
uordblks     46e8     46e8
fordblks     f918     f918

user_main: pthread_exit() test
pthread_exit_test: Started pthread_exit_main at PID=37
pthread_exit_main 37: Starting pthread_exit_thread
pthread_exit_main 37: Sleeping for 5 seconds
pthread_exit_thread 40: Sleeping for 10 second
pthread_exit_main 37: Calling pthread_exit()
pthread_exit_thread 40: Still running...

End of test memory usage:
VARIABLE  BEFORE   AFTER
======== ======== ========
arena       14000    14000
ordblks         3        4
mxordblk     7ff8     7ff8
uordblks     46e8     66f0
fordblks     f918     d910

user_main: pthread_rwlock test
pthread_rwlock: Initializing rwlock
pthread_exit_thread 40: Exiting

End of test memory usage:
VARIABLE  BEFORE   AFTER
======== ======== ========
arena       14000    18000
ordblks         4        5
mxordblk     7ff8     5ff8
uordblks     66f0     86f8
fordblks     d910     f908

user_main: pthread_rwlock_cancel test
pthread_rwlock_cancel: Starting test

End of test memory usage:
VARIABLE  BEFORE   AFTER
======== ======== ========
arena       18000    18000
ordblks         5        2
mxordblk     5ff8    13ff8
uordblks     86f8     26e0
fordblks     f908    15920

user_main: timed wait test
thread_waiter: Initializing mutex
timedwait_test: Initializing cond
timedwait_test: Starting waiter
timedwait_test: Set thread 2 priority to 177
thread_waiter: Taking mutex
thread_waiter: Starting 5 second wait for condition
timedwait_test: Joining
thread_waiter: pthread_cond_timedwait timed out
thread_waiter: Releasing mutex
thread_waiter: Exit with status 0x12345678
timedwait_test: waiter exited with result=0x12345678

End of test memory usage:
VARIABLE  BEFORE   AFTER
======== ======== ========
arena       18000    18000
ordblks         2        3
mxordblk    13ff8     fff8
uordblks     26e0     46e8
fordblks    15920    13918

user_main: timed message queue test
timedmqueue_test: Starting sender
timedmqueue_test: Waiting for sender to complete
sender_thread: Starting
sender_thread: mq_timedsend succeeded on msg 0
sender_thread: mq_timedsend succeeded on msg 1
sender_thread: mq_timedsend succeeded on msg 2
sender_thread: mq_timedsend succeeded on msg 3
sender_thread: mq_timedsend succeeded on msg 4
sender_thread: mq_timedsend succeeded on msg 5
sender_thread: mq_timedsend succeeded on msg 6
sender_thread: mq_timedsend succeeded on msg 7
sender_thread: mq_timedsend succeeded on msg 8
sender_thread: mq_timedsend 9 timed out as expected
sender_thread: returning nerrors=0
timedmqueue_test: Starting receiver
timedmqueue_test: Waiting for receiver to complete
receiver_thread: Starting
receiver_thread: mq_timedreceive succeed on msg 0
receiver_thread: mq_timedreceive succeed on msg 1
receiver_thread: mq_timedreceive succeed on msg 2
receiver_thread: mq_timedreceive succeed on msg 3
receiver_thread: mq_timedreceive succeed on msg 4
receiver_thread: mq_timedreceive succeed on msg 5
receiver_thread: mq_timedreceive succeed on msg 6
receiver_thread: mq_timedreceive succeed on msg 7
receiver_thread: mq_timedreceive succeed on msg 8
receiver_thread: Receive 9 timed out as expected
receiver_thread: returning nerrors=0
timedmqueue_test: Test complete

End of test memory usage:
VARIABLE  BEFORE   AFTER
======== ======== ========
arena       18000    18000
ordblks         3        3
mxordblk     fff8     fff8
uordblks     46e8     46e8
fordblks    13918    13918

user_main: sigprocmask test
sigprocmask_test: SUCCESS

End of test memory usage:
VARIABLE  BEFORE   AFTER
======== ======== ========
arena       18000    18000
ordblks         3        3
mxordblk     fff8     fff8
uordblks     46e8     46e8
fordblks    13918    13918

user_main: message queue test
mqueue_test: Starting receiver
mqueue_test: Set receiver priority to 128
receiver_thread: Starting
mqueue_test: Starting sender
mqueue_test: Set sender thread priority to 64
mqueue_test: Waiting for sender to complete
sender_thread: Starting
receiver_thread: mq_receive succeeded on msg 0
sender_thread: mq_send succeeded on msg 0
receiver_thread: mq_receive succeeded on msg 1
sender_thread: mq_send succeeded on msg 1
receiver_thread: mq_receive succeeded on msg 2
sender_thread: mq_send succeeded on msg 2
receiver_thread: mq_receive succeeded on msg 3
sender_thread: mq_send succeeded on msg 3
receiver_thread: mq_receive succeeded on msg 4
sender_thread: mq_send succeeded on msg 4
receiver_thread: mq_receive succeeded on msg 5
sender_thread: mq_send succeeded on msg 5
receiver_thread: mq_receive succeeded on msg 6
sender_thread: mq_send succeeded on msg 6
receiver_thread: mq_receive succeeded on msg 7
sender_thread: mq_send succeeded on msg 7
receiver_thread: mq_receive succeeded on msg 8
sender_thread: mq_send succeeded on msg 8
receiver_thread: mq_receive succeeded on msg 9
sender_thread: mq_send succeeded on msg 9
sender_thread: returning nerrors=0
mqueue_test: Killing receiver
receiver_thread: mq_receive interrupted!
receiver_thread: returning nerrors=0
mqueue_test: Canceling receiver
mqueue_test: receiver has already terminated

End of test memory usage:
VARIABLE  BEFORE   AFTER
======== ======== ========
arena       18000    18000
ordblks         3        4
mxordblk     fff8     bff8
uordblks     46e8     66f0
fordblks    13918    11910

user_main: signal handler test
sighand_test: Initializing semaphore to 0
sighand_test: Unmasking SIGCHLD
sighand_test: Registering SIGCHLD handler
sighand_test: Starting waiter task
sighand_test: Started waiter_main pid=58
waiter_main: Waiter started
waiter_main: Unmasking signal 32
waiter_main: Registering signal handler
waiter_main: oact.sigaction=0 oact.sa_flags=0 oact.sa_mask=0000000000000000
waiter_main: Waiting on semaphore
sighand_test: Signaling pid=58 with signo=32 sigvalue=42
waiter_main: sem_wait() successfully interrupted by signal
waiter_main: done
sighand_test: done

End of test memory usage:
VARIABLE  BEFORE   AFTER
======== ======== ========
arena       18000    18000
ordblks         4        3
mxordblk     bff8     fff8
uordblks     66f0     46e8
fordblks    11910    13918

user_main: nested signal handler test
signest_test: Starting signal waiter task at priority 101
waiter_main: Waiter started
waiter_main: Setting signal mask
waiter_main: Registering signal handler
waiter_main: Waiting on semaphore
signest_test: Started waiter_main pid=59
signest_test: Starting interfering task at priority 102
interfere_main: Waiting on semaphore
signest_test: Started interfere_main pid=60
signest_test: Simple case:
  Total signalled 1180  Odd=580 Even=600
  Total handled   1180  Odd=580 Even=600
  Total nested    0    Odd=0   Even=0  
signest_test: With task locking
  Total signalled 2360  Odd=1160 Even=1200
  Total handled   2360  Odd=1160 Even=1200
  Total nested    0    Odd=0   Even=0  
signest_test: With interfering thread
  Total signalled 3540  Odd=1740 Even=1800
  Total handled   3540  Odd=1740 Even=1800
  Total nested    0    Odd=0   Even=0  
signest_test: done

End of test memory usage:
VARIABLE  BEFORE   AFTER
======== ======== ========
arena       18000    18000
ordblks         3        4
mxordblk     fff8     bff8
uordblks     46e8     66f0
fordblks    13918    11910

user_main: POSIX timer test
timer_test: Masking signal 32
timer_test: Creating timer
timer_test: Starting timer
timer_test: Waiting on sigwaitinfo
timer_test: sigwaitinfo() returned signo=32
timer_expiration: sival_int=42
timer_expiration: si_code=2 (SI_TIMER)
timer_expiration: g_nsigreceived=1
timer_test: Waiting on sigwaitinfo
timer_test: sigwaitinfo() returned signo=32
timer_expiration: sival_int=42
timer_expiration: si_code=2 (SI_TIMER)
timer_expiration: g_nsigreceived=2
timer_test: Waiting on sigwaitinfo
timer_test: sigwaitinfo() returned signo=32
timer_expiration: sival_int=42
timer_expiration: si_code=2 (SI_TIMER)
timer_expiration: g_nsigreceived=3
timer_test: Waiting on sigwaitinfo
timer_test: sigwaitinfo() returned signo=32
timer_expiration: sival_int=42
timer_expiration: si_code=2 (SI_TIMER)
timer_expiration: g_nsigreceived=4
timer_test: Waiting on sigwaitinfo
timer_test: sigwaitinfo() returned signo=32
timer_expiration: sival_int=42
timer_expiration: si_code=2 (SI_TIMER)
timer_expiration: g_nsigreceived=5
timer_test: Deleting timer
timer_test: done

End of test memory usage:
VARIABLE  BEFORE   AFTER
======== ======== ========
arena       18000    18000
ordblks         4        4
mxordblk     bff8     bff8
uordblks     66f0     66f0
fordblks    11910    11910

user_main: round-robin scheduler test
rr_test: Set thread priority to 1
rr_test: Set thread policy to SCHED_RR
rr_test: Starting first get_primes_thread
         First get_primes_thread: 61
rr_test: Starting second get_primes_thread
         Second get_primes_thread: 62
rr_test: Waiting for threads to complete -- this should take awhile
         If RR scheduling is working, they should start and complete at
         about the same time
get_primes_thread id=1 started, looking for primes < 30000, doing 10 run(s)
get_primes_thread id=2 started, looking for primes < 30000, doing 10 run(s)
get_primes_thread id=1 finished, found 3246 primes, last one was 29989
get_primes_thread id=2 finished, found 3246 primes, last one was 29989
rr_test: Done

End of test memory usage:
VARIABLE  BEFORE   AFTER
======== ======== ========
arena       18000    18000
ordblks         4        4
mxordblk     bff8     bff8
uordblks     66f0     66f0
fordblks    11910    11910

user_main: barrier test
barrier_test: Initializing barrier
barrier_test: Thread 0 created
barrier_test: Thread 1 created
barrier_test: Thread 2 created
barrier_test: Thread 3 created
barrier_test: Thread 4 created
barrier_test: Thread 5 created
barrier_test: Thread 6 created
barrier_test: Thread 7 created
barrier_func: Thread 0 started
barrier_func: Thread 1 started
barrier_func: Thread 2 started
barrier_func: Thread 3 started
barrier_func: Thread 4 started
barrier_func: Thread 5 started
barrier_func: Thread 6 started
barrier_func: Thread 7 started
barrier_func: Thread 0 calling pthread_barrier_wait()
barrier_func: Thread 1 calling pthread_barrier_wait()
barrier_func: Thread 2 calling pthread_barrier_wait()
barrier_func: Thread 3 calling pthread_barrier_wait()
barrier_func: Thread 4 calling pthread_barrier_wait()
barrier_func: Thread 5 calling pthread_barrier_wait()
barrier_func: Thread 6 calling pthread_barrier_wait()
barrier_func: Thread 7 calling pthread_barrier_wait()
barrier_func: Thread 7, back with status=PTHREAD_BARRIER_SERIAL_THREAD (I AM SPECIAL)
barrier_func: Thread 0, back with status=0 (I am not special)
barrier_func: Thread 1, back with status=0 (I am not special)
barrier_func: Thread 2, back with status=0 (I am not special)
barrier_func: Thread 3, back with status=0 (I am not special)
barrier_func: Thread 4, back with status=0 (I am not special)
barrier_func: Thread 5, back with status=0 (I am not special)
barrier_func: Thread 6, back with status=0 (I am not special)
barrier_func: Thread 7 done
barrier_func: Thread 0 done
barrier_func: Thread 1 done
barrier_func: Thread 2 done
barrier_func: Thread 3 done
barrier_func: Thread 4 done
barrier_func: Thread 5 done
barrier_func: Thread 6 done
barrier_test: Thread 0 completed with result=0
barrier_test: Thread 1 completed with result=0
barrier_test: Thread 2 completed with result=0
barrier_test: Thread 3 completed with result=0
barrier_test: Thread 4 completed with result=0
barrier_test: Thread 5 completed with result=0
barrier_test: Thread 6 completed with result=0
barrier_test: Thread 7 completed with result=0

End of test memory usage:
VARIABLE  BEFORE   AFTER
======== ======== ========
arena       18000    28000
ordblks         4       10
mxordblk     bff8     3ff8
uordblks     66f0    12720
fordblks    11910    158e0

user_main: scheduler lock test
sched_lock: Starting lowpri_thread at 97
sched_lock: Set lowpri_thread priority to 97
sched_lock: Starting highpri_thread at 98
sched_lock: Set highpri_thread priority to 98
sched_lock: Waiting...
sched_lock: PASSED No pre-emption occurred while scheduler was locked.
sched_lock: Starting lowpri_thread at 97
sched_lock: Set lowpri_thread priority to 97
sched_lock: Starting highpri_thread at 98
sched_lock: Set highpri_thread priority to 98
sched_lock: Waiting...
sched_lock: PASSED No pre-emption occurred while scheduler was locked.
sched_lock: Finished

End of test memory usage:
VARIABLE  BEFORE   AFTER
======== ======== ========
arena       28000    28000
ordblks        10        4
mxordblk     3ff8    1bff8
uordblks    12720     66f0
fordblks    158e0    21910

Final memory usage:
VARIABLE  BEFORE   AFTER
======== ======== ========
arena        a000    28000
ordblks         2        4
mxordblk     5ff8    1bff8
uordblks     27d8     66f0
fordblks     7828    21910
user_main: Exiting
ostest_main: Exiting with status 0
nsh> qemu-system-aarch64: terminating on signal 15 from pid 1 (timeout)
rv-virt:knsh64: hello, then ostest; identical to master's
OpenSBI v0.9
   ____                    _____ ____ _____
  / __ \                  / ____|  _ \_   _|
 | |  | |_ __   ___ _ __ | (___ | |_) || |
 | |  | | '_ \ / _ \ '_ \ \___ \|  _ < | |
 | |__| | |_) |  __/ | | |____) | |_) || |_
  \____/| .__/ \___|_| |_|_____/|____/_____|
        | |
        |_|

Platform Name             : riscv-virtio,qemu
Platform Features         : timer,mfdeleg
Platform HART Count       : 1
Firmware Base             : 0x80000000
Firmware Size             : 100 KB
Runtime SBI Version       : 0.2

Domain0 Name              : root
Domain0 Boot HART         : 0
Domain0 HARTs             : 0*
Domain0 Region00          : 0x0000000080000000-0x000000008001ffff ()
Domain0 Region01          : 0x0000000000000000-0xffffffffffffffff (R,W,X)
Domain0 Next Address      : 0x0000000080200000
Domain0 Next Arg1         : 0x0000000087000000
Domain0 Next Mode         : S-mode
Domain0 SysReset          : yes

Boot HART ID              : 0
Boot HART Domain          : root
Boot HART ISA             : rv64imafdcsu
Boot HART Features        : scounteren,mcounteren,time
Boot HART PMP Count       : 16
Boot HART PMP Granularity : 4
Boot HART PMP Address Bits: 54
Boot HART MHPM Count      : 0
Boot HART MHPM Count      : 0
Boot HART MIDELEG         : 0x0000000000000222
Boot HART MEDELEG         : 0x000000000000b109
ABC
NuttShell (NSH)
nsh> /system/bin/hello
Hello, World!!
nsh> ostest
stdio_test: write fd=1
stdio_test: Standard I/O Check: printf
stdio_test: write fd=2
stdio_test: Standard I/O Check: fprintf to stderr
ostest_main: putenv(Variable1=BadValue3)
ostest_main: setenv(Variable1, GoodValue1, TRUE)
ostest_main: setenv(Variable2, BadValue1, FALSE)
ostest_main: setenv(Variable2, GoodValue2, TRUE)
ostest_main: setenv(Variable3, GoodValue3, FALSE)
ostest_main: setenv(Variable3, BadValue2, FALSE)
show_variable: Variable=Variable1 has value=GoodValue1
show_variable: Variable=Variable2 has value=GoodValue2
show_variable: Variable=Variable3 has value=GoodValue3
ostest_main: Started user_main at PID=6
qemu-system-riscv64: terminating on signal 15 from pid 1 (timeout)
i.MX93, PX4 kernel build, this series applied: tests isolation
INFO  [tests] RUNNING TEST: test_capabilities_enforced
INFO  [tests] isolation PASSED
INFO  [tests] without capabilities, got through: 0x0
INFO  [tests] TEST PASSED: test_capabilities_enforced
INFO  [tests]   Tests passed :      15
INFO  [tests]   Tests failed :      0

tools/checkpatch.sh -c -u -m -g clean.

A SIGEV_SIGNAL | SIGEV_THREAD_ID timer skipped timer_create's signal
check, and nxsig_notification honoured any thread ID. Check the signal
where the event is delivered, keep SIGEV_THREAD_ID inside the owner's
process as Linux does, and refuse both in timer_create so an expiring
timer never fails its DEBUGVERIFY.

Signed-off-by: Royyan Zahir <royzah@gmail.com>
@github-actions github-actions Bot added Area: OS Components OS Components issues Size: S The size of the change in this PR is small labels Oct 1, 2026
@github-actions

github-actions Bot commented Oct 1, 2026

Copy link
Copy Markdown

MemBrowse Memory Report

hifive1-revb

  • flash: .text +48 B (+0.1%, 83,884 B / 4,194,304 B, total: 2% used)

qemu-armv8a

  • Code: .text.nxsig_notification +68 B, .text.timer_create +28 B (+0.0%, 346,992 B)

qemu-intel64

memcpy(&info.si_value, &event->sigev_value, sizeof(union sigval));

/* SIGEV_THREAD_ID currently used only by POSIX timer. */
if (!GOOD_SIGNO(event->sigev_signo))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why need check again

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gpio, button and phy_notify store user sigevents unchecked, so this is the one spot every path goes thru. timer_create checks too, so the caller gets EINVAL, not a DEBUGVERIFY at expiry.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but it's better to algin the check point between gpio/button/phy and timer.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@xiaoxiang781216 ah ok got it, still learning this part so lemme check I get u right:

one small helper like nxsig_event_valid(), called at register time in timer_create + gpio + button + phy, then I drop the recheck in nxsig_notification?

but there is like ~15 more drivers that also take sigevent from user (joysticks, rtc, oneshot, aio, esp wifi ...). u want them all in this PR too, or ok to do in follow-up?

{
FAR struct tcb_s *owner = nxsched_get_tcb(pid);
FAR struct tcb_s *target =
nxsched_get_tcb(event->sigev_notify_thread_id);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why check again too

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same, those drivers take SIGEV_THREAD_ID from user space unchecked.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area: OS Components OS Components issues Size: S The size of the change in this PR is small

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants