Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3df26787 authored by Wei Yongjun's avatar Wei Yongjun Committed by David S. Miller
Browse files

sctp: fix kernel panic with ERROR chunk containing too many error causes

If ERROR chunk is received with too many error causes in ESTABLISHED
state, the kernel get panic.

This is because sctp limit the max length of cmds to 14, but while
ERROR chunk is received, one error cause will add around 2 cmds by
sctp_add_cmd_sf(). So many error causes will fill the limit of cmds
and panic.

This patch fixed the problem.

This bug can be test by SCTP Conformance Test Suite
<http://networktest.sourceforge.net/

>.

Signed-off-by: default avatarWei Yongjun <yjwei@cn.fujitsu.com>
Signed-off-by: default avatarVlad Yasevich <vladislav.yasevich@hp.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent d1dd5247
Loading
Loading
Loading
Loading
+33 −21
Original line number Original line Diff line number Diff line
@@ -787,25 +787,36 @@ static void sctp_cmd_process_operr(sctp_cmd_seq_t *cmds,
				   struct sctp_association *asoc,
				   struct sctp_association *asoc,
				   struct sctp_chunk *chunk)
				   struct sctp_chunk *chunk)
{
{
	struct sctp_operr_chunk *operr_chunk;
	struct sctp_errhdr *err_hdr;
	struct sctp_errhdr *err_hdr;
	struct sctp_ulpevent *ev;


	operr_chunk = (struct sctp_operr_chunk *)chunk->chunk_hdr;
	while (chunk->chunk_end > chunk->skb->data) {
	err_hdr = &operr_chunk->err_hdr;
		err_hdr = (struct sctp_errhdr *)(chunk->skb->data);

		ev = sctp_ulpevent_make_remote_error(asoc, chunk, 0,
						     GFP_ATOMIC);
		if (!ev)
			return;

		sctp_ulpq_tail_event(&asoc->ulpq, ev);


		switch (err_hdr->cause) {
		switch (err_hdr->cause) {
		case SCTP_ERROR_UNKNOWN_CHUNK:
		case SCTP_ERROR_UNKNOWN_CHUNK:
		{
		{
		struct sctp_chunkhdr *unk_chunk_hdr;
			sctp_chunkhdr_t *unk_chunk_hdr;


		unk_chunk_hdr = (struct sctp_chunkhdr *)err_hdr->variable;
			unk_chunk_hdr = (sctp_chunkhdr_t *)err_hdr->variable;
			switch (unk_chunk_hdr->type) {
			switch (unk_chunk_hdr->type) {
		/* ADDIP 4.1 A9) If the peer responds to an ASCONF with an
			/* ADDIP 4.1 A9) If the peer responds to an ASCONF with
		 * ERROR chunk reporting that it did not recognized the ASCONF
			 * an ERROR chunk reporting that it did not recognized
		 * chunk type, the sender of the ASCONF MUST NOT send any
			 * the ASCONF chunk type, the sender of the ASCONF MUST
		 * further ASCONF chunks and MUST stop its T-4 timer.
			 * NOT send any further ASCONF chunks and MUST stop its
			 * T-4 timer.
			 */
			 */
			case SCTP_CID_ASCONF:
			case SCTP_CID_ASCONF:
				if (asoc->peer.asconf_capable == 0)
					break;

				asoc->peer.asconf_capable = 0;
				asoc->peer.asconf_capable = 0;
				sctp_add_cmd_sf(cmds, SCTP_CMD_TIMER_STOP,
				sctp_add_cmd_sf(cmds, SCTP_CMD_TIMER_STOP,
					SCTP_TO(SCTP_EVENT_TIMEOUT_T4_RTO));
					SCTP_TO(SCTP_EVENT_TIMEOUT_T4_RTO));
@@ -819,6 +830,7 @@ static void sctp_cmd_process_operr(sctp_cmd_seq_t *cmds,
			break;
			break;
		}
		}
	}
	}
}


/* Process variable FWDTSN chunk information. */
/* Process variable FWDTSN chunk information. */
static void sctp_cmd_process_fwdtsn(struct sctp_ulpq *ulpq,
static void sctp_cmd_process_fwdtsn(struct sctp_ulpq *ulpq,
+2 −14
Original line number Original line Diff line number Diff line
@@ -3163,7 +3163,6 @@ sctp_disposition_t sctp_sf_operr_notify(const struct sctp_endpoint *ep,
					sctp_cmd_seq_t *commands)
					sctp_cmd_seq_t *commands)
{
{
	struct sctp_chunk *chunk = arg;
	struct sctp_chunk *chunk = arg;
	struct sctp_ulpevent *ev;


	if (!sctp_vtag_verify(chunk, asoc))
	if (!sctp_vtag_verify(chunk, asoc))
		return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
		return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
@@ -3173,21 +3172,10 @@ sctp_disposition_t sctp_sf_operr_notify(const struct sctp_endpoint *ep,
		return sctp_sf_violation_chunklen(ep, asoc, type, arg,
		return sctp_sf_violation_chunklen(ep, asoc, type, arg,
						  commands);
						  commands);


	while (chunk->chunk_end > chunk->skb->data) {
		ev = sctp_ulpevent_make_remote_error(asoc, chunk, 0,
						     GFP_ATOMIC);
		if (!ev)
			goto nomem;

		sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP,
				SCTP_ULPEVENT(ev));
	sctp_add_cmd_sf(commands, SCTP_CMD_PROCESS_OPERR,
	sctp_add_cmd_sf(commands, SCTP_CMD_PROCESS_OPERR,
			SCTP_CHUNK(chunk));
			SCTP_CHUNK(chunk));
	}
	return SCTP_DISPOSITION_CONSUME;


nomem:
	return SCTP_DISPOSITION_CONSUME;
	return SCTP_DISPOSITION_NOMEM;
}
}


/*
/*