This is an automated email from the ASF dual-hosted git repository.

casaroli pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git

commit 8c0d6b9957d3835f895066c000a1778f7a653bf5
Author: zhangkai25 <[email protected]>
AuthorDate: Wed Apr 1 10:45:56 2026 +0800

    net/can: Fix send buffer notify race.
    
    can_sendbuffer_notify() uses nxsem_get_value() to check if any thread
    is waiting on the send semaphore before posting.  However, there is a
    race window between releasing the locks in conn_dev_sem_timedwait()
    and nxsem_wait().  If a higher-priority workqueue thread (e.g., TX
    completion handler) runs in this window and calls
    can_sendbuffer_notify(), it sees val == 0 (no waiter yet) and skips
    the post.  The send thread then enters sem_wait but never gets woken
    up, causing permanent blocking.
    
    Change val < 0 to val < 1 so that the notify also posts when val is
    0.  This ensures the semaphore value becomes 1 before the send thread
    calls sem_wait, allowing it to return immediately.  val cannot
    accumulate beyond 1 since val > 0 is not posted.
    
    Because the semaphore can now be posted while nobody waits,
    can_sendmsg() checks the send buffer limit again after every wake-up
    instead of only once, so a send never queues past the limit.  A frame
    is always accepted into an empty queue, so a send buffer smaller than
    one frame cannot block the socket forever.
    
    This is the CAN part of the original change, which also fixed UDP, TCP
    and packet sockets.
    
    Assisted-by: Claude:claude-opus-5-5
    Signed-off-by: zhangkai25 <[email protected]>
    Signed-off-by: p-szafonimateusz <[email protected]>
---
 net/can/can_sendmsg_buffered.c | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/net/can/can_sendmsg_buffered.c b/net/can/can_sendmsg_buffered.c
index 40773022a4d..9b2a6138d4d 100644
--- a/net/can/can_sendmsg_buffered.c
+++ b/net/can/can_sendmsg_buffered.c
@@ -244,8 +244,14 @@ ssize_t can_sendmsg(FAR struct socket *psock, FAR const 
struct msghdr *msg,
   conn_dev_lock(&conn->sconn, dev);
 
 #if CONFIG_NET_SEND_BUFSIZE > 0
-  if ((iob_get_queue_size(&conn->write_q) + msg->msg_iov->iov_len) >
-      conn->sndbufs)
+  /* Wait until the frame fits.  The semaphore can be posted while there is
+   * still no room, so check again after every wake-up.  A frame is always
+   * accepted into an empty queue, even if it is larger than sndbufs.
+   */
+
+  while (!IOB_QEMPTY(&conn->write_q) &&
+         (iob_get_queue_size(&conn->write_q) + msg->msg_iov->iov_len) >
+         conn->sndbufs)
     {
       /* send buffer size exceeds the send limit */
 
@@ -475,7 +481,7 @@ void can_sendbuffer_notify(FAR struct can_conn_s *conn)
   int val = 0;
 
   nxsem_get_value(&conn->sndsem, &val);
-  if (val < 0)
+  if (val < 1)
     {
       nxsem_post(&conn->sndsem);
     }

Reply via email to