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:
parent
ab8e311b11
commit
966dfbf910
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user