Skip to content

Commit 3b3d622

Browse files
authored
[DFSan] Fix recvmsg wrapper to support MSG_TRUNC flag. (#92599)
The MSG_TRUNC flag makes recvmsg return the real length of the packet, even if it was too big to fit in the provided buffer. This is commonly used together with MSG_PEEK. Without this patch, dfsan's clear_msghdr_labels expects the return value of recvmsg (size recieved) to be less than or equal to the iov buffer length where recvmsg writes data, resulting in a crash.
1 parent df626dd commit 3b3d622

File tree

2 files changed

+65
-28
lines changed

2 files changed

+65
-28
lines changed

compiler-rt/lib/dfsan/dfsan_custom.cpp

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1901,17 +1901,27 @@ SANITIZER_INTERFACE_ATTRIBUTE int __dfso_nanosleep(
19011901
return __dfsw_nanosleep(req, rem, req_label, rem_label, ret_label);
19021902
}
19031903

1904-
static void clear_msghdr_labels(size_t bytes_written, struct msghdr *msg) {
1904+
static void clear_msghdr_labels(size_t bytes_written, struct msghdr *msg,
1905+
int flags) {
19051906
dfsan_set_label(0, msg, sizeof(*msg));
19061907
dfsan_set_label(0, msg->msg_name, msg->msg_namelen);
19071908
dfsan_set_label(0, msg->msg_control, msg->msg_controllen);
1908-
for (size_t i = 0; bytes_written > 0; ++i) {
1909-
assert(i < msg->msg_iovlen);
1909+
for (size_t i = 0; i < msg->msg_iovlen; ++i) {
19101910
struct iovec *iov = &msg->msg_iov[i];
1911-
size_t iov_written =
1912-
bytes_written < iov->iov_len ? bytes_written : iov->iov_len;
1911+
size_t iov_written = iov->iov_len;
1912+
1913+
// When MSG_TRUNC is not set, we want to avoid setting 0 label on bytes that
1914+
// may not have changed, using bytes_written to bound the 0 label write.
1915+
// When MSG_TRUNC flag is set, bytes_written may be larger than the buffer,
1916+
// and should not be used as a bound.
1917+
if (!(MSG_TRUNC & flags)) {
1918+
if (bytes_written < iov->iov_len) {
1919+
iov_written = bytes_written;
1920+
}
1921+
bytes_written -= iov_written;
1922+
}
1923+
19131924
dfsan_set_label(0, iov->iov_base, iov_written);
1914-
bytes_written -= iov_written;
19151925
}
19161926
}
19171927

@@ -1923,7 +1933,7 @@ SANITIZER_INTERFACE_ATTRIBUTE int __dfsw_recvmmsg(
19231933
int ret = recvmmsg(sockfd, msgvec, vlen, flags, timeout);
19241934
for (int i = 0; i < ret; ++i) {
19251935
dfsan_set_label(0, &msgvec[i].msg_len, sizeof(msgvec[i].msg_len));
1926-
clear_msghdr_labels(msgvec[i].msg_len, &msgvec[i].msg_hdr);
1936+
clear_msghdr_labels(msgvec[i].msg_len, &msgvec[i].msg_hdr, flags);
19271937
}
19281938
*ret_label = 0;
19291939
return ret;
@@ -1947,7 +1957,7 @@ SANITIZER_INTERFACE_ATTRIBUTE ssize_t __dfsw_recvmsg(
19471957
dfsan_label msg_label, dfsan_label flags_label, dfsan_label *ret_label) {
19481958
ssize_t ret = recvmsg(sockfd, msg, flags);
19491959
if (ret >= 0)
1950-
clear_msghdr_labels(ret, msg);
1960+
clear_msghdr_labels(ret, msg, flags);
19511961
*ret_label = 0;
19521962
return ret;
19531963
}

compiler-rt/test/dfsan/custom.cpp

Lines changed: 47 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -768,26 +768,53 @@ void test_recvmsg() {
768768
ssize_t sent = sendmsg(sockfds[0], &smsg, 0);
769769
assert(sent > 0);
770770

771-
char rbuf[128];
772-
struct iovec riovs[2] = {{&rbuf[0], 4}, {&rbuf[4], 4}};
773-
struct msghdr rmsg = {};
774-
rmsg.msg_iov = riovs;
775-
rmsg.msg_iovlen = 2;
776-
777-
dfsan_set_label(i_label, rbuf, sizeof(rbuf));
778-
dfsan_set_label(i_label, &rmsg, sizeof(rmsg));
779-
780-
DEFINE_AND_SAVE_ORIGINS(rmsg)
781-
782-
ssize_t received = recvmsg(sockfds[1], &rmsg, 0);
783-
assert(received == sent);
784-
assert(memcmp(sbuf, rbuf, 8) == 0);
785-
ASSERT_ZERO_LABEL(received);
786-
ASSERT_READ_ZERO_LABEL(&rmsg, sizeof(rmsg));
787-
ASSERT_READ_ZERO_LABEL(&rbuf[0], 8);
788-
ASSERT_READ_LABEL(&rbuf[8], 1, i_label);
789-
790-
ASSERT_SAVED_ORIGINS(rmsg)
771+
{
772+
char rpbuf[2];
773+
struct iovec peek_iov;
774+
peek_iov.iov_base = rpbuf;
775+
peek_iov.iov_len = 2;
776+
777+
struct msghdr peek_header = {};
778+
peek_header.msg_iov = &peek_iov;
779+
peek_header.msg_iovlen = 1;
780+
781+
dfsan_set_label(i_label, rpbuf, sizeof(rpbuf));
782+
dfsan_set_label(i_label, &peek_header, sizeof(peek_header));
783+
784+
DEFINE_AND_SAVE_ORIGINS(peek_header)
785+
786+
ssize_t received = recvmsg(sockfds[1], &peek_header, MSG_PEEK | MSG_TRUNC);
787+
assert(received == sent);
788+
assert(memcmp(sbuf, rpbuf, 2) == 0);
789+
ASSERT_ZERO_LABEL(received);
790+
ASSERT_READ_ZERO_LABEL(&peek_header, sizeof(peek_header));
791+
ASSERT_READ_ZERO_LABEL(&rpbuf[0], 0);
792+
793+
ASSERT_SAVED_ORIGINS(peek_header)
794+
}
795+
796+
{
797+
char rbuf[128];
798+
struct iovec riovs[2] = {{&rbuf[0], 4}, {&rbuf[4], 4}};
799+
struct msghdr rmsg = {};
800+
rmsg.msg_iov = riovs;
801+
rmsg.msg_iovlen = 2;
802+
803+
dfsan_set_label(i_label, rbuf, sizeof(rbuf));
804+
dfsan_set_label(i_label, &rmsg, sizeof(rmsg));
805+
806+
DEFINE_AND_SAVE_ORIGINS(rmsg)
807+
808+
ssize_t received = recvmsg(sockfds[1], &rmsg, 0);
809+
assert(received == sent);
810+
assert(memcmp(sbuf, rbuf, 8) == 0);
811+
ASSERT_ZERO_LABEL(received);
812+
ASSERT_READ_ZERO_LABEL(&rmsg, sizeof(rmsg));
813+
ASSERT_READ_ZERO_LABEL(&rbuf[0], 8);
814+
ASSERT_READ_LABEL(&rbuf[8], 1, i_label);
815+
816+
ASSERT_SAVED_ORIGINS(rmsg)
817+
}
791818

792819
close(sockfds[0]);
793820
close(sockfds[1]);

0 commit comments

Comments
 (0)