From: Neil Horman <nhorman@tuxdriver.com>
Subject: [PATCH] e1000e: enhance frame fragment detection
References: bnc#567376, CVE-2009-4538

A security discussion was recently given:
http://events.ccc.de/congress/2009/Fahrplan//events/3596.en.html And a patch
that I submitted awhile back was brought up.  Apparently some of their testing
revealed that they were able to force a buffer fragment in e1000e in which the
trailing fragment was greater than 4 bytes.  As a result the fragment check I
introduced failed to detect the fragement and a partial invalid frame was
passed up into the network stack.  I've written this patch to correct it.  I'm
in the process of testing it now, but it makes good logical sense to me.
Effectively it maintains a per-adapter state variable which detects a non-EOP
frame, and discards it and subsequent non-EOP frames leading up to _and_
_including_ the next positive-EOP frame (as it is by definition the last
fragment).  This should prevent any and all partial frames from entering the
network stack from e1000e

Signed-off-by: Neil Horman <nhorman@tuxdriver.com>
Signed-off-by: Brandon Philips <bphilips@suse.de>
---
 drivers/net/e1000e/e1000.h  |    3 ++-
 drivers/net/e1000e/netdev.c |   13 +++++++++++--
 2 files changed, 13 insertions(+), 3 deletions(-)

Index: linux-2.6.31-openSUSE-11.2/drivers/net/e1000e/e1000.h
===================================================================
--- linux-2.6.31-openSUSE-11.2.orig/drivers/net/e1000e/e1000.h
+++ linux-2.6.31-openSUSE-11.2/drivers/net/e1000e/e1000.h
@@ -412,7 +412,8 @@ struct e1000_info {
 enum e1000_state_t {
 	__E1000_TESTING,
 	__E1000_RESETTING,
-	__E1000_DOWN
+	__E1000_DOWN,
+	__E1000_DISCARDING
 };
 
 enum latency_range {
Index: linux-2.6.31-openSUSE-11.2/drivers/net/e1000e/netdev.c
===================================================================
--- linux-2.6.31-openSUSE-11.2.orig/drivers/net/e1000e/netdev.c
+++ linux-2.6.31-openSUSE-11.2/drivers/net/e1000e/netdev.c
@@ -483,12 +483,21 @@ static bool e1000_clean_rx_irq(struct e1
 		length = le16_to_cpu(rx_desc->length);
 
 		/* !EOP means multiple descriptors were used to store a single
-		 * packet, also make sure the frame isn't just CRC only */
-		if (!(status & E1000_RXD_STAT_EOP) || (length <= 4)) {
+		 * packet, if thats the case we need to toss it.  In fact, we
+		 * to toss every packet with the EOP bit clear and the next
+		 * frame that _does_ have the EOP bit set, as it is by
+		 * definition only a frame fragment
+		 */
+		if (unlikely(!(status & E1000_RXD_STAT_EOP)))
+			set_bit(__E1000_DISCARDING, &adapter->state);
+
+		if (test_bit(__E1000_DISCARDING, &adapter->state)) {
 			/* All receives must fit into a single buffer */
 			e_dbg("Receive packet consumed multiple buffers\n");
 			/* recycle */
 			buffer_info->skb = skb;
+			if (status & E1000_RXD_STAT_EOP)
+			       clear_bit(__E1000_DISCARDING, &adapter->state);
 			goto next_desc;
 		}
 










































































 
