Fix parsing error when processing cmsg in SCTP send calls. Thei bug is

related to a signed/unsigned mismatch.
This should most likely fix the issue in sctp_sosend reported by
Dmitry Vyukov on the freebsd-hackers mailing list and found by
running syzkaller.
This commit is contained in:
Michael Tuexen 2017-10-27 19:27:05 +00:00
parent ab8e311b11
commit 966dfbf910
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=325046

View File

@ -3465,32 +3465,35 @@ static int
sctp_find_cmsg(int c_type, void *data, struct mbuf *control, size_t cpsize)
{
struct cmsghdr cmh;
int tlen, at, found;
struct sctp_sndinfo sndinfo;
struct sctp_prinfo prinfo;
struct sctp_authinfo authinfo;
int tot_len, rem_len, cmsg_data_len, cmsg_data_off, off;
int found;
tlen = SCTP_BUF_LEN(control);
at = 0;
found = 0;
/*
* Independent of how many mbufs, find the c_type inside the control
* structure and copy out the data.
*/
while (at < tlen) {
if ((tlen - at) < (int)CMSG_ALIGN(sizeof(cmh))) {
found = 0;
tot_len = SCTP_BUF_LEN(control);
for (off = 0; off < tot_len; off += CMSG_ALIGN(cmh.cmsg_len)) {
rem_len = tot_len - off;
if (rem_len < (int)CMSG_ALIGN(sizeof(cmh))) {
/* There is not enough room for one more. */
return (found);
}
m_copydata(control, at, sizeof(cmh), (caddr_t)&cmh);
m_copydata(control, off, sizeof(cmh), (caddr_t)&cmh);
if (cmh.cmsg_len < CMSG_ALIGN(sizeof(cmh))) {
/* We dont't have a complete CMSG header. */
return (found);
}
if (((int)cmh.cmsg_len + at) > tlen) {
if ((cmh.cmsg_len > INT_MAX) || ((int)cmh.cmsg_len > rem_len)) {
/* We don't have the complete CMSG. */
return (found);
}
cmsg_data_len = (int)cmh.cmsg_len - CMSG_ALIGN(sizeof(cmh));
cmsg_data_off = off + CMSG_ALIGN(sizeof(cmh));
if ((cmh.cmsg_level == IPPROTO_SCTP) &&
((c_type == cmh.cmsg_type) ||
((c_type == SCTP_SNDRCV) &&
@ -3498,11 +3501,14 @@ sctp_find_cmsg(int c_type, void *data, struct mbuf *control, size_t cpsize)
(cmh.cmsg_type == SCTP_PRINFO) ||
(cmh.cmsg_type == SCTP_AUTHINFO))))) {
if (c_type == cmh.cmsg_type) {
if ((size_t)(cmh.cmsg_len - CMSG_ALIGN(sizeof(cmh))) < cpsize) {
if (cpsize > INT_MAX) {
return (found);
}
if (cmsg_data_len < (int)cpsize) {
return (found);
}
/* It is exactly what we want. Copy it out. */
m_copydata(control, at + CMSG_ALIGN(sizeof(cmh)), (int)cpsize, (caddr_t)data);
m_copydata(control, cmsg_data_off, (int)cpsize, (caddr_t)data);
return (1);
} else {
struct sctp_sndrcvinfo *sndrcvinfo;
@ -3516,10 +3522,10 @@ sctp_find_cmsg(int c_type, void *data, struct mbuf *control, size_t cpsize)
}
switch (cmh.cmsg_type) {
case SCTP_SNDINFO:
if ((size_t)(cmh.cmsg_len - CMSG_ALIGN(sizeof(cmh))) < sizeof(struct sctp_sndinfo)) {
if (cmsg_data_len < (int)sizeof(struct sctp_sndinfo)) {
return (found);
}
m_copydata(control, at + CMSG_ALIGN(sizeof(cmh)), sizeof(struct sctp_sndinfo), (caddr_t)&sndinfo);
m_copydata(control, cmsg_data_off, sizeof(struct sctp_sndinfo), (caddr_t)&sndinfo);
sndrcvinfo->sinfo_stream = sndinfo.snd_sid;
sndrcvinfo->sinfo_flags = sndinfo.snd_flags;
sndrcvinfo->sinfo_ppid = sndinfo.snd_ppid;
@ -3527,10 +3533,10 @@ sctp_find_cmsg(int c_type, void *data, struct mbuf *control, size_t cpsize)
sndrcvinfo->sinfo_assoc_id = sndinfo.snd_assoc_id;
break;
case SCTP_PRINFO:
if ((size_t)(cmh.cmsg_len - CMSG_ALIGN(sizeof(cmh))) < sizeof(struct sctp_prinfo)) {
if (cmsg_data_len < (int)sizeof(struct sctp_prinfo)) {
return (found);
}
m_copydata(control, at + CMSG_ALIGN(sizeof(cmh)), sizeof(struct sctp_prinfo), (caddr_t)&prinfo);
m_copydata(control, cmsg_data_off, sizeof(struct sctp_prinfo), (caddr_t)&prinfo);
if (prinfo.pr_policy != SCTP_PR_SCTP_NONE) {
sndrcvinfo->sinfo_timetolive = prinfo.pr_value;
} else {
@ -3539,10 +3545,10 @@ sctp_find_cmsg(int c_type, void *data, struct mbuf *control, size_t cpsize)
sndrcvinfo->sinfo_flags |= prinfo.pr_policy;
break;
case SCTP_AUTHINFO:
if ((size_t)(cmh.cmsg_len - CMSG_ALIGN(sizeof(cmh))) < sizeof(struct sctp_authinfo)) {
if (cmsg_data_len < (int)sizeof(struct sctp_authinfo)) {
return (found);
}
m_copydata(control, at + CMSG_ALIGN(sizeof(cmh)), sizeof(struct sctp_authinfo), (caddr_t)&authinfo);
m_copydata(control, cmsg_data_off, sizeof(struct sctp_authinfo), (caddr_t)&authinfo);
sndrcvinfo->sinfo_keynumber_valid = 1;
sndrcvinfo->sinfo_keynumber = authinfo.auth_keynumber;
break;
@ -3552,7 +3558,6 @@ sctp_find_cmsg(int c_type, void *data, struct mbuf *control, size_t cpsize)
found = 1;
}
}
at += CMSG_ALIGN(cmh.cmsg_len);
}
return (found);
}