Skip to content

Commit

Permalink
MFS r352509:
Browse files Browse the repository at this point in the history
Only allow a SCTP-AUTH shared key to be updated by the application
if it is not deactivated and not used.
This avoids a use-after-free problem.

MFS r352674:

Fix the handling of invalid parameters in ASCONF chunks.
Thanks to Mark Wodrich from Google for reproting the issue in
sctplab/usrsctp#376
for the userland stack.

MFS r352675:

Cleanup the RTO calculation and perform some consistency checks
before computing the RTO.
This should fix an overflow issue reported by Felix Weinrank in
sctplab/usrsctp#375
for the userland stack and found by running a fuzz tester.

MFS r352676:

Don't hold the info lock when calling sctp_select_a_tag().
This avoids a double lock bug in the NAT colliding state processing
of SCTP. Thanks to Felix Weinrank for finding and reporting this issue in
sctplab/usrsctp#374
He found this bug using fuzz testing.

MFS r353034:

Plumb a memory leak.
Thanks to Felix Weinrank for finding this issue using fuzz testing
and reporting it for the userland stack:
sctplab/usrsctp#378

MFS r353036:

Don't use stack memory which is not initialized.
Thanks to Mark Wodrich for reporting this issue for the userland stack in
sctplab/usrsctp#380
This issue was also found for usrsctp by OSS-fuzz in
https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=17778

Approved by:		re (kib@)
  • Loading branch information
