[SCTP]: Prevent possible infinite recursion with multiple bundled DATA. (CVE-2006-2274)
Linux Kernel Mailing List <[email protected]> Tue, 13 Jun 2006 19:59:17 GMT
| Newsgroups | gmane.linux.kernel.commits.2-4 |
|---|---|
| Message-ID | <[email protected]> |
commit ff6c55bb3febfeeac029a7ebf866bae0cbf32921 tree 1043974c1cac94b57eb62946d7488dc7542bdb24 parent 4ff1c7d9b24059c758ed4600f35da71a0b11557a author Vladislav Yasevich <[email protected]> Mon, 29 May 2006 13:32:14 -0700 committer David S. Miller <[email protected]> Mon, 29 May 2006 13:32:14 -0700 [SCTP]: Prevent possible infinite recursion with multiple bundled DATA. (CVE-2006-2274) There is a rare situation that causes lksctp to go into infinite recursion and crash the system. The trigger is a packet that contains at least the first two DATA fragments of a message bundled together. The recursion is triggered when the user data buffer is smaller that the full data message. The problem is that we clone the skb for every fragment in the message. When reassembling the full message, we try to link skbs from the "first fragment" clone using the frag_list. However, since the frag_list is shared between two clones in this rare situation, we end up setting the frag_list pointer of the second fragment to point to itself. This causes sctp_skb_pull() to potentially recurse indefinitely. Proposed solution is to make a copy of the skb when attempting to link things using frag_list. Signed-off-by: Vladislav Yasevich <[email protected]> Signed-off-by: Sridhar Samudrala <[email protected]> Signed-off-by: David S. Miller <[email protected]> net/sctp/ulpqueue.c | 27 +++++++++++++++++++++++++-- 1 files changed, 25 insertions(+), 2 deletions(-) diff --git a/net/sctp/ulpqueue.c b/net/sctp/ulpqueue.c index 28f41f8..58acc5b 100644 --- a/net/sctp/ulpqueue.c +++ b/net/sctp/ulpqueue.c @@ -273,6 +273,7 @@ static inline void sctp_ulpq_store_reasm static struct sctp_ulpevent *sctp_make_reassembled_event(struct sk_buff *f_frag, struct sk_buff *l_frag) { struct sk_buff *pos; + struct sk_buff *new = NULL; struct sctp_ulpevent *event; struct sk_buff *pnext, *last; struct sk_buff *list = skb_shinfo(f_frag)->frag_list; @@ -291,11 +292,33 @@ static struct sctp_ulpevent *sctp_make_r */ if (last) last->next = pos; - else - skb_shinfo(f_frag)->frag_list = pos; + else { + if (skb_cloned(f_frag)) { + /* This is a cloned skb, we can't just modify + * the frag_list. We need a new skb to do that. + * Instead of calling skb_unshare(), we'll do it + * ourselves since we need to delay the free. + */ + new = skb_copy(f_frag, GFP_ATOMIC); + if (!new) + return NULL; /* try again later */ + + new->sk = f_frag->sk; + + skb_shinfo(new)->frag_list = pos; + } else + skb_shinfo(f_frag)->frag_list = pos; + } /* Remove the first fragment from the reassembly queue. */ __skb_unlink(f_frag, f_frag->list); + + /* if we did unshare, then free the old skb and re-assign */ + if (new) { + kfree_skb(f_frag); + f_frag = new; + } + while (pos) { pnext = pos->next;