Sophie

Sophie

distrib > CentOS > 5 > x86_64 > by-pkgid > ea32411352494358b8d75a78402a4713 > files > 3168

kernel-2.6.18-238.19.1.el5.centos.plus.src.rpm

From: Neil Horman <nhorman@redhat.com>
Date: Mon, 3 Jan 2011 19:44:19 -0500
Subject: [net] sctp: fix panic from bad socket lock on icmp error
Message-id: <20110103194419.GB13951@hmsreliant.think-freely.org>
Patchwork-id: 4534
O-Subject: [kernel team] [RHEL 5.7 PATCH] sctp: Fix panic resulting from bad
 locking of socket on icmp error (bz 665477)
Bugzilla: 665477
CVE: CVE-2010-4526
RH-Acked-by: David S. Miller <davem@redhat.com>
RH-Acked-by: Herbert Xu <herbert.xu@redhat.com>
RH-Acked-by: Eugene Teo <eugene@redhat.com>

Hey-
	Backport of upstream commit 50b5d6ad63821cea324a5a7a19854d4de1a0a819,
which fixes an sctp panic resulting from freeing an association on a potentially
locked socket when an icmp dest unreach error is received.  Tested by myself to
fix the problem.

Brew Status:
https://brewweb.devel.redhat.com/taskinfo?taskID=3003869

Signed-off-by: Jarod Wilson <jarod@redhat.com>

diff --git a/include/net/sctp/sm.h b/include/net/sctp/sm.h
index c483e9a..a382d73 100644
--- a/include/net/sctp/sm.h
+++ b/include/net/sctp/sm.h
@@ -268,6 +268,7 @@ int sctp_do_sm(sctp_event_t event_type, sctp_subtype_t subtype,
 /* 2nd level prototypes */
 void sctp_generate_t3_rtx_event(unsigned long peer);
 void sctp_generate_heartbeat_event(unsigned long peer);
+void sctp_generate_proto_unreach_event(unsigned long peer);
 
 void sctp_ootb_pkt_free(struct sctp_packet *);
 
diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h
index a0a6196..70f9157 100644
--- a/include/net/sctp/structs.h
+++ b/include/net/sctp/structs.h
@@ -938,6 +938,9 @@ struct sctp_transport {
 	/* Heartbeat timer is per destination. */
 	struct timer_list hb_timer;
 
+	/* Timer to handle ICMP proto unreachable envets */
+	struct timer_list proto_unreach_timer;
+
 	/* Since we're using per-destination retransmission timers
 	 * (see above), we're also using per-destination "transmitted"
 	 * queues.  This probably ought to be a private struct
diff --git a/net/sctp/input.c b/net/sctp/input.c
index 2060bbe..2a4ba84 100644
--- a/net/sctp/input.c
+++ b/net/sctp/input.c
@@ -423,11 +423,25 @@ void sctp_icmp_proto_unreachable(struct sock *sk,
 {
 	SCTP_DEBUG_PRINTK("%s\n",  __FUNCTION__);
 
-	sctp_do_sm(SCTP_EVENT_T_OTHER,
-		   SCTP_ST_OTHER(SCTP_EVENT_ICMP_PROTO_UNREACH),
-		   asoc->state, asoc->ep, asoc, t,
-		   GFP_ATOMIC);
+	if (sock_owned_by_user(sk)) {
+		if (timer_pending(&t->proto_unreach_timer))
+			return;
+		else {
+			if (!mod_timer(&t->proto_unreach_timer,
+						jiffies + (HZ/20)))
+				sctp_association_hold(asoc);
+		}
+			
+	} else {
+		if (timer_pending(&t->proto_unreach_timer) &&
+		    del_timer(&t->proto_unreach_timer))
+			sctp_association_put(asoc);
 
+		sctp_do_sm(SCTP_EVENT_T_OTHER,
+			   SCTP_ST_OTHER(SCTP_EVENT_ICMP_PROTO_UNREACH),
+			   asoc->state, asoc->ep, asoc, t,
+			   GFP_ATOMIC);
+	}
 }
 
 /* Common lookup code for icmp/icmpv6 error handler. */
diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c
index bf17c73..d436a00 100644
--- a/net/sctp/sm_sideeffect.c
+++ b/net/sctp/sm_sideeffect.c
@@ -388,6 +388,41 @@ out_unlock:
 	sctp_transport_put(transport);
 }
 
+/* Handle the timeout of the ICMP protocol unreachable timer.  Trigger
+ * the correct state machine transition that will close the association.
+ */
+void sctp_generate_proto_unreach_event(unsigned long data)
+{
+	struct sctp_transport *transport = (struct sctp_transport *) data;
+	struct sctp_association *asoc = transport->asoc;
+	
+	sctp_bh_lock_sock(asoc->base.sk);
+	if (sock_owned_by_user(asoc->base.sk)) {
+		SCTP_DEBUG_PRINTK("%s:Sock is busy.\n", __func__);
+
+		/* Try again later.  */
+		if (!mod_timer(&transport->proto_unreach_timer,
+				jiffies + (HZ/20)))
+			sctp_association_hold(asoc);
+		goto out_unlock;
+	}
+
+	/* Is this structure just waiting around for us to actually
+	 * get destroyed?
+	 */
+	if (asoc->base.dead)
+		goto out_unlock;
+
+	sctp_do_sm(SCTP_EVENT_T_OTHER,
+		   SCTP_ST_OTHER(SCTP_EVENT_ICMP_PROTO_UNREACH),
+		   asoc->state, asoc->ep, asoc, transport, GFP_ATOMIC);
+
+out_unlock:
+	sctp_bh_unlock_sock(asoc->base.sk);
+	sctp_association_put(asoc);
+}
+
+
 /* Inject a SACK Timeout event into the state machine.  */
 static void sctp_generate_sack_event(unsigned long data)
 {
diff --git a/net/sctp/transport.c b/net/sctp/transport.c
index 2763aa9..1d49698 100644
--- a/net/sctp/transport.c
+++ b/net/sctp/transport.c
@@ -109,6 +109,10 @@ static struct sctp_transport *sctp_transport_init(struct sctp_transport *peer,
 	peer->hb_timer.function = sctp_generate_heartbeat_event;
 	peer->hb_timer.data = (unsigned long)peer;
 
+	init_timer(&peer->proto_unreach_timer);
+        peer->proto_unreach_timer.function = sctp_generate_proto_unreach_event;
+	peer->proto_unreach_timer.data = (unsigned long)peer;
+
 	/* Initialize the 64-bit random nonce sent with heartbeat. */
 	get_random_bytes(&peer->hb_nonce, sizeof(peer->hb_nonce));