tuexen authored and fichtner committed Oct 29, 2019
1 parent b276e4f commit 6cfb164
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 61 deletions.
18 changes: 11 additions & 7 deletions sys/netinet/sctp_asconf.c
Expand Up @@ -236,6 +236,7 @@ sctp_process_asconf_add_ip(struct sockaddr *src, struct sctp_asconf_paramhdr *ap
"process_asconf_add_ip: using source addr ");
SCTPDBG_ADDR(SCTP_DEBUG_ASCONF1, src);
}
net = NULL;
/* add the address */
if (bad_address) {
m_reply = sctp_asconf_error_response(aph->correlation_id,
Expand All @@ -250,17 +251,19 @@ sctp_process_asconf_add_ip(struct sockaddr *src, struct sctp_asconf_paramhdr *ap
SCTP_CAUSE_RESOURCE_SHORTAGE, (uint8_t *)aph,
aparam_length);
} else {
/* notify upper layer */
sctp_ulp_notify(SCTP_NOTIFY_ASCONF_ADD_IP, stcb, 0, sa, SCTP_SO_NOT_LOCKED);
if (response_required) {
m_reply =
sctp_asconf_success_response(aph->correlation_id);
}
sctp_timer_start(SCTP_TIMER_TYPE_PATHMTURAISE, stcb->sctp_ep, stcb, net);
sctp_timer_start(SCTP_TIMER_TYPE_HEARTBEAT, stcb->sctp_ep,
stcb, net);
if (send_hb) {
sctp_send_hb(stcb, net, SCTP_SO_NOT_LOCKED);
if (net != NULL) {
/* notify upper layer */
sctp_ulp_notify(SCTP_NOTIFY_ASCONF_ADD_IP, stcb, 0, sa, SCTP_SO_NOT_LOCKED);
sctp_timer_start(SCTP_TIMER_TYPE_PATHMTURAISE, stcb->sctp_ep, stcb, net);
sctp_timer_start(SCTP_TIMER_TYPE_HEARTBEAT, stcb->sctp_ep,
stcb, net);
if (send_hb) {
sctp_send_hb(stcb, net, SCTP_SO_NOT_LOCKED);
}
}
}
return (m_reply);
Expand Down Expand Up @@ -703,6 +706,7 @@ sctp_handle_asconf(struct mbuf *m, unsigned int offset,
if (param_length <= sizeof(struct sctp_paramhdr)) {
SCTPDBG(SCTP_DEBUG_ASCONF1, "handle_asconf: param length (%u) too short\n", param_length);
sctp_m_freem(m_ack);
return;
}
/* get the entire parameter */
aph = (struct sctp_asconf_paramhdr *)sctp_m_getptr(m, offset, param_length, aparam_buf);
Expand Down
2 changes: 1 addition & 1 deletion sys/netinet/sctp_auth.c
Expand Up @@ -523,7 +523,7 @@ sctp_insert_sharedkey(struct sctp_keyhead *shared_keys,
} else if (new_skey->keyid == skey->keyid) {
/* replace the existing key */
/* verify this key *can* be replaced */
if ((skey->deactivated) && (skey->refcount > 1)) {
if ((skey->deactivated) || (skey->refcount > 1)) {
SCTPDBG(SCTP_DEBUG_AUTH1,
"can't replace shared key id %u\n",
new_skey->keyid);
Expand Down
46 changes: 23 additions & 23 deletions sys/netinet/sctp_indata.c
Expand Up @@ -472,6 +472,11 @@ sctp_clean_up_control(struct sctp_tcb *stcb, struct sctp_queued_to_read *control
chk->data = NULL;
sctp_free_a_chunk(stcb, chk, SCTP_SO_NOT_LOCKED);
}
sctp_free_remote_addr(control->whoFrom);
if (control->data) {
sctp_m_freem(control->data);
control->data = NULL;
}
sctp_free_a_readq(stcb, control);
}

Expand Down Expand Up @@ -3108,13 +3113,12 @@ sctp_process_segment_range(struct sctp_tcb *stcb, struct sctp_tmit_chunk **p_tp1
* update RTO too ?
*/
if (tp1->do_rtt) {
if (*rto_ok) {
tp1->whoTo->RTO =
sctp_calculate_rto(stcb,
&stcb->asoc,
tp1->whoTo,
&tp1->sent_rcv_time,
SCTP_RTT_FROM_DATA);
if (*rto_ok &&
sctp_calculate_rto(stcb,
&stcb->asoc,
tp1->whoTo,
&tp1->sent_rcv_time,
SCTP_RTT_FROM_DATA)) {
*rto_ok = 0;
}
if (tp1->whoTo->rto_needed == 0) {
Expand Down Expand Up @@ -4086,16 +4090,12 @@ sctp_express_handle_sack(struct sctp_tcb *stcb, uint32_t cumack,

/* update RTO too? */
if (tp1->do_rtt) {
if (rto_ok) {
tp1->whoTo->RTO =
/*
* sa_ignore
* NO_NULL_CHK
*/
sctp_calculate_rto(stcb,
asoc, tp1->whoTo,
&tp1->sent_rcv_time,
SCTP_RTT_FROM_DATA);
if (rto_ok &&
sctp_calculate_rto(stcb,
&stcb->asoc,
tp1->whoTo,
&tp1->sent_rcv_time,
SCTP_RTT_FROM_DATA)) {
rto_ok = 0;
}
if (tp1->whoTo->rto_needed == 0) {
Expand Down Expand Up @@ -4704,12 +4704,12 @@ sctp_handle_sack(struct mbuf *m, int offset_seg, int offset_dup,

/* update RTO too? */
if (tp1->do_rtt) {
if (rto_ok) {
tp1->whoTo->RTO =
sctp_calculate_rto(stcb,
asoc, tp1->whoTo,
&tp1->sent_rcv_time,
SCTP_RTT_FROM_DATA);
if (rto_ok &&
sctp_calculate_rto(stcb,
&stcb->asoc,
tp1->whoTo,
&tp1->sent_rcv_time,
SCTP_RTT_FROM_DATA)) {
rto_ok = 0;
}
if (tp1->whoTo->rto_needed == 0) {
Expand Down
32 changes: 16 additions & 16 deletions sys/netinet/sctp_input.c
Expand Up @@ -548,7 +548,7 @@ sctp_process_init_ack(struct mbuf *m, int iphlen, int offset,
asoc->primary_destination, SCTP_FROM_SCTP_INPUT + SCTP_LOC_3);

/* calculate the RTO */
net->RTO = sctp_calculate_rto(stcb, asoc, net, &asoc->time_entered,
sctp_calculate_rto(stcb, asoc, net, &asoc->time_entered,
SCTP_RTT_FROM_NON_DATA);
retval = sctp_send_cookie_echo(m, offset, initack_limit, stcb, net);
return (retval);
Expand Down Expand Up @@ -648,7 +648,7 @@ sctp_handle_heartbeat_ack(struct sctp_heartbeat_chunk *cp,
tv.tv_sec = cp->heartbeat.hb_info.time_value_1;
tv.tv_usec = cp->heartbeat.hb_info.time_value_2;
/* Now lets do a RTO with this */
r_net->RTO = sctp_calculate_rto(stcb, &stcb->asoc, r_net, &tv,
sctp_calculate_rto(stcb, &stcb->asoc, r_net, &tv,
SCTP_RTT_FROM_NON_DATA);
if (!(r_net->dest_state & SCTP_ADDR_REACHABLE)) {
r_net->dest_state |= SCTP_ADDR_REACHABLE;
Expand Down Expand Up @@ -703,34 +703,37 @@ static int
sctp_handle_nat_colliding_state(struct sctp_tcb *stcb)
{
/*
* return 0 means we want you to proceed with the abort non-zero
* means no abort processing
* Return 0 means we want you to proceed with the abort non-zero
* means no abort processing.
*/
uint32_t new_vtag;
struct sctpasochead *head;

if ((SCTP_GET_STATE(stcb) == SCTP_STATE_COOKIE_WAIT) ||
(SCTP_GET_STATE(stcb) == SCTP_STATE_COOKIE_ECHOED)) {
new_vtag = sctp_select_a_tag(stcb->sctp_ep, stcb->sctp_ep->sctp_lport, stcb->rport, 1);
atomic_add_int(&stcb->asoc.refcnt, 1);
SCTP_TCB_UNLOCK(stcb);
SCTP_INP_INFO_WLOCK();
SCTP_TCB_LOCK(stcb);
atomic_subtract_int(&stcb->asoc.refcnt, 1);
} else {
return (0);
}
if (SCTP_GET_STATE(stcb) == SCTP_STATE_COOKIE_WAIT) {
/* generate a new vtag and send init */
LIST_REMOVE(stcb, sctp_asocs);
stcb->asoc.my_vtag = sctp_select_a_tag(stcb->sctp_ep, stcb->sctp_ep->sctp_lport, stcb->rport, 1);
stcb->asoc.my_vtag = new_vtag;
head = &SCTP_BASE_INFO(sctp_asochash)[SCTP_PCBHASH_ASOC(stcb->asoc.my_vtag, SCTP_BASE_INFO(hashasocmark))];
/*
* put it in the bucket in the vtag hash of assoc's for the
* system
*/
LIST_INSERT_HEAD(head, stcb, sctp_asocs);
sctp_send_initiate(stcb->sctp_ep, stcb, SCTP_SO_NOT_LOCKED);
SCTP_INP_INFO_WUNLOCK();
sctp_send_initiate(stcb->sctp_ep, stcb, SCTP_SO_NOT_LOCKED);
return (1);
}
if (SCTP_GET_STATE(stcb) == SCTP_STATE_COOKIE_ECHOED) {
} else {
/*
* treat like a case where the cookie expired i.e.: - dump
* current cookie. - generate a new vtag. - resend init.
Expand All @@ -740,15 +743,15 @@ sctp_handle_nat_colliding_state(struct sctp_tcb *stcb)
SCTP_SET_STATE(stcb, SCTP_STATE_COOKIE_WAIT);
sctp_stop_all_cookie_timers(stcb);
sctp_toss_old_cookies(stcb, &stcb->asoc);
stcb->asoc.my_vtag = sctp_select_a_tag(stcb->sctp_ep, stcb->sctp_ep->sctp_lport, stcb->rport, 1);
stcb->asoc.my_vtag = new_vtag;
head = &SCTP_BASE_INFO(sctp_asochash)[SCTP_PCBHASH_ASOC(stcb->asoc.my_vtag, SCTP_BASE_INFO(hashasocmark))];
/*
* put it in the bucket in the vtag hash of assoc's for the
* system
*/
LIST_INSERT_HEAD(head, stcb, sctp_asocs);
sctp_send_initiate(stcb->sctp_ep, stcb, SCTP_SO_NOT_LOCKED);
SCTP_INP_INFO_WUNLOCK();
sctp_send_initiate(stcb->sctp_ep, stcb, SCTP_SO_NOT_LOCKED);
return (1);
}
return (0);
Expand Down Expand Up @@ -1674,8 +1677,7 @@ sctp_process_cookie_existing(struct mbuf *m, int iphlen, int offset,
old.tv_sec = cookie->time_entered.tv_sec;
old.tv_usec = cookie->time_entered.tv_usec;
net->hb_responded = 1;
net->RTO = sctp_calculate_rto(stcb, asoc, net,
&old,
sctp_calculate_rto(stcb, asoc, net, &old,
SCTP_RTT_FROM_NON_DATA);

if (stcb->asoc.sctp_autoclose_ticks &&
Expand Down Expand Up @@ -2399,8 +2401,7 @@ sctp_process_cookie_new(struct mbuf *m, int iphlen, int offset,
/* calculate the RTT and set the encaps port */
old.tv_sec = cookie->time_entered.tv_sec;
old.tv_usec = cookie->time_entered.tv_usec;
(*netp)->RTO = sctp_calculate_rto(stcb, asoc, *netp,
&old, SCTP_RTT_FROM_NON_DATA);
sctp_calculate_rto(stcb, asoc, *netp, &old, SCTP_RTT_FROM_NON_DATA);
}
/* respond with a COOKIE-ACK */
sctp_send_cookie_ack(stcb);
Expand Down Expand Up @@ -2976,8 +2977,7 @@ sctp_handle_cookie_ack(struct sctp_cookie_ack_chunk *cp SCTP_UNUSED,
SCTP_STAT_INCR_COUNTER32(sctps_activeestab);
SCTP_STAT_INCR_GAUGE32(sctps_currestab);
if (asoc->overall_error_count == 0) {
net->RTO = sctp_calculate_rto(stcb, asoc, net,
&asoc->time_entered,
sctp_calculate_rto(stcb, asoc, net, &asoc->time_entered,
SCTP_RTT_FROM_NON_DATA);
}
(void)SCTP_GETTIME_TIMEVAL(&asoc->time_entered);
Expand Down
34 changes: 21 additions & 13 deletions sys/netinet/sctputil.c
Expand Up @@ -2469,25 +2469,24 @@ sctp_mtu_size_reset(struct sctp_inpcb *inp,


/*
* given an association and starting time of the current RTT period return
* RTO in number of msecs net should point to the current network
* Given an association and starting time of the current RTT period, update
* RTO in number of msecs. net should point to the current network.
* Return 1, if an RTO update was performed, return 0 if no update was
* performed due to invalid starting point.
*/

uint32_t
int
sctp_calculate_rto(struct sctp_tcb *stcb,
struct sctp_association *asoc,
struct sctp_nets *net,
struct timeval *old,
int rtt_from_sack)
{
/*-
* given an association and the starting time of the current RTT
* period (in value1/value2) return RTO in number of msecs.
*/
struct timeval now;
uint64_t rtt_us; /* RTT in us */
int32_t rtt; /* RTT in ms */
uint32_t new_rto;
int first_measure = 0;
struct timeval now;

/************************/
/* 1. calculate new RTT */
Expand All @@ -2498,10 +2497,19 @@ sctp_calculate_rto(struct sctp_tcb *stcb,
} else {
(void)SCTP_GETTIME_TIMEVAL(&now);
}
if ((old->tv_sec > now.tv_sec) ||
((old->tv_sec == now.tv_sec) && (old->tv_sec > now.tv_sec))) {
/* The starting point is in the future. */
return (0);
}
timevalsub(&now, old);
rtt_us = (uint64_t)1000000 * (uint64_t)now.tv_sec + (uint64_t)now.tv_usec;
if (rtt_us > SCTP_RTO_UPPER_BOUND * 1000) {
/* The RTT is larger than a sane value. */
return (0);
}
/* store the current RTT in us */
net->rtt = (uint64_t)1000000 * (uint64_t)now.tv_sec +
(uint64_t)now.tv_usec;
net->rtt = rtt_us;
/* compute rtt in ms */
rtt = (int32_t)(net->rtt / 1000);
if ((asoc->cc_functions.sctp_rtt_calculated) && (rtt_from_sack == SCTP_RTT_FROM_DATA)) {
Expand Down Expand Up @@ -2533,7 +2541,7 @@ sctp_calculate_rto(struct sctp_tcb *stcb,
* Paper "Congestion Avoidance and Control", Annex A.
*
* (net->lastsa >> SCTP_RTT_SHIFT) is the srtt
* (net->lastsa >> SCTP_RTT_VAR_SHIFT) is the rttvar
* (net->lastsv >> SCTP_RTT_VAR_SHIFT) is the rttvar
*/
if (net->RTO_measured) {
rtt -= (net->lastsa >> SCTP_RTT_SHIFT);
Expand Down Expand Up @@ -2574,8 +2582,8 @@ sctp_calculate_rto(struct sctp_tcb *stcb,
if (new_rto > stcb->asoc.maxrto) {
new_rto = stcb->asoc.maxrto;
}
/* we are now returning the RTO */
return (new_rto);
net->RTO = new_rto;
return (1);
}

/*
Expand Down
2 changes: 1 addition & 1 deletion sys/netinet/sctputil.h
Expand Up @@ -133,7 +133,7 @@ uint32_t sctp_get_next_mtu(uint32_t);
void
sctp_timeout_handler(void *);

uint32_t
int
sctp_calculate_rto(struct sctp_tcb *, struct sctp_association *,
struct sctp_nets *, struct timeval *, int);

Expand Down

0 comments on commit 6cfb164

Please sign in to comment.