[7/7] batman-adv: Fix wrong memory access in gw_is_target

Message ID 1280697613-32001-8-git-send-email-sven.eckelmann@gmx.de (mailing list archive)
State Accepted, archived
Headers

Commit Message

Sven Eckelmann Aug. 1, 2010, 9:20 p.m. UTC
  gw_is_target tries to access the data in a udp header without checking
if there is enough data available inside the linear skb head.

Signed-off-by: Sven Eckelmann <sven.eckelmann@gmx.de>
---
 batman-adv/gateway_client.c |   26 ++++++++++++++++++++++----
 1 files changed, 22 insertions(+), 4 deletions(-)
  

Patch

diff --git a/batman-adv/gateway_client.c b/batman-adv/gateway_client.c
index f50bc41..2ab8b6b 100644
--- a/batman-adv/gateway_client.c
+++ b/batman-adv/gateway_client.c
@@ -399,6 +399,7 @@  bool gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb)
 	struct ethhdr *ethhdr;
 	struct iphdr *iphdr;
 	struct udphdr *udphdr;
+	unsigned int header_len = 0;
 
 	if (atomic_read(&bat_priv->gw_mode) != GW_MODE_CLIENT)
 		return false;
@@ -406,22 +407,39 @@  bool gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb)
 	if (!curr_gateway)
 		return false;
 
+	/* check for ethernet header */
+	if (!pskb_may_pull(skb, header_len + ETH_HLEN))
+		return false;
 	ethhdr = (struct ethhdr *)skb->data;
+	header_len += ETH_HLEN;
 
-	if (ntohs(ethhdr->h_proto) == ETH_P_8021Q)
+	/* check for initial vlan header */
+	if (ntohs(ethhdr->h_proto) == ETH_P_8021Q) {
+		if (!pskb_may_pull(skb, header_len + VLAN_HLEN))
+			return false;
 		ethhdr = (struct ethhdr *)(skb->data + VLAN_HLEN);
+		header_len += VLAN_HLEN;
+	}
 
+	/* check for ip header */
 	if (ntohs(ethhdr->h_proto) != ETH_P_IP)
 		return false;
 
-	iphdr = (struct iphdr *)(((unsigned char *)ethhdr) + ETH_HLEN);
+	if (!pskb_may_pull(skb, header_len + sizeof(struct iphdr)))
+		return false;
+	iphdr = (struct iphdr *)(skb->data + header_len);
+	header_len += iphdr->ihl * 4;
 
+	/* check for udp header */
 	if (iphdr->protocol != IPPROTO_UDP)
 		return false;
 
-	udphdr = (struct udphdr *)(((unsigned char *)iphdr) +
-						(iphdr->ihl * 4));
+	if (!pskb_may_pull(skb, header_len + sizeof(struct udphdr)))
+		return false;
+	udphdr = (struct udphdr *)(skb->data + header_len);
+	header_len += sizeof(struct udphdr);
 
+	/* check for bootp port */
 	if (ntohs(udphdr->dest) != 67)
 		return false;