Skip to content

Commit

Permalink
MFS r353395:
Browse files Browse the repository at this point in the history
Add missing input validation. This could result in reading from
uninitialized memory.
The issue was found by OSS-Fuzz for usrsctp  and reported in
https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=17780

MFS r353396:

Cleanup sctp_asconf_error_response() and ensure that the parameter
is padded as required. This fixes the followig bug reported by
OSS-Fuzz for the usersctp stack:
https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=17790

MFS r353397:

When skipping the address parameter, take the padding into account.

MFS r353398:

Fix the adding of padding to COOKIE-ECHO chunks.

Thanks to Mark Wodrich who found this issue while fuzz testing the
usrsctp stack and reported the issue in
sctplab/usrsctp#382

MFS r353399:

Plumb an mbuf leak found by Mark Wodrich from Google by fuzz testing the
userland stack and reporting it in:
sctplab/usrsctp#396

MFS r353400:

Fix a use after free bug when removing remote addresses.
This bug was found by OSS-Fuzz and reported in
https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=18004

MFS r353401:

Plumb an mbuf leak in a code path that should not be taken. Also avoid
that this path is taken by setting the tail pointer correctly.
There is still bug related to handling unordered unfragmented messages
which were delayed in deferred handling.
This issue was found by OSS-Fuzz testing the usrsctp stack and reported
in
https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=17794

MFS r353403:

Validate length before use it, not vice versa.
r353060 should have contained this...
This fixes
https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=18070

Approved by:		re (gjb@)
  • Loading branch information
tuexen authored and fichtner committed Oct 29, 2019
1 parent ed67602 commit 30de4eb
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 32 deletions.
79 changes: 50 additions & 29 deletions sys/netinet/sctp_asconf.c
Expand Up @@ -105,42 +105,47 @@ sctp_asconf_error_response(uint32_t id, uint16_t cause, uint8_t *error_tlv,
struct mbuf *m_reply = NULL;
struct sctp_asconf_paramhdr *aph;
struct sctp_error_cause *error;
size_t buf_len;
uint16_t i, param_length, cause_length, padding_length;
uint8_t *tlv;

m_reply = sctp_get_mbuf_for_msg((sizeof(struct sctp_asconf_paramhdr) +
tlv_length +
sizeof(struct sctp_error_cause)),
0, M_NOWAIT, 1, MT_DATA);
if (error_tlv == NULL) {
tlv_length = 0;
}
cause_length = sizeof(struct sctp_error_cause) + tlv_length;
param_length = sizeof(struct sctp_asconf_paramhdr) + cause_length;
padding_length = tlv_length % 4;
if (padding_length != 0) {
padding_length = 4 - padding_length;
}
buf_len = param_length + padding_length;
if (buf_len > MLEN) {
SCTPDBG(SCTP_DEBUG_ASCONF1,
"asconf_error_response: tlv_length (%xh) too big\n",
tlv_length);
return (NULL);
}
m_reply = sctp_get_mbuf_for_msg(buf_len, 0, M_NOWAIT, 1, MT_DATA);
if (m_reply == NULL) {
SCTPDBG(SCTP_DEBUG_ASCONF1,
"asconf_error_response: couldn't get mbuf!\n");
return (NULL);
}
aph = mtod(m_reply, struct sctp_asconf_paramhdr *);
error = (struct sctp_error_cause *)(aph + 1);

aph->correlation_id = id;
aph->ph.param_type = htons(SCTP_ERROR_CAUSE_IND);
aph->ph.param_length = htons(param_length);
aph->correlation_id = id;
error = (struct sctp_error_cause *)(aph + 1);
error->code = htons(cause);
error->length = tlv_length + sizeof(struct sctp_error_cause);
aph->ph.param_length = error->length +
sizeof(struct sctp_asconf_paramhdr);

if (aph->ph.param_length > MLEN) {
SCTPDBG(SCTP_DEBUG_ASCONF1,
"asconf_error_response: tlv_length (%xh) too big\n",
tlv_length);
sctp_m_freem(m_reply); /* discard */
return (NULL);
}
error->length = htons(cause_length);
if (error_tlv != NULL) {
tlv = (uint8_t *)(error + 1);
memcpy(tlv, error_tlv, tlv_length);
for (i = 0; i < padding_length; i++) {
tlv[tlv_length + i] = 0;
}
}
SCTP_BUF_LEN(m_reply) = aph->ph.param_length;
error->length = htons(error->length);
aph->ph.param_length = htons(aph->ph.param_length);

SCTP_BUF_LEN(m_reply) = buf_len;
return (m_reply);
}

