Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F172849040
D58695.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
6 KB
Referenced Files
None
Subscribers
None
D58695.diff
View Options
diff --git a/sys/kern/uipc_usrreq.c b/sys/kern/uipc_usrreq.c
--- a/sys/kern/uipc_usrreq.c
+++ b/sys/kern/uipc_usrreq.c
@@ -1451,14 +1451,26 @@
ctl = 0;
first = STAILQ_FIRST(&sb->uxst_mbq);
if (first->m_type == MT_CONTROL) {
+ struct mbuf *prev;
+
control = first;
+ prev = NULL;
+
+ /*
+ * Unlink control messages from the socket buffer. The head of
+ * the socket buffer queue is updated below.
+ */
STAILQ_FOREACH_FROM(first, &sb->uxst_mbq, m_stailq) {
- if (first->m_type != MT_CONTROL)
+ if (first->m_type != MT_CONTROL) {
+ if (!peek && prev != NULL)
+ STAILQ_NEXT(prev, m_stailq) = NULL;
break;
+ }
ctl += first->m_len;
mbcnt += MSIZE;
if (first->m_flags & M_EXT)
mbcnt += first->m_ext.ext_size;
+ prev = first;
}
} else
control = NULL;
@@ -1553,10 +1565,25 @@
*/
error = unp_externalize(so, control, controlp, flags);
control = m_free(control);
- if (__predict_false(error && control != NULL)) {
+ if (__predict_false(error != 0)) {
struct mchain cmc;
- mc_init_m(&cmc, control);
+ /*
+ * Build an mbuf chain containing the remainder
+ * of the control messages and the subsequent
+ * data, to be prepended back to the socket
+ * buffer.
+ */
+ if (control != NULL)
+ mc_init_m(&cmc, control);
+ else
+ mc_init(&cmc);
+ for (m = first; datalen > 0 && m != part;
+ m = next) {
+ datalen -= m->m_len;
+ next = STAILQ_NEXT(m, m_stailq);
+ mc_append(&cmc, m);
+ }
SOCK_RECVBUF_LOCK(so);
if (__predict_false(
@@ -1566,7 +1593,7 @@
/*
* While the lock was dropped and we
* were failing in unp_externalize(),
- * the peer could has a) disconnected,
+ * the peer could have a) disconnected,
* b) filled the buffer so that we
* can't prepend data back.
* These are two edge conditions that
@@ -1590,6 +1617,10 @@
sb->sb_mbcnt = 0;
STAILQ_FOREACH(m, &sb->uxst_mbq, m_stailq) {
if (m->m_type == MT_DATA) {
+ if (m == part) {
+ m->m_len += partlen;
+ m->m_data -= partlen;
+ }
sb->sb_acc += m->m_len;
sb->sb_ccc += m->m_len;
} else {
diff --git a/sys/sys/mbuf.h b/sys/sys/mbuf.h
--- a/sys/sys/mbuf.h
+++ b/sys/sys/mbuf.h
@@ -1758,6 +1758,13 @@
}
}
+static inline void
+mc_init(struct mchain *mc)
+{
+ STAILQ_INIT(&mc->mc_q);
+ mc->mc_len = mc->mc_mlen = 0;
+}
+
/*
* Get mchain from a classic mbuf chain linked by m_next. Two hacks here:
* we use the fact that m_next is alias to m_stailq, we use internal queue(3)
diff --git a/tests/sys/kern/unix_passfd_test.c b/tests/sys/kern/unix_passfd_test.c
--- a/tests/sys/kern/unix_passfd_test.c
+++ b/tests/sys/kern/unix_passfd_test.c
@@ -1094,6 +1094,125 @@
closesocketpair(fd);
}
+/*
+ * Exercise handling of errors from unp_externalize().
+ */
+ATF_TC_WITHOUT_HEAD(externalize_error_partial_read);
+ATF_TC_BODY(externalize_error_partial_read, tc)
+{
+ struct iovec iovec;
+ struct msghdr msghdr;
+ struct rlimit rl, orl;
+ struct stat sb;
+ char cmsgbuf[CMSG_SPACE(sizeof(int))];
+ char msg1[16];
+ char *fill, *rbuf;
+ size_t fillsz;
+#if TEST_PROTO == SOCK_STREAM
+ size_t got;
+#endif
+ ssize_t len;
+ int fd[2], nfds, putfd;
+
+ memset(msg1, 'A', sizeof(msg1));
+
+ domainsocketpair(fd);
+ devnull(&putfd);
+ dofstat(putfd, &sb);
+ nfds = getnfds();
+
+#if TEST_PROTO == SOCK_STREAM
+ fillsz = (size_t)getrecvspace() * 3 / 5;
+#elif TEST_PROTO == SOCK_DGRAM
+ fillsz = 128;
+#endif
+
+ fill = malloc(fillsz);
+ ATF_REQUIRE(fill != NULL);
+ memset(fill, 'B', fillsz);
+ rbuf = malloc(sizeof(msg1) + fillsz);
+ ATF_REQUIRE(rbuf != NULL);
+
+ /*
+ * The first message carries the rights and a small payload; the second
+ * queues more data behind it, so that the read below leaves the receive
+ * buffer non-empty.
+ */
+ len = sendfd_payload(fd[0], putfd, msg1, sizeof(msg1));
+ ATF_REQUIRE_MSG(len == (ssize_t)sizeof(msg1),
+ "sendmsg: %zd bytes sent; expected %zu: %s", len, sizeof(msg1),
+ strerror(errno));
+ len = send(fd[0], fill, fillsz, 0);
+ ATF_REQUIRE_MSG(len == (ssize_t)fillsz,
+ "send: %zd bytes sent; expected %zu: %s", len, fillsz,
+ strerror(errno));
+
+ /*
+ * Use fd limits to force receive to fail.
+ */
+ ATF_REQUIRE_MSG(getrlimit(RLIMIT_NOFILE, &orl) == 0,
+ "getrlimit failed: %s", strerror(errno));
+ rl = orl;
+ rl.rlim_cur = 1;
+ ATF_REQUIRE_MSG(setrlimit(RLIMIT_NOFILE, &rl) == 0,
+ "setrlimit failed: %s", strerror(errno));
+
+ bzero(&msghdr, sizeof(msghdr));
+ iovec.iov_base = rbuf;
+ iovec.iov_len = sizeof(msg1);
+ msghdr.msg_iov = &iovec;
+ msghdr.msg_iovlen = 1;
+ msghdr.msg_control = cmsgbuf;
+ msghdr.msg_controllen = sizeof(cmsgbuf);
+
+ ATF_REQUIRE_ERRNO(EMFILE, recvmsg(fd[1], &msghdr, 0) == -1);
+
+ ATF_REQUIRE_MSG(setrlimit(RLIMIT_NOFILE, &orl) == 0,
+ "setrlimit failed: %s", strerror(errno));
+
+ /* The rights must have been disposed of rather than installed. */
+ ATF_REQUIRE_MSG(getnfds() == nfds, "descriptor leaked");
+
+ /*
+ * The failed read must leave the socket usable with both payloads still
+ * queued.
+ */
+#if TEST_PROTO == SOCK_STREAM
+ for (got = 0; got < sizeof(msg1) + fillsz; got += (size_t)len) {
+ len = recv(fd[1], rbuf + got, sizeof(msg1) + fillsz - got, 0);
+ if (len <= 0)
+ break;
+ }
+ ATF_REQUIRE_MSG(got == sizeof(msg1) + fillsz,
+ "recovered %zu of %zu bytes after the failed read: %s", got,
+ sizeof(msg1) + fillsz, strerror(errno));
+ ATF_REQUIRE_MSG(memcmp(rbuf, msg1, sizeof(msg1)) == 0,
+ "first payload corrupted");
+ ATF_REQUIRE_MSG(memcmp(rbuf + sizeof(msg1), fill, fillsz) == 0,
+ "second payload corrupted");
+#elif TEST_PROTO == SOCK_DGRAM
+ /*
+ * For datagrams, soreceive_dgram() dequeues the record before
+ * processing control messages, so the first datagram's payload is
+ * consumed even when externalize fails. Only the second datagram
+ * should remain queued.
+ */
+ len = recv(fd[1], rbuf, fillsz, 0);
+ ATF_REQUIRE_MSG(len == (ssize_t)fillsz,
+ "second datagram: got %zd bytes, expected %zu: %s", len, fillsz,
+ strerror(errno));
+ ATF_REQUIRE_MSG(memcmp(rbuf, fill, fillsz) == 0,
+ "second payload corrupted");
+#endif
+
+ dofstat(putfd, &sb);
+
+ free(rbuf);
+ free(fill);
+ close(putfd);
+ closesocketpair(fd);
+}
+
/*
* Verify that we can handle empty rights messages.
*/
@@ -1424,6 +1543,7 @@
ATF_TP_ADD_TC(tp, rights_creds_payload);
ATF_TP_ADD_TC(tp, truncated_rights);
ATF_TP_ADD_TC(tp, copyout_rights_error);
+ ATF_TP_ADD_TC(tp, externalize_error_partial_read);
ATF_TP_ADD_TC(tp, empty_rights_message);
ATF_TP_ADD_TC(tp, control_creates_records);
ATF_TP_ADD_TC(tp, cross_jail_dirfd);
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Tue, Sep 22, 1:51 PM (19 h, 13 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
39417964
Default Alt Text
D58695.diff (6 KB)
Attached To
Mode
D58695: unix: Fix some bugs in the SOCK_STREAM receive path
Attached
Detach File
Event Timeline
Log In to Comment