Improve input validation for the IPPROTO_SCTP level socket options

SCTP_CONNECT_X and SCTP_CONNECT_X_DELAYED.

Some issues where found by running syzkaller.

MFC after:		3 days
This commit is contained in:
Michael Tuexen 2019-05-19 17:28:00 +00:00
parent bac5ec96f4
commit fc26bf717c
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=347975
3 changed files with 30 additions and 46 deletions

View file

@ -1352,13 +1352,12 @@ static int
sctp_do_connect_x(struct socket *so, struct sctp_inpcb *inp, void *optval,
size_t optsize, void *p, int delay)
{
int error = 0;
int error;
int creat_lock_on = 0;
struct sctp_tcb *stcb = NULL;
struct sockaddr *sa;
unsigned int num_v6 = 0, num_v4 = 0, *totaddrp, totaddr;
uint32_t vrf_id;
int bad_addresses = 0;
sctp_assoc_t *a_id;
SCTPDBG(SCTP_DEBUG_PCB1, "Connectx called\n");
@ -1397,17 +1396,12 @@ sctp_do_connect_x(struct socket *so, struct sctp_inpcb *inp, void *optval,
totaddrp = (unsigned int *)optval;
totaddr = *totaddrp;
sa = (struct sockaddr *)(totaddrp + 1);
stcb = sctp_connectx_helper_find(inp, sa, &totaddr, &num_v4, &num_v6, &error, (unsigned int)(optsize - sizeof(int)), &bad_addresses);
if ((stcb != NULL) || bad_addresses) {
error = sctp_connectx_helper_find(inp, sa, totaddr, &num_v4, &num_v6, (unsigned int)(optsize - sizeof(int)));
if (error != 0) {
/* Already have or am bring up an association */
SCTP_ASOC_CREATE_UNLOCK(inp);
creat_lock_on = 0;
if (stcb)
SCTP_TCB_UNLOCK(stcb);
if (bad_addresses == 0) {
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EALREADY);
error = EALREADY;
}
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, error);
goto out_now;
}
#ifdef INET6

View file

@ -6391,30 +6391,33 @@ sctp_connectx_helper_add(struct sctp_tcb *stcb, struct sockaddr *addr,
return (added);
}
struct sctp_tcb *
int
sctp_connectx_helper_find(struct sctp_inpcb *inp, struct sockaddr *addr,
unsigned int *totaddr,
unsigned int *num_v4, unsigned int *num_v6, int *error,
unsigned int limit, int *bad_addr)
unsigned int totaddr,
unsigned int *num_v4, unsigned int *num_v6,
unsigned int limit)
{
struct sockaddr *sa;
struct sctp_tcb *stcb = NULL;
struct sctp_tcb *stcb;
unsigned int incr, at, i;
at = 0;
sa = addr;
*error = *num_v6 = *num_v4 = 0;
*num_v6 = *num_v4 = 0;
/* account and validate addresses */
for (i = 0; i < *totaddr; i++) {
if (totaddr == 0) {
return (EINVAL);
}
for (i = 0; i < totaddr; i++) {
if (at + sizeof(struct sockaddr) > limit) {
return (EINVAL);
}
switch (sa->sa_family) {
#ifdef INET
case AF_INET:
incr = (unsigned int)sizeof(struct sockaddr_in);
if (sa->sa_len != incr) {
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTPUTIL, EINVAL);
*error = EINVAL;
*bad_addr = 1;
return (NULL);
return (EINVAL);
}
(*num_v4) += 1;
break;
@ -6427,46 +6430,34 @@ sctp_connectx_helper_find(struct sctp_inpcb *inp, struct sockaddr *addr,
sin6 = (struct sockaddr_in6 *)sa;
if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
/* Must be non-mapped for connectx */
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTPUTIL, EINVAL);
*error = EINVAL;
*bad_addr = 1;
return (NULL);
return (EINVAL);
}
incr = (unsigned int)sizeof(struct sockaddr_in6);
if (sa->sa_len != incr) {
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTPUTIL, EINVAL);
*error = EINVAL;
*bad_addr = 1;
return (NULL);
return (EINVAL);
}
(*num_v6) += 1;
break;
}
#endif
default:
*totaddr = i;
incr = 0;
/* we are done */
break;
return (EINVAL);
}
if (i == *totaddr) {
break;
if ((at + incr) > limit) {
return (EINVAL);
}
SCTP_INP_INCR_REF(inp);
stcb = sctp_findassociation_ep_addr(&inp, sa, NULL, NULL, NULL);
if (stcb != NULL) {
/* Already have or am bring up an association */
return (stcb);
SCTP_TCB_UNLOCK(stcb);
return (EALREADY);
} else {
SCTP_INP_DECR_REF(inp);
}
if ((at + incr) > limit) {
*totaddr = i;
break;
}
at += incr;
sa = (struct sockaddr *)((caddr_t)sa + incr);
}
return ((struct sctp_tcb *)NULL);
return (0);
}
/*

View file

@ -211,10 +211,9 @@ int
sctp_connectx_helper_add(struct sctp_tcb *stcb, struct sockaddr *addr,
int totaddr, int *error);
struct sctp_tcb *
sctp_connectx_helper_find(struct sctp_inpcb *inp, struct sockaddr *addr,
unsigned int *totaddr, unsigned int *num_v4, unsigned int *num_v6,
int *error, unsigned int limit, int *bad_addr);
int
sctp_connectx_helper_find(struct sctp_inpcb *, struct sockaddr *,
unsigned int, unsigned int *, unsigned int *, unsigned int);
int sctp_is_there_an_abort_here(struct mbuf *, int, uint32_t *);
#ifdef INET6