Browse Source

[LibOS] Avoid gaps due to partial reads in iovecs in recvmsg()

Syscall recvmsg() accepts an array of buffers (iovecs). This buffers are
filled in array order. POSIX does not allow to fill e.g. iov[0] only
partially before proceeding to iov[1]. This commit makes recvmsg()
emulation compliant with this requirement.

Also, this commit fixes a small performance issue when recvmsg() would
overwrite the same received-address info over and over.
jack.wxz 4 years ago
parent
commit
99193dc1f7
1 changed files with 25 additions and 21 deletions
  1. 25 21
      LibOS/shim/src/sys/shim_socket.c

+ 25 - 21
LibOS/shim/src/sys/shim_socket.c

@@ -1206,36 +1206,40 @@ static ssize_t do_recvmsg(int fd, struct iovec* bufs, int nbufs, int flags, stru
 
         bytes += pal_ret;
 
-        if (!addr || !bytes || address_received)
-            continue;
+        if (addr && !address_received) {
+            if (sock->domain == AF_UNIX) {
+                unix_copy_addr(addr, sock->addr.un.dentry);
+                *addrlen = sizeof(struct sockaddr_un);
+            }
 
-        if (sock->domain == AF_UNIX) {
-            unix_copy_addr(addr, sock->addr.un.dentry);
-            *addrlen = sizeof(struct sockaddr_un);
-        }
+            if (sock->domain == AF_INET || sock->domain == AF_INET6) {
+                if (uri) {
+                    struct addr_inet conn;
 
-        if (sock->domain == AF_INET || sock->domain == AF_INET6) {
-            if (uri) {
-                struct addr_inet conn;
+                    if ((ret = inet_parse_addr(sock->domain, sock->sock_type, uri, &conn, NULL)) < 0) {
+                        lock(&hdl->lock);
+                        goto out_locked;
+                    }
 
-                if ((ret = inet_parse_addr(sock->domain, sock->sock_type, uri, &conn, NULL)) < 0) {
-                    lock(&hdl->lock);
-                    goto out_locked;
-                }
+                    debug("last packet received from %s\n", uri);
 
-                debug("last packet received from %s\n", uri);
+                    inet_rebase_port(true, sock->domain, &conn, false);
+                    inet_copy_addr(sock->domain, addr, &conn);
+                } else {
+                    inet_copy_addr(sock->domain, addr, &sock->addr.in.conn);
+                }
 
-                inet_rebase_port(true, sock->domain, &conn, false);
-                inet_copy_addr(sock->domain, addr, &conn);
-            } else {
-                inet_copy_addr(sock->domain, addr, &sock->addr.in.conn);
+                *addrlen = (sock->domain == AF_INET) ? sizeof(struct sockaddr_in)
+                                                     : sizeof(struct sockaddr_in6);
             }
 
-            *addrlen = (sock->domain == AF_INET) ? sizeof(struct sockaddr_in)
-                                                 : sizeof(struct sockaddr_in6);
+            address_received = true;
         }
 
-        address_received = false;
+        /* gap in iovecs is not allowed, return a partial read to user; it is the responsibility of
+         * user application to deal with partial reads */
+        if (pal_ret < bufs[i].iov_len)
+            break;
     }
 
     if (bytes)