Expand Down Expand Up @@ -169,10 +174,16 @@ sctp_process_asconf_add_ip(struct sockaddr *src, struct sctp_asconf_paramhdr *ap
#endif

aparam_length = ntohs(aph->ph.param_length);
if (aparam_length < sizeof(struct sctp_asconf_paramhdr) + sizeof(struct sctp_paramhdr)) {
return (NULL);
}
ph = (struct sctp_paramhdr *)(aph + 1);
param_type = ntohs(ph->param_type);
#if defined(INET) || defined(INET6)
param_length = ntohs(ph->param_length);
if (param_length + sizeof(struct sctp_asconf_paramhdr) != aparam_length) {
return (NULL);
}
#endif
sa = &store.sa;
switch (param_type) {
Expand Down Expand Up @@ -272,7 +283,7 @@ sctp_process_asconf_add_ip(struct sockaddr *src, struct sctp_asconf_paramhdr *ap
static int
sctp_asconf_del_remote_addrs_except(struct sctp_tcb *stcb, struct sockaddr *src)
{
struct sctp_nets *src_net, *net;
struct sctp_nets *src_net, *net, *nnet;

/* make sure the source address exists as a destination net */
src_net = sctp_findnet(stcb, src);
Expand All @@ -282,17 +293,17 @@ sctp_asconf_del_remote_addrs_except(struct sctp_tcb *stcb, struct sockaddr *src)
}

/* delete all destination addresses except the source */
TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) {
TAILQ_FOREACH_SAFE(net, &stcb->asoc.nets, sctp_next, nnet) {
if (net != src_net) {
/* delete this address */
sctp_remove_net(stcb, net);
SCTPDBG(SCTP_DEBUG_ASCONF1,
"asconf_del_remote_addrs_except: deleting ");
SCTPDBG_ADDR(SCTP_DEBUG_ASCONF1,
(struct sockaddr *)&net->ro._l_addr);
/* notify upper layer */
sctp_ulp_notify(SCTP_NOTIFY_ASCONF_DELETE_IP, stcb, 0,
(struct sockaddr *)&net->ro._l_addr, SCTP_SO_NOT_LOCKED);
sctp_remove_net(stcb, net);
}
}
return (0);
Expand Down Expand Up @@ -323,10 +334,16 @@ sctp_process_asconf_delete_ip(struct sockaddr *src,
#endif

aparam_length = ntohs(aph->ph.param_length);
if (aparam_length < sizeof(struct sctp_asconf_paramhdr) + sizeof(struct sctp_paramhdr)) {
return (NULL);
}
ph = (struct sctp_paramhdr *)(aph + 1);
param_type = ntohs(ph->param_type);
#if defined(INET) || defined(INET6)
param_length = ntohs(ph->param_length);
if (param_length + sizeof(struct sctp_asconf_paramhdr) != aparam_length) {
return (NULL);
}
#endif
sa = &store.sa;
switch (param_type) {
Expand Down Expand Up @@ -454,10 +471,16 @@ sctp_process_asconf_set_primary(struct sockaddr *src,
#endif

aparam_length = ntohs(aph->ph.param_length);
if (aparam_length < sizeof(struct sctp_asconf_paramhdr) + sizeof(struct sctp_paramhdr)) {
return (NULL);
}
ph = (struct sctp_paramhdr *)(aph + 1);
param_type = ntohs(ph->param_type);
#if defined(INET) || defined(INET6)
param_length = ntohs(ph->param_length);
if (param_length + sizeof(struct sctp_asconf_paramhdr) != aparam_length) {
return (NULL);
}
#endif
sa = &store.sa;
switch (param_type) {
Expand Down Expand Up @@ -676,8 +699,8 @@ sctp_handle_asconf(struct mbuf *m, unsigned int offset,
sctp_m_freem(m_ack);
return;
}
/* param_length is already validated in process_control... */
offset += ntohs(p_addr->ph.param_length); /* skip lookup addr */
/* skip lookup addr */
offset += SCTP_SIZE32(ntohs(p_addr->ph.param_length));
/* get pointer to first asconf param in ASCONF */
aph = (struct sctp_asconf_paramhdr *)sctp_m_getptr(m, offset, sizeof(struct sctp_asconf_paramhdr), (uint8_t *)&aparam_buf);
if (aph == NULL) {
Expand Down Expand Up @@ -762,8 +785,6 @@ sctp_handle_asconf(struct mbuf *m, unsigned int offset,
if (m_result != NULL) {
SCTP_BUF_NEXT(m_tail) = m_result;
m_tail = m_result;
/* update lengths, make sure it's aligned too */
SCTP_BUF_LEN(m_result) = SCTP_SIZE32(SCTP_BUF_LEN(m_result));
ack_cp->ch.chunk_length += SCTP_BUF_LEN(m_result);
/* set flag to force success reports */
error = 1;
Expand Down
6 changes: 5 additions & 1 deletion sys/netinet/sctp_indata.c
Expand Up @@ -716,6 +716,7 @@ sctp_add_to_tail_pointer(struct sctp_queued_to_read *control, struct mbuf *m, ui
}
if (control->tail_mbuf == NULL) {
/* TSNH */
sctp_m_freem(control->data);
control->data = m;
sctp_setup_tail_pointer(control);
return;
Expand Down Expand Up @@ -2119,10 +2120,13 @@ sctp_process_a_data_chunk(struct sctp_tcb *stcb, struct sctp_association *asoc,
struct mbuf *mm;

control->data = dmbuf;
control->tail_mbuf = NULL;
for (mm = control->data; mm; mm = mm->m_next) {
control->length += SCTP_BUF_LEN(mm);
if (SCTP_BUF_NEXT(mm) == NULL) {
control->tail_mbuf = mm;
}
}
control->tail_mbuf = NULL;
control->end_added = 1;
control->last_frag_seen = 1;
control->first_frag_seen = 1;
Expand Down
4 changes: 4 additions & 0 deletions sys/netinet/sctp_input.c
Expand Up @@ -465,6 +465,10 @@ sctp_process_init_ack(struct mbuf *m, int iphlen, int offset,
if (!cookie_found) {
uint16_t len;

/* Only report the missing cookie parameter */
if (op_err != NULL) {
sctp_m_freem(op_err);
}
len = (uint16_t)(sizeof(struct sctp_error_missing_param) + sizeof(uint16_t));
/* We abort with an error of missing mandatory param */
op_err = sctp_get_mbuf_for_msg(len, 0, M_NOWAIT, 1, MT_DATA);
Expand Down
3 changes: 1 addition & 2 deletions sys/netinet/sctp_output.c
Expand Up @@ -9059,8 +9059,7 @@ sctp_send_cookie_echo(struct mbuf *m,
pad = 4 - pad;
}
if (pad > 0) {
cookie = sctp_pad_lastmbuf(cookie, pad, NULL);
if (cookie == NULL) {
if (sctp_pad_lastmbuf(cookie, pad, NULL) == NULL) {
return (-8);
}
}
Expand Down

0 comments on commit 30de4eb

Please sign in to comment.