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:
parent
3b9f1e4292
commit
6e83ea0505
@ -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);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user