Fix the reporting of multiple unknown parameters in an received INIT

chunk. This also plugs an potential mbuf leak.
Thanks to Felix Weinrank for reporting this issue found by fuzz-testing
the userland stack.

MFC after:		3 days
This commit is contained in:
Michael Tuexen 2019-08-01 19:45:34 +00:00
parent fbe12f2085
commit bedf9eb987

View file

@ -4988,17 +4988,17 @@ sctp_arethere_unrecognized_parameters(struct mbuf *in_initpkt,
*/
struct sctp_paramhdr *phdr, params;
struct mbuf *mat, *op_err;
struct mbuf *mat, *m_tmp, *op_err, *op_err_last;
int at, limit, pad_needed;
uint16_t ptype, plen, padded_size;
int err_at;
*abort_processing = 0;
mat = in_initpkt;
err_at = 0;
limit = ntohs(cp->chunk_length) - sizeof(struct sctp_init_chunk);
at = param_offset;
op_err = NULL;
op_err_last = NULL;
pad_needed = 0;
SCTPDBG(SCTP_DEBUG_OUTPUT1, "Check for unrecognized param's\n");
phdr = sctp_get_next_param(mat, at, &params, sizeof(params));
while ((phdr != NULL) && ((size_t)limit >= sizeof(struct sctp_paramhdr))) {
@ -5123,6 +5123,7 @@ sctp_arethere_unrecognized_parameters(struct mbuf *in_initpkt,
*abort_processing = 1;
sctp_m_freem(op_err);
op_err = NULL;
op_err_last = NULL;
#ifdef INET6
l_len = SCTP_MIN_OVERHEAD;
#else
@ -5131,7 +5132,7 @@ sctp_arethere_unrecognized_parameters(struct mbuf *in_initpkt,
l_len += sizeof(struct sctp_chunkhdr);
l_len += sizeof(struct sctp_gen_error_cause);
op_err = sctp_get_mbuf_for_msg(l_len, 0, M_NOWAIT, 1, MT_DATA);
if (op_err) {
if (op_err != NULL) {
/*
* Pre-reserve space for IP, SCTP,
* and chunk header.
@ -5151,6 +5152,7 @@ sctp_arethere_unrecognized_parameters(struct mbuf *in_initpkt,
if (SCTP_BUF_NEXT(op_err) == NULL) {
sctp_m_freem(op_err);
op_err = NULL;
op_err_last = NULL;
}
}
return (op_err);
@ -5186,37 +5188,55 @@ sctp_arethere_unrecognized_parameters(struct mbuf *in_initpkt,
#endif
SCTP_BUF_RESV_UF(op_err, sizeof(struct sctphdr));
SCTP_BUF_RESV_UF(op_err, sizeof(struct sctp_chunkhdr));
op_err_last = op_err;
}
}
if (op_err) {
if (op_err != NULL) {
/* If we have space */
struct sctp_paramhdr s;
struct sctp_paramhdr *param;
if (err_at % 4) {
uint32_t cpthis = 0;
pad_needed = 4 - (err_at % 4);
m_copyback(op_err, err_at, pad_needed, (caddr_t)&cpthis);
err_at += pad_needed;
if (pad_needed > 0) {
op_err_last = sctp_add_pad_tombuf(op_err_last, pad_needed);
}
s.param_type = htons(SCTP_UNRECOG_PARAM);
s.param_length = htons((uint16_t)sizeof(struct sctp_paramhdr) + plen);
m_copyback(op_err, err_at, sizeof(struct sctp_paramhdr), (caddr_t)&s);
err_at += sizeof(struct sctp_paramhdr);
SCTP_BUF_NEXT(op_err) = SCTP_M_COPYM(mat, at, plen, M_NOWAIT);
if (SCTP_BUF_NEXT(op_err) == NULL) {
if (op_err_last == NULL) {
sctp_m_freem(op_err);
/*
* we are out of memory but
* we still need to have a
* look at what to do (the
* system is in trouble
* though).
*/
op_err = NULL;
op_err_last = NULL;
goto more_processing;
}
err_at += plen;
if (M_TRAILINGSPACE(op_err_last) < (int)sizeof(struct sctp_paramhdr)) {
m_tmp = sctp_get_mbuf_for_msg(sizeof(struct sctp_paramhdr), 0, M_NOWAIT, 1, MT_DATA);
if (m_tmp == NULL) {
sctp_m_freem(op_err);
op_err = NULL;
op_err_last = NULL;
goto more_processing;
}
SCTP_BUF_LEN(m_tmp) = 0;
SCTP_BUF_NEXT(m_tmp) = NULL;
SCTP_BUF_NEXT(op_err_last) = m_tmp;
op_err_last = m_tmp;
}
param = (struct sctp_paramhdr *)(mtod(op_err_last, caddr_t)+SCTP_BUF_LEN(op_err_last));
param->param_type = htons(SCTP_UNRECOG_PARAM);
param->param_length = htons((uint16_t)sizeof(struct sctp_paramhdr) + plen);
SCTP_BUF_LEN(op_err_last) += sizeof(struct sctp_paramhdr);
SCTP_BUF_NEXT(op_err_last) = SCTP_M_COPYM(mat, at, plen, M_NOWAIT);
if (SCTP_BUF_NEXT(op_err_last) == NULL) {
sctp_m_freem(op_err);
op_err = NULL;
op_err_last = NULL;
goto more_processing;
} else {
while (SCTP_BUF_NEXT(op_err_last) != NULL) {
op_err_last = SCTP_BUF_NEXT(op_err_last);
}
}
if (plen % 4 != 0) {
pad_needed = 4 - (plen % 4);
} else {
pad_needed = 0;
}
}
}
more_processing:
@ -5239,6 +5259,7 @@ invalid_size:
*abort_processing = 1;
sctp_m_freem(op_err);
op_err = NULL;
op_err_last = NULL;
if (phdr != NULL) {
struct sctp_paramhdr *param;
int l_len;