This is an automated email from the ASF dual-hosted git repository. xiaoxiang781216 pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit 410c52eedf5c0c8c033a1cdce0dc0cc710f01c43 Author: zhangyuan29 <[email protected]> AuthorDate: Thu Nov 12 00:43:51 2026 +0800 fs/aio: unlink lio_listio entries before waking the submitter aio_signal() sends the per-request notification and SIGPOLL first, and only then checks and unlinks aiocbp->lio_link. Either signal can wake an lio_listio(LIO_WAIT) caller, which may return and release the aiocb list, usually on its stack, while the worker still touches lio_link. Unlink the entry and note whether it emptied the batch before sending any notification. The notification order is unchanged. Signed-off-by: zhangyuan29 <[email protected]> Signed-off-by: jingfei <[email protected]> --- fs/aio/aio_signal.c | 67 ++++++++++++++++++++++++++++++++++------------------- 1 file changed, 43 insertions(+), 24 deletions(-) diff --git a/fs/aio/aio_signal.c b/fs/aio/aio_signal.c index c8c734d12dd..a813125a5b6 100644 --- a/fs/aio/aio_signal.c +++ b/fs/aio/aio_signal.c @@ -60,7 +60,10 @@ * negated errno value is returned. * * Assumptions: - * This function runs only in the context of the worker thread. + * This function runs only in the context of the worker thread. The + * aiocbp is owned by the submitter and may become invalid once the + * submitter is woken, so lio_link must be unlinked before any + * notification that can wake it. * ****************************************************************************/ @@ -69,18 +72,42 @@ int aio_signal(pid_t pid, FAR struct aiocb *aiocbp) union sigval value; int status; int ret; + bool last = false; /* This completion emptied the lio_listio batch */ DEBUGASSERT(aiocbp); ret = OK; /* Assume success */ + /* Unlink from the lio_listio batch before anything that can wake the + * submitter: aiocbp usually lives on the submitter's stack, and once + * woken the submitter may return and invalidate it before we get a + * chance to touch lio_link. Even the in-list check needs the lock: + * completing a neighboring batch entry updates lio_link.prev under it. + * The batch-completion notification stays at its original position + * (after the poll signal) so the per-request -> poll -> batch + * notification order is unchanged. + */ + + aio_lock(); + if (list_in_list(&aiocbp->lio_link)) + { + last = list_is_empty(&aiocbp->lio_link); + list_delete(&aiocbp->lio_link); + } + + aio_unlock(); + /* Signal the client */ - ret = nxsig_notification(pid, &aiocbp->aio_sigevent, - SI_ASYNCIO, &aiocbp->aio_sigwork); - if (ret < 0) + status = nxsig_notification(pid, &aiocbp->aio_sigevent, + SI_ASYNCIO, &aiocbp->aio_sigwork); + if (status < 0) { - ferr("ERROR: nxsig_notification failed: %d\n", ret); + ferr("ERROR: nxsig_notification failed: %d\n", status); + if (ret >= OK) + { + ret = status; + } } /* Send the poll signal in any event in case the caller is waiting @@ -98,28 +125,20 @@ int aio_signal(pid_t pid, FAR struct aiocb *aiocbp) } } - if (list_in_list(&aiocbp->lio_link)) - { - /* This I/O is queued by lio_listio, remove this I/O from the list, - * signal the client when all I/O is completed - */ - - aio_lock(); - status = list_is_empty(&aiocbp->lio_link); - list_delete(&aiocbp->lio_link); - aio_unlock(); + /* This I/O was queued by lio_listio and emptied the batch: signal the + * client that all I/O of the batch has completed. + */ - if (status) + if (last) + { + status = nxsig_notification(pid, &aiocbp->lio_sigevent, SI_ASYNCIO, + &aiocbp->lio_sigwork); + if (status < 0) { - status = nxsig_notification(pid, &aiocbp->lio_sigevent, SI_ASYNCIO, - &aiocbp->lio_sigwork); - if (status < 0) + ferr("ERROR: nxsig_notification failed: %d\n", status); + if (ret >= OK) { - ferr("ERROR: nxsig_notification failed: %d\n", status); - if (ret >= OK) - { - ret = status; - } + ret = status; } } }
