Fix the handling of ancillary data for SCTP socket. Implement

sctp_process_cmsgs_for_init() and sctp_findassociation_cmsgs()
similar to sctp_find_cmsg() to improve consistency and avoid
the signed/unsigned issues in sctp_process_cmsgs_for_init()
and sctp_findassociation_cmsgs().

Thanks to andrew@ for reporting the problem he found using
syzcaller.

Approved by:            re (kib@)
MFC after:              1 week
This commit is contained in:
Michael Tuexen 2018-09-30 16:21:31 +00:00
parent ae0a9a8850
commit 8184648425
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=339024

View File

@ -3572,7 +3572,6 @@ static int
sctp_process_cmsgs_for_init(struct sctp_tcb *stcb, struct mbuf *control, int *error)
{
struct cmsghdr cmh;
int tlen, at;
struct sctp_initmsg initmsg;
#ifdef INET
struct sockaddr_in sin;
@ -3580,34 +3579,37 @@ sctp_process_cmsgs_for_init(struct sctp_tcb *stcb, struct mbuf *control, int *er
#ifdef INET6
struct sockaddr_in6 sin6;
#endif
int tot_len, rem_len, cmsg_data_len, cmsg_data_off, off;
tlen = SCTP_BUF_LEN(control);
at = 0;
while (at < tlen) {
if ((tlen - at) < (int)CMSG_ALIGN(sizeof(cmh))) {
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. */
*error = EINVAL;
return (1);
}
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. */
*error = EINVAL;
return (1);
}
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. */
*error = EINVAL;
return (1);
}
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) {
switch (cmh.cmsg_type) {
case SCTP_INIT:
if ((size_t)(cmh.cmsg_len - CMSG_ALIGN(sizeof(cmh))) < sizeof(struct sctp_initmsg)) {
if (cmsg_data_len < (int)sizeof(struct sctp_initmsg)) {
*error = EINVAL;
return (1);
}
m_copydata(control, at + CMSG_ALIGN(sizeof(cmh)), sizeof(struct sctp_initmsg), (caddr_t)&initmsg);
m_copydata(control, cmsg_data_off, sizeof(struct sctp_initmsg), (caddr_t)&initmsg);
if (initmsg.sinit_max_attempts)
stcb->asoc.max_init_times = initmsg.sinit_max_attempts;
if (initmsg.sinit_num_ostreams)
@ -3662,7 +3664,7 @@ sctp_process_cmsgs_for_init(struct sctp_tcb *stcb, struct mbuf *control, int *er
break;
#ifdef INET
case SCTP_DSTADDRV4:
if ((size_t)(cmh.cmsg_len - CMSG_ALIGN(sizeof(cmh))) < sizeof(struct in_addr)) {
if (cmsg_data_len < (int)sizeof(struct in_addr)) {
*error = EINVAL;
return (1);
}
@ -3670,7 +3672,7 @@ sctp_process_cmsgs_for_init(struct sctp_tcb *stcb, struct mbuf *control, int *er
sin.sin_family = AF_INET;
sin.sin_len = sizeof(struct sockaddr_in);
sin.sin_port = stcb->rport;
m_copydata(control, at + CMSG_ALIGN(sizeof(cmh)), sizeof(struct in_addr), (caddr_t)&sin.sin_addr);
m_copydata(control, cmsg_data_off, sizeof(struct in_addr), (caddr_t)&sin.sin_addr);
if ((sin.sin_addr.s_addr == INADDR_ANY) ||
(sin.sin_addr.s_addr == INADDR_BROADCAST) ||
IN_MULTICAST(ntohl(sin.sin_addr.s_addr))) {
@ -3686,7 +3688,7 @@ sctp_process_cmsgs_for_init(struct sctp_tcb *stcb, struct mbuf *control, int *er
#endif
#ifdef INET6
case SCTP_DSTADDRV6:
if ((size_t)(cmh.cmsg_len - CMSG_ALIGN(sizeof(cmh))) < sizeof(struct in6_addr)) {
if (cmsg_data_len < (int)sizeof(struct in6_addr)) {
*error = EINVAL;
return (1);
}
@ -3694,7 +3696,7 @@ sctp_process_cmsgs_for_init(struct sctp_tcb *stcb, struct mbuf *control, int *er
sin6.sin6_family = AF_INET6;
sin6.sin6_len = sizeof(struct sockaddr_in6);
sin6.sin6_port = stcb->rport;
m_copydata(control, at + CMSG_ALIGN(sizeof(cmh)), sizeof(struct in6_addr), (caddr_t)&sin6.sin6_addr);
m_copydata(control, cmsg_data_off, sizeof(struct in6_addr), (caddr_t)&sin6.sin6_addr);
if (IN6_IS_ADDR_UNSPECIFIED(&sin6.sin6_addr) ||
IN6_IS_ADDR_MULTICAST(&sin6.sin6_addr)) {
*error = EINVAL;
@ -3727,7 +3729,6 @@ sctp_process_cmsgs_for_init(struct sctp_tcb *stcb, struct mbuf *control, int *er
break;
}
}
at += CMSG_ALIGN(cmh.cmsg_len);
}
return (0);
}
@ -3740,7 +3741,6 @@ sctp_findassociation_cmsgs(struct sctp_inpcb **inp_p,
int *error)
{
struct cmsghdr cmh;
int tlen, at;
struct sctp_tcb *stcb;
struct sockaddr *addr;
#ifdef INET
@ -3749,31 +3749,34 @@ sctp_findassociation_cmsgs(struct sctp_inpcb **inp_p,
#ifdef INET6
struct sockaddr_in6 sin6;
#endif
int tot_len, rem_len, cmsg_data_len, cmsg_data_off, off;
tlen = SCTP_BUF_LEN(control);
at = 0;
while (at < tlen) {
if ((tlen - at) < (int)CMSG_ALIGN(sizeof(cmh))) {
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. */
*error = EINVAL;
return (NULL);
}
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. */
*error = EINVAL;
return (NULL);
}
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. */
*error = EINVAL;
return (NULL);
}
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) {
switch (cmh.cmsg_type) {
#ifdef INET
case SCTP_DSTADDRV4:
if ((size_t)(cmh.cmsg_len - CMSG_ALIGN(sizeof(cmh))) < sizeof(struct in_addr)) {
if (cmsg_data_len < (int)sizeof(struct in_addr)) {
*error = EINVAL;
return (NULL);
}
@ -3781,13 +3784,13 @@ sctp_findassociation_cmsgs(struct sctp_inpcb **inp_p,
sin.sin_family = AF_INET;
sin.sin_len = sizeof(struct sockaddr_in);
sin.sin_port = port;
m_copydata(control, at + CMSG_ALIGN(sizeof(cmh)), sizeof(struct in_addr), (caddr_t)&sin.sin_addr);
m_copydata(control, cmsg_data_off, sizeof(struct in_addr), (caddr_t)&sin.sin_addr);
addr = (struct sockaddr *)&sin;
break;
#endif
#ifdef INET6
case SCTP_DSTADDRV6:
if ((size_t)(cmh.cmsg_len - CMSG_ALIGN(sizeof(cmh))) < sizeof(struct in6_addr)) {
if (cmsg_data_len < (int)sizeof(struct in6_addr)) {
*error = EINVAL;
return (NULL);
}
@ -3795,7 +3798,7 @@ sctp_findassociation_cmsgs(struct sctp_inpcb **inp_p,
sin6.sin6_family = AF_INET6;
sin6.sin6_len = sizeof(struct sockaddr_in6);
sin6.sin6_port = port;
m_copydata(control, at + CMSG_ALIGN(sizeof(cmh)), sizeof(struct in6_addr), (caddr_t)&sin6.sin6_addr);
m_copydata(control, cmsg_data_off, sizeof(struct in6_addr), (caddr_t)&sin6.sin6_addr);
#ifdef INET
if (IN6_IS_ADDR_V4MAPPED(&sin6.sin6_addr)) {
in6_sin6_2_sin(&sin, &sin6);
@ -3816,7 +3819,6 @@ sctp_findassociation_cmsgs(struct sctp_inpcb **inp_p,
}
}
}
at += CMSG_ALIGN(cmh.cmsg_len);
}
return (NULL);
}