summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWei Yongjun <yjwei@cn.fujitsu.com>2008-09-30 05:32:24 -0700
committerDavid S. Miller <davem@davemloft.net>2008-09-30 05:32:24 -0700
commitba0166708ef4da7eeb61dd92bbba4d5a749d6561 (patch)
tree0e28c1d17b67d24125df4f05cbcca94c7e90ccd3
parent8b122efd13a227d35d5ca242561770db1b5e3658 (diff)
sctp: Fix kernel panic while process protocol violation parameter
Since call to function sctp_sf_abort_violation() need paramter 'arg' with 'struct sctp_chunk' type, it will read the chunk type and chunk length from the chunk_hdr member of chunk. But call to sctp_sf_violation_paramlen() always with 'struct sctp_paramhdr' type's parameter, it will be passed to sctp_sf_abort_violation(). This may cause kernel panic. sctp_sf_violation_paramlen() |-- sctp_sf_abort_violation() |-- sctp_make_abort_violation() This patch fixed this problem. This patch also fix two place which called sctp_sf_violation_paramlen() with wrong paramter type. Signed-off-by: Wei Yongjun <yjwei@cn.fujitsu.com> Signed-off-by: Vlad Yasevich <vladislav.yasevich@hp.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--include/net/sctp/sm.h3
-rw-r--r--net/sctp/sm_make_chunk.c37
-rw-r--r--net/sctp/sm_statefuns.c48
3 files changed, 64 insertions, 24 deletions
diff --git a/include/net/sctp/sm.h b/include/net/sctp/sm.h
index 24811732bdb..029a54a0239 100644
--- a/include/net/sctp/sm.h
+++ b/include/net/sctp/sm.h
@@ -227,6 +227,9 @@ struct sctp_chunk *sctp_make_abort_violation(const struct sctp_association *,
const struct sctp_chunk *,
const __u8 *,
const size_t );
+struct sctp_chunk *sctp_make_violation_paramlen(const struct sctp_association *,
+ const struct sctp_chunk *,
+ struct sctp_paramhdr *);
struct sctp_chunk *sctp_make_heartbeat(const struct sctp_association *,
const struct sctp_transport *,
const void *payload,
diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c
index b599cbba4fb..d68869f966c 100644
--- a/net/sctp/sm_make_chunk.c
+++ b/net/sctp/sm_make_chunk.c
@@ -1012,6 +1012,29 @@ end:
return retval;
}
+struct sctp_chunk *sctp_make_violation_paramlen(
+ const struct sctp_association *asoc,
+ const struct sctp_chunk *chunk,
+ struct sctp_paramhdr *param)
+{
+ struct sctp_chunk *retval;
+ static const char error[] = "The following parameter had invalid length:";
+ size_t payload_len = sizeof(error) + sizeof(sctp_errhdr_t) +
+ sizeof(sctp_paramhdr_t);
+
+ retval = sctp_make_abort(asoc, chunk, payload_len);
+ if (!retval)
+ goto nodata;
+
+ sctp_init_cause(retval, SCTP_ERROR_PROTO_VIOLATION,
+ sizeof(error) + sizeof(sctp_paramhdr_t));
+ sctp_addto_chunk(retval, sizeof(error), error);
+ sctp_addto_param(retval, sizeof(sctp_paramhdr_t), param);
+
+nodata:
+ return retval;
+}
+
/* Make a HEARTBEAT chunk. */
struct sctp_chunk *sctp_make_heartbeat(const struct sctp_association *asoc,
const struct sctp_transport *transport,
@@ -1782,11 +1805,6 @@ static int sctp_process_inv_paramlength(const struct sctp_association *asoc,
const struct sctp_chunk *chunk,
struct sctp_chunk **errp)
{
- static const char error[] = "The following parameter had invalid length:";
- size_t payload_len = WORD_ROUND(sizeof(error)) +
- sizeof(sctp_paramhdr_t);
-
-
/* This is a fatal error. Any accumulated non-fatal errors are
* not reported.
*/
@@ -1794,14 +1812,7 @@ static int sctp_process_inv_paramlength(const struct sctp_association *asoc,
sctp_chunk_free(*errp);
/* Create an error chunk and fill it in with our payload. */
- *errp = sctp_make_op_error_space(asoc, chunk, payload_len);
-
- if (*errp) {
- sctp_init_cause(*errp, SCTP_ERROR_PROTO_VIOLATION,
- sizeof(error) + sizeof(sctp_paramhdr_t));
- sctp_addto_chunk(*errp, sizeof(error), error);
- sctp_addto_param(*errp, sizeof(sctp_paramhdr_t), param);
- }
+ *errp = sctp_make_violation_paramlen(asoc, chunk, param);
return 0;
}
diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c
index 8848d329aa2..7c622af2ce5 100644
--- a/net/sctp/sm_statefuns.c
+++ b/net/sctp/sm_statefuns.c
@@ -119,7 +119,7 @@ static sctp_disposition_t sctp_sf_violation_paramlen(
const struct sctp_endpoint *ep,
const struct sctp_association *asoc,
const sctp_subtype_t type,
- void *arg,
+ void *arg, void *ext,
sctp_cmd_seq_t *commands);
static sctp_disposition_t sctp_sf_violation_ctsn(
@@ -3425,7 +3425,7 @@ sctp_disposition_t sctp_sf_do_asconf(const struct sctp_endpoint *ep,
addr_param = (union sctp_addr_param *)hdr->params;
length = ntohs(addr_param->p.length);
if (length < sizeof(sctp_paramhdr_t))
- return sctp_sf_violation_paramlen(ep, asoc, type,
+ return sctp_sf_violation_paramlen(ep, asoc, type, arg,
(void *)addr_param, commands);
/* Verify the ASCONF chunk before processing it. */
@@ -3433,8 +3433,8 @@ sctp_disposition_t sctp_sf_do_asconf(const struct sctp_endpoint *ep,
(sctp_paramhdr_t *)((void *)addr_param + length),
(void *)chunk->chunk_end,
&err_param))
- return sctp_sf_violation_paramlen(ep, asoc, type,
- (void *)&err_param, commands);
+ return sctp_sf_violation_paramlen(ep, asoc, type, arg,
+ (void *)err_param, commands);
/* ADDIP 5.2 E1) Compare the value of the serial number to the value
* the endpoint stored in a new association variable
@@ -3542,8 +3542,8 @@ sctp_disposition_t sctp_sf_do_asconf_ack(const struct sctp_endpoint *ep,
(sctp_paramhdr_t *)addip_hdr->params,
(void *)asconf_ack->chunk_end,
&err_param))
- return sctp_sf_violation_paramlen(ep, asoc, type,
- (void *)&err_param, commands);
+ return sctp_sf_violation_paramlen(ep, asoc, type, arg,
+ (void *)err_param, commands);
if (last_asconf) {
addip_hdr = (sctp_addiphdr_t *)last_asconf->subh.addip_hdr;
@@ -4240,12 +4240,38 @@ static sctp_disposition_t sctp_sf_violation_paramlen(
const struct sctp_endpoint *ep,
const struct sctp_association *asoc,
const sctp_subtype_t type,
- void *arg,
- sctp_cmd_seq_t *commands) {
- static const char err_str[] = "The following parameter had invalid length:";
+ void *arg, void *ext,
+ sctp_cmd_seq_t *commands)
+{
+ struct sctp_chunk *chunk = arg;
+ struct sctp_paramhdr *param = ext;
+ struct sctp_chunk *abort = NULL;
- return sctp_sf_abort_violation(ep, asoc, arg, commands, err_str,
- sizeof(err_str));
+ if (sctp_auth_recv_cid(SCTP_CID_ABORT, asoc))
+ goto discard;
+
+ /* Make the abort chunk. */
+ abort = sctp_make_violation_paramlen(asoc, chunk, param);
+ if (!abort)
+ goto nomem;
+
+ sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(abort));
+ SCTP_INC_STATS(SCTP_MIB_OUTCTRLCHUNKS);
+
+ sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR,
+ SCTP_ERROR(ECONNABORTED));
+ sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED,
+ SCTP_PERR(SCTP_ERROR_PROTO_VIOLATION));
+ SCTP_DEC_STATS(SCTP_MIB_CURRESTAB);
+
+discard:
+ sctp_sf_pdiscard(ep, asoc, SCTP_ST_CHUNK(0), arg, commands);
+
+ SCTP_INC_STATS(SCTP_MIB_ABORTEDS);
+
+ return SCTP_DISPOSITION_ABORT;
+nomem:
+ return SCTP_DISPOSITION_NOMEM;
}
/* Handle a protocol violation when the peer trying to advance the