unix/*: rewrite unp_internalize() cmsg parsing cycle
Make it a complex, but a single for(;;) statement. The previous cycle with some loop logic in the beginning and some loop logic at the end was confusing. Both me and markj@ were misleaded to a conclusion that some checks are unnecessary, while they actually were necessary. While here, handle an edge case found by Mark, when on 64-bit platform an incorrect message from userland would underflow length counter, but return without any error. Provide a test case for such message. Reviewed by: markj Differential revision: https://reviews.freebsd.org/D35375
This commit is contained in:
parent
8d95f50052
commit
d97922c6c6
@ -2193,22 +2193,20 @@ unp_internalize(struct mbuf **controlp, struct thread *td)
|
||||
fdesc = p->p_fd;
|
||||
error = 0;
|
||||
control = *controlp;
|
||||
clen = control->m_len;
|
||||
*controlp = NULL;
|
||||
initial_controlp = controlp;
|
||||
for (cm = mtod(control, struct cmsghdr *); cm != NULL;) {
|
||||
if (sizeof(*cm) > clen || cm->cmsg_level != SOL_SOCKET
|
||||
|| cm->cmsg_len > clen || cm->cmsg_len < sizeof(*cm)) {
|
||||
error = EINVAL;
|
||||
goto out;
|
||||
}
|
||||
data = CMSG_DATA(cm);
|
||||
datalen = (caddr_t)cm + cm->cmsg_len - (caddr_t)data;
|
||||
for (clen = control->m_len, cm = mtod(control, struct cmsghdr *),
|
||||
data = CMSG_DATA(cm);
|
||||
|
||||
clen >= sizeof(*cm) && cm->cmsg_level == SOL_SOCKET &&
|
||||
clen >= cm->cmsg_len && cm->cmsg_len >= sizeof(*cm) &&
|
||||
(char *)cm + cm->cmsg_len >= (char *)data;
|
||||
|
||||
clen -= min(CMSG_SPACE(datalen), clen),
|
||||
cm = (struct cmsghdr *) ((char *)cm + CMSG_SPACE(datalen)),
|
||||
data = CMSG_DATA(cm)) {
|
||||
datalen = (char *)cm + cm->cmsg_len - (char *)data;
|
||||
switch (cm->cmsg_type) {
|
||||
/*
|
||||
* Fill in credential information.
|
||||
*/
|
||||
case SCM_CREDS:
|
||||
*controlp = sbcreatecontrol(NULL, sizeof(*cmcred),
|
||||
SCM_CREDS, SOL_SOCKET, M_WAITOK);
|
||||
@ -2228,7 +2226,7 @@ unp_internalize(struct mbuf **controlp, struct thread *td)
|
||||
case SCM_RIGHTS:
|
||||
oldfds = datalen / sizeof (int);
|
||||
if (oldfds == 0)
|
||||
break;
|
||||
continue;
|
||||
/* On some machines sizeof pointer is bigger than
|
||||
* sizeof int, so we need to check if data fits into
|
||||
* single mbuf. We could allocate several mbufs, and
|
||||
@ -2334,17 +2332,10 @@ unp_internalize(struct mbuf **controlp, struct thread *td)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (*controlp != NULL)
|
||||
controlp = &(*controlp)->m_next;
|
||||
if (CMSG_SPACE(datalen) < clen) {
|
||||
clen -= CMSG_SPACE(datalen);
|
||||
cm = (struct cmsghdr *)
|
||||
((caddr_t)cm + CMSG_SPACE(datalen));
|
||||
} else {
|
||||
clen = 0;
|
||||
cm = NULL;
|
||||
}
|
||||
controlp = &(*controlp)->m_next;
|
||||
}
|
||||
if (clen > 0)
|
||||
error = EINVAL;
|
||||
|
||||
out:
|
||||
if (error != 0 && initial_controlp != NULL)
|
||||
|
@ -744,14 +744,14 @@ ATF_TC_BODY(copyout_rights_error, tc)
|
||||
}
|
||||
|
||||
/*
|
||||
* Verify that we can handle empty rights messages. Try sending two SCM_RIGHTS
|
||||
* messages with a single call, one empty and one containing a single FD.
|
||||
* Verify that we can handle empty rights messages.
|
||||
*/
|
||||
ATF_TC_WITHOUT_HEAD(empty_rights_message);
|
||||
ATF_TC_BODY(empty_rights_message, tc)
|
||||
{
|
||||
struct iovec iov;
|
||||
struct msghdr msghdr;
|
||||
struct cmsghdr cmsg;
|
||||
char *cm, message[CMSG_SPACE(0) + CMSG_SPACE(sizeof(int))];
|
||||
ssize_t len;
|
||||
int error, fd[2], putfd;
|
||||
@ -759,21 +759,40 @@ ATF_TC_BODY(empty_rights_message, tc)
|
||||
domainsocketpair(fd);
|
||||
devnull(&putfd);
|
||||
|
||||
memset(&msghdr, 0, sizeof(msghdr));
|
||||
iov.iov_base = NULL;
|
||||
iov.iov_len = 0;
|
||||
msghdr.msg_iov = &iov;
|
||||
msghdr.msg_iovlen = 1;
|
||||
|
||||
/*
|
||||
* First, try sending an empty message followed by a non-empty message.
|
||||
* Try sending incorrect empty message. On 64-bit platforms, where
|
||||
* CMSG_SPACE(0) > sizeof(struct cmsghdr), this will exercise
|
||||
* an edge case.
|
||||
*/
|
||||
cmsg = (struct cmsghdr ){
|
||||
.cmsg_len = sizeof(struct cmsghdr), /* not CMSG_LEN(0)! */
|
||||
.cmsg_level = SOL_SOCKET,
|
||||
.cmsg_type = SCM_RIGHTS,
|
||||
};
|
||||
msghdr.msg_control = &cmsg;
|
||||
msghdr.msg_controllen = CMSG_SPACE(0);
|
||||
|
||||
len = sendmsg(fd[0], &msghdr, 0);
|
||||
if (CMSG_LEN(0) != sizeof(struct cmsghdr))
|
||||
ATF_REQUIRE(len == -1 && errno == EINVAL);
|
||||
else
|
||||
ATF_REQUIRE(len == 0);
|
||||
|
||||
/*
|
||||
* Try sending an empty message followed by a non-empty message.
|
||||
*/
|
||||
cm = message;
|
||||
putfds(cm, -1, 0);
|
||||
cm += CMSG_SPACE(0);
|
||||
putfds(cm, putfd, 1);
|
||||
|
||||
memset(&msghdr, 0, sizeof(msghdr));
|
||||
iov.iov_base = NULL;
|
||||
iov.iov_len = 0;
|
||||
msghdr.msg_control = message;
|
||||
msghdr.msg_controllen = sizeof(message);
|
||||
msghdr.msg_iov = &iov;
|
||||
msghdr.msg_iovlen = 1;
|
||||
|
||||
len = sendmsg(fd[0], &msghdr, 0);
|
||||
ATF_REQUIRE_MSG(len == 0, "sendmsg failed: %s", strerror(errno));
|
||||
|
Loading…
Reference in New Issue
Block a user