From: Neil Horman <nhorman@redhat.com> Date: Fri, 30 Apr 2010 13:57:44 -0400 Subject: [net] sctp: fix skb_over_panic w/too many unknown params Message-id: <20100430135744.GG21783@hmsreliant.think-freely.org> Patchwork-id: 4362 O-Subject: [kernel team] [RHEL5 PATCH] sctp: [CVE2010-1173] fix skb_over_panic from processing too many unknown params (bz 584658) Bugzilla: 584658 CVE: CVE-2010-1173 RH-Acked-by: Jiri Pirko <jpirko@redhat.com> RH-Acked-by: David S. Miller <davem@redhat.com> RH-Acked-by: Eugene Teo <eugene@redhat.com> Hey- RHEL5 backport of commit 5fa782c2f5ef6c2e4f04d3e228412c9b4a4c8809 to RHEL5. Fixes the skb_over_panic that arises from over-packing an sctp op error chunk as described in bz 584658, and fixes CVE2010-1173 Signed-off-by: Jarod Wilson <jarod@redhat.com> diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index 97f6fe0..a0a6196 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -721,6 +721,7 @@ int sctp_user_addto_chunk(struct sctp_chunk *chunk, int off, int len, struct iovec *data); void sctp_chunk_free(struct sctp_chunk *); void *sctp_addto_chunk(struct sctp_chunk *, int len, const void *data); +void *sctp_addto_chunk_fixed(struct sctp_chunk *, int len, const void *data); struct sctp_chunk *sctp_chunkify(struct sk_buff *, const struct sctp_association *, struct sock *); diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index 289a4fc..7b00643 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -107,7 +107,7 @@ static const struct sctp_paramhdr prsctp_param = { __constant_htons(sizeof(struct sctp_paramhdr)), }; -/* A helper to initialize to initialize an op error inside a +/* A helper to initialize an op error inside a * provided chunk, as most cause codes will be embedded inside an * abort chunk. */ @@ -128,6 +128,29 @@ void sctp_init_cause(struct sctp_chunk *chunk, __u16 cause_code, chunk->subh.err_hdr = sctp_addto_chunk(chunk, paylen, payload); } +/* A helper to initialize an op error inside a + * provided chunk, as most cause codes will be embedded inside an + * abort chunk. Differs from sctp_init_cause in that it won't oops + * if there isn't enough space in the op error chunk + */ +int sctp_init_cause_fixed(struct sctp_chunk *chunk, __be16 cause_code, + size_t paylen) +{ + sctp_errhdr_t err; + __u16 len; + + /* Cause code constants are now defined in network order. */ + err.cause = cause_code; + len = sizeof(sctp_errhdr_t) + paylen; + err.length = htons(len); + + if (skb_tailroom(chunk->skb) > len) + return -ENOSPC; + chunk->subh.err_hdr = sctp_addto_chunk_fixed(chunk, + sizeof(sctp_errhdr_t), + &err); + return 0; +} /* 3.3.2 Initiation (INIT) (1) * * This chunk is used to initiate a SCTP association between two @@ -948,6 +971,24 @@ nodata: return retval; } +/* Create an Operation Error chunk of a fixed size, + * specifically, max(asoc->pathmtu, SCTP_DEFAULT_MAXSEGMENT) + * This is a helper function to allocate an error chunk for + * for those invalid parameter codes in which we may not want + * to report all the errors, if the incomming chunk is large + */ +static inline struct sctp_chunk *sctp_make_op_error_fixed( + const struct sctp_association *asoc, + const struct sctp_chunk *chunk) +{ + size_t size = asoc ? asoc->pathmtu : 0; + + if (!size) + size = SCTP_DEFAULT_MAXSEGMENT; + + return sctp_make_op_error_space(asoc, chunk, size); +} + /* Create an Operation Error chunk. */ struct sctp_chunk *sctp_make_op_error(const struct sctp_association *asoc, const struct sctp_chunk *chunk, @@ -1149,6 +1190,18 @@ void *sctp_addto_chunk(struct sctp_chunk *chunk, int len, const void *data) return target; } +/* Append bytes to the end of a chunk. Returns NULL if there isn't sufficient + * space in the chunk + */ +void *sctp_addto_chunk_fixed(struct sctp_chunk *chunk, + int len, const void *data) +{ + if (skb_tailroom(chunk->skb) > len) + return sctp_addto_chunk(chunk, len, data); + else + return NULL; +} + /* Append bytes from user space to the end of a chunk. Will panic if * chunk is not big enough. * Returns a kernel err value. @@ -1654,12 +1707,10 @@ static int sctp_process_unk_param(const struct sctp_association *asoc, * returning multiple unknown parameters. */ if (NULL == *errp) - *errp = sctp_make_op_error_space(asoc, chunk, - ntohs(chunk->chunk_hdr->length)); + *errp = sctp_make_op_error_fixed(asoc, chunk); if (*errp) - sctp_init_cause(*errp, SCTP_ERROR_UNKNOWN_PARAM, - param.v, + sctp_init_cause_fixed(*errp, SCTP_ERROR_UNKNOWN_PARAM, WORD_ROUND(ntohs(param.p->length))); break; @@ -1670,12 +1721,10 @@ static int sctp_process_unk_param(const struct sctp_association *asoc, * returning multiple unknown parameters. */ if (NULL == *errp) - *errp = sctp_make_op_error_space(asoc, chunk, - ntohs(chunk->chunk_hdr->length)); + *errp = sctp_make_op_error_fixed(asoc, chunk); if (*errp) { - sctp_init_cause(*errp, SCTP_ERROR_UNKNOWN_PARAM, - param.v, + sctp_init_cause_fixed(*errp, SCTP_ERROR_UNKNOWN_PARAM, WORD_ROUND(ntohs(param.p->length))); } else { /* If there is no memory for generating the ERROR