casaroli commented on code in PR #21418:
URL: https://github.com/apache/nuttx/pull/21418#discussion_r4459452388


##########
net/local/local_sockif.c:
##########
@@ -822,9 +832,36 @@ static int local_connect(FAR struct socket *psock,
 #ifdef CONFIG_NET_LOCAL_DGRAM
       case SOCK_DGRAM:
         {
-          /* Perform the datagram connection logic */
+          FAR struct local_conn_s *conn = psock->s_conn;
+          FAR const struct sockaddr_un *unaddr =
+              (FAR const struct sockaddr_un *)addr;
+
+          if (addr->sa_family == AF_UNSPEC)
+            {
+              memset(&conn->lc_daddr, 0, sizeof(conn->lc_daddr));
+              if (conn->lc_state == LOCAL_STATE_CONNECTED)
+                {
+                  conn->lc_state = conn->lc_type == LOCAL_TYPE_UNNAMED
+                                   ? LOCAL_STATE_UNBOUND
+                                   : LOCAL_STATE_BOUND;
+                }
+
+              return OK;
+            }
+
+          /* Record the default peer.  Copy only what the caller supplied
+           * so that a short addrlen does not read past its buffer.
+           */
 
-          return -ENOSYS;
+          if (addrlen > sizeof(conn->lc_daddr))
+            {
+              addrlen = sizeof(conn->lc_daddr);
+            }
+
+          memset(&conn->lc_daddr, 0, sizeof(conn->lc_daddr));
+          memcpy(&conn->lc_daddr, unaddr, addrlen);
+          conn->lc_state = LOCAL_STATE_CONNECTED;

Review Comment:
   connect() sets `lc_state` to `LOCAL_STATE_CONNECTED`, but `lc_peer` stays 
NULL. `local_getpeername()` checks only `lc_state` and then reads 
`peer->lc_type`, so getpeername() of a connected datagram socket is a NULL 
dereference: data abort at FAR 0x52 on qemu-armv8a:nsh and :knsh.
   
   Fix: in `local_getpeername()`, when `lc_peer` is NULL, give the address in 
`lc_daddr`.



##########
net/local/local_netpoll.c:
##########
@@ -161,7 +161,7 @@ int local_pollsetup(FAR struct socket *psock, FAR struct 
pollfd_s *fds)
 
   if (conn->lc_proto == SOCK_DGRAM)
     {
-      return -ENOSYS;
+      return file_poll(&conn->lc_infile, fds, true);

Review Comment:
   Each send opens and closes the receiver FIFO, and `pipecommon_close()` posts 
POLLHUP at the close of the last writer also for buffer policy 1 
(drivers/pipes/pipe_common.c:350). poll() of the receiver gives POLLIN|POLLHUP 
for each datagram, and POLLHUP alone for a send that fails with EMSGSIZE. A 
program that closes the socket on POLLHUP stops after the first datagram.
   
   Fix: in `pipecommon_close()`, post POLLHUP only for policy 0, as 
`pipecommon_poll()` does.



##########
net/local/local_bind.c:
##########
@@ -100,6 +100,27 @@ int psock_local_bind(FAR struct socket *psock,
 
   strlcpy(conn->lc_path, &unaddr->sun_path[index], sizeof(conn->lc_path));
 
+#ifdef CONFIG_NET_LOCAL_DGRAM
+  if (psock->s_type == SOCK_DGRAM)
+    {
+      int ret;
+
+      ret = local_create_halfduplex(conn, conn->lc_path, conn->lc_rcvsize);
+      if (ret < 0)
+        {
+          return ret;
+        }
+
+      ret = local_open_receiver(conn,

Review Comment:
   bind() now opens the FIFO in the blocking mode of the socket, and 
`psock_dgram_recvfrom()` uses MSG_DONTWAIT only when it opens the FIFO itself. 
So recv(MSG_DONTWAIT) of an empty bound socket now waits forever (master: 
EAGAIN).
   
   Fix: in `psock_dgram_recvfrom()`, when `lc_infile` is open and MSG_DONTWAIT 
is set, return -EAGAIN if FIONREAD gives 0, as `psock_stream_recvfrom()` does.



##########
net/local/local_sendmsg.c:
##########
@@ -286,11 +201,13 @@ static ssize_t local_sendto(FAR struct socket *psock,
 
   /* Verify that this is not a connected peer socket.  It need not be
    * bound, however.  If unbound, recvfrom will see this as a nameless
-   * connection.
+   * connection.  DGRAM connected state is allowed since connect() only
+   * sets the default peer address without establishing a FIFO link.
    */
 
   if (conn->lc_state != LOCAL_STATE_UNBOUND &&
-      conn->lc_state != LOCAL_STATE_BOUND)
+      conn->lc_state != LOCAL_STATE_BOUND &&
+      conn->lc_state != LOCAL_STATE_CONNECTED)

Review Comment:
   POSIX connect(): for SOCK_DGRAM the peer address "limits the remote sender 
for subsequent recv() functions". A connected receiver still takes datagrams 
from every socket.
   
   Fix: in `local_sendto()`, return -EPERM when the receiver has a peer address 
that is not the name of the sender (Linux does this).



##########
net/local/local_conn.c:
##########
@@ -178,6 +178,17 @@ FAR struct local_conn_s *local_alloc(void)
       nxmutex_init(&conn->lc_polllock);
       nxrmutex_init(&conn->lc_conn.s_lock);
 
+      /* Give the connection a unique placeholder path.  local_findconn()
+       * matches on lc_path, so leaving it empty would make every unbound
+       * connection look like every other one, and bind() would reject the
+       * second one with EADDRINUSE.  lc_type stays LOCAL_TYPE_UNNAMED:
+       * the socket has no name until it is bound, and getsockname() and
+       * getpeername() rely on that to report a zero-length sun_path.
+       */
+
+      snprintf(conn->lc_path, sizeof(conn->lc_path),
+               "unnamed.%p", conn);

Review Comment:
   The placeholder name goes out of the kernel. `local_send_preamble()` sends 
`lc_path` as the source of each datagram, so recvfrom() of a datagram from an 
unbound socket gives `unnamed.0x402ed1...` (master: no name). A stream client 
gets `lc_type` LOCAL_TYPE_PATHNAME in `local_stream_connect()`, so 
getsockname() of an unbound client and getpeername() of the accepted socket now 
give `unnamed.0x402ec8f0` (master: empty). These are kernel heap addresses. 
bind() does not need the placeholder: `psock_local_bind()` looks up the 
requested name, and an empty `lc_path` matches only an empty name.
   
   Fix: remove the placeholder.



##########
net/local/local_sockif.c:
##########
@@ -822,9 +832,36 @@ static int local_connect(FAR struct socket *psock,
 #ifdef CONFIG_NET_LOCAL_DGRAM
       case SOCK_DGRAM:
         {
-          /* Perform the datagram connection logic */
+          FAR struct local_conn_s *conn = psock->s_conn;
+          FAR const struct sockaddr_un *unaddr =
+              (FAR const struct sockaddr_un *)addr;
+
+          if (addr->sa_family == AF_UNSPEC)
+            {
+              memset(&conn->lc_daddr, 0, sizeof(conn->lc_daddr));
+              if (conn->lc_state == LOCAL_STATE_CONNECTED)
+                {
+                  conn->lc_state = conn->lc_type == LOCAL_TYPE_UNNAMED
+                                   ? LOCAL_STATE_UNBOUND
+                                   : LOCAL_STATE_BOUND;
+                }
+
+              return OK;
+            }
+
+          /* Record the default peer.  Copy only what the caller supplied
+           * so that a short addrlen does not read past its buffer.
+           */
 
-          return -ENOSYS;
+          if (addrlen > sizeof(conn->lc_daddr))
+            {
+              addrlen = sizeof(conn->lc_daddr);
+            }
+
+          memset(&conn->lc_daddr, 0, sizeof(conn->lc_daddr));
+          memcpy(&conn->lc_daddr, unaddr, addrlen);
+          conn->lc_state = LOCAL_STATE_CONNECTED;

Review Comment:
   connect() does not check the receiver: it returns 0 for a path that does not 
exist (POSIX: ENOENT) and for a regular file (ECONNREFUSED). send() after the 
receiver was closed returns ENOENT (Linux: ECONNREFUSED).
   
   Fix: look up the receiver with `local_findconn()` in connect() and return 
ENOENT or ECONNREFUSED as `psock_local_connect()` does; in `local_send()`, 
return ECONNREFUSED for -ENOENT.



##########
net/local/local_bind.c:
##########
@@ -100,6 +100,27 @@ int psock_local_bind(FAR struct socket *psock,
 
   strlcpy(conn->lc_path, &unaddr->sun_path[index], sizeof(conn->lc_path));
 
+#ifdef CONFIG_NET_LOCAL_DGRAM
+  if (psock->s_type == SOCK_DGRAM)
+    {
+      int ret;
+
+      ret = local_create_halfduplex(conn, conn->lc_path, conn->lc_rcvsize);
+      if (ret < 0)
+        {
+          return ret;
+        }
+
+      ret = local_open_receiver(conn,

Review Comment:
   A second bind() of a datagram socket opens `lc_infile` again over the open 
file: the first FIFO keeps a reader that is never closed. If the FIFO cannot be 
made, `lc_path` and `lc_type` stay set, and a retry gets EADDRINUSE from the 
socket itself.
   
   Fix: return -EINVAL when a datagram socket is already bound (Linux), and 
clear `lc_path` and `lc_type` on the error path.



##########
net/local/local_sockif.c:
##########
@@ -822,9 +832,36 @@ static int local_connect(FAR struct socket *psock,
 #ifdef CONFIG_NET_LOCAL_DGRAM
       case SOCK_DGRAM:
         {
-          /* Perform the datagram connection logic */
+          FAR struct local_conn_s *conn = psock->s_conn;
+          FAR const struct sockaddr_un *unaddr =
+              (FAR const struct sockaddr_un *)addr;
+
+          if (addr->sa_family == AF_UNSPEC)
+            {
+              memset(&conn->lc_daddr, 0, sizeof(conn->lc_daddr));
+              if (conn->lc_state == LOCAL_STATE_CONNECTED)
+                {
+                  conn->lc_state = conn->lc_type == LOCAL_TYPE_UNNAMED
+                                   ? LOCAL_STATE_UNBOUND
+                                   : LOCAL_STATE_BOUND;
+                }
+
+              return OK;
+            }
+
+          /* Record the default peer.  Copy only what the caller supplied
+           * so that a short addrlen does not read past its buffer.
+           */
 
-          return -ENOSYS;
+          if (addrlen > sizeof(conn->lc_daddr))
+            {
+              addrlen = sizeof(conn->lc_daddr);
+            }
+
+          memset(&conn->lc_daddr, 0, sizeof(conn->lc_daddr));
+          memcpy(&conn->lc_daddr, unaddr, addrlen);

Review Comment:
   With an abstract name, send() after connect() returns success, but the 
receiver gets nothing. `local_findconn()` skips the NUL at the start of 
`sun_path`, but `local_sendto()` builds the FIFO name from `unaddr->sun_path` 
(line 249), which is empty, so the datagram goes to the FIFO of the empty name.
   
   Fix: in `local_sendto()`, use `&unaddr->sun_path[1]` for an abstract name, 
as `local_findconn()` does.



##########
net/local/local_bind.c:
##########
@@ -100,6 +100,27 @@ int psock_local_bind(FAR struct socket *psock,
 
   strlcpy(conn->lc_path, &unaddr->sun_path[index], sizeof(conn->lc_path));
 
+#ifdef CONFIG_NET_LOCAL_DGRAM
+  if (psock->s_type == SOCK_DGRAM)
+    {
+      int ret;
+
+      ret = local_create_halfduplex(conn, conn->lc_path, conn->lc_rcvsize);
+      if (ret < 0)
+        {
+          return ret;
+        }
+
+      ret = local_open_receiver(conn,
+              _SS_ISNONBLOCK(conn->lc_conn.s_flags));
+      if (ret < 0)
+        {
+          local_release_halfduplex(conn);
+          return ret;
+        }
+    }
+#endif
+
   conn->lc_state = LOCAL_STATE_BOUND;

Review Comment:
   connect() and then bind() loses the peer: bind() sets `LOCAL_STATE_BOUND`, 
and send() then fails with EDESTADDRREQ.
   
   Fix: keep `LOCAL_STATE_CONNECTED` in bind() when the socket has a peer 
address.



##########
net/local/local_sendmsg.c:
##########
@@ -393,6 +310,81 @@ static ssize_t local_sendto(FAR struct socket *psock,
 #endif /* CONFIG_NET_LOCAL_DGRAM */
 }
 
+/****************************************************************************
+ * Name: local_send
+ *
+ * Description:
+ *   Send a local packet as a stream.
+ *
+ * Input Parameters:
+ *   psock    An instance of the internal socket structure.
+ *   buf      Data to send
+ *   len      Length of data to send
+ *   flags    Send flags (ignored for now)
+ *
+ * Returned Value:
+ *   On success, returns the number of characters sent.  On  error,
+ *   -1 is returned, and errno is set appropriately (see send() for the
+ *   list of errno numbers).
+ *
+ ****************************************************************************/
+
+static ssize_t local_send(FAR struct socket *psock,
+                          FAR const struct iovec *buf,
+                          size_t len, int flags)
+{
+  ssize_t ret;
+
+  switch (psock->s_type)
+    {
+      case SOCK_STREAM:
+        {
+          FAR struct local_conn_s *conn = psock->s_conn;
+
+          DEBUGASSERT(buf);
+
+          if (conn->lc_state != LOCAL_STATE_CONNECTED)
+            {
+              nerr("ERROR: not connected\n");
+              return -ENOTCONN;
+            }
+
+          if (conn->lc_outfile.f_inode == NULL)
+            {
+              return -EPIPE;
+            }
+
+          ret = local_send_packet(&conn->lc_outfile, buf, len);

Review Comment:
   The stream path no longer takes `lc_sendlock`, so two threads that send() on 
one stream socket are not serialized any more. The PR body says that stream 
sockets do not change.
   
   Fix: keep `nxmutex_lock(&conn->lc_sendlock)` around `local_send_packet()`.



##########
net/local/local_netpoll.c:
##########
@@ -161,7 +161,7 @@ int local_pollsetup(FAR struct socket *psock, FAR struct 
pollfd_s *fds)
 
   if (conn->lc_proto == SOCK_DGRAM)
     {
-      return -ENOSYS;
+      return file_poll(&conn->lc_infile, fds, true);

Review Comment:
   `lc_infile` is read-only, so poll() never reports POLLOUT for a datagram 
socket; poll(POLLOUT) of a bound socket waits forever. An unbound socket has no 
`lc_infile`: file_poll() returns -ENOSYS and poll() gives POLLERR.
   
   Fix: for SOCK_DGRAM, always report POLLOUT (a send has no send buffer to 
wait for), and call file_poll() for POLLIN only when `lc_infile` is open.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to