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(-)
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;