diff options
author | Herbert Xu <herbert@gondor.apana.org.au> | 2007-02-05 08:33:10 +0100 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2007-02-08 21:38:47 +0100 |
commit | ffbc61117d32dc4e768f999325ecfb2528d6b303 (patch) | |
tree | f59a235c4eb22ac5ad9ede80f814f5d6ab1e6641 /net/packet | |
parent | [PACKET]: Add optional checksum computation for recvmsg (diff) | |
download | linux-ffbc61117d32dc4e768f999325ecfb2528d6b303.tar.xz linux-ffbc61117d32dc4e768f999325ecfb2528d6b303.zip |
[PACKET]: Fix skb->cb clobbering between aux and sockaddr
Both aux data and sockaddr tries to use the same buffer which
obviously doesn't work. We just happen to have 4 bytes free in
the skb->cb if you take away the maximum length of sockaddr_ll.
That's just enough to store the one piece of info from aux data
that we can't generate at recvmsg(2) time.
This is what the following patch does.
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/packet')
-rw-r--r-- | net/packet/af_packet.c | 46 |
1 files changed, 30 insertions, 16 deletions
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 8973ea78831e..a6fa48788e8f 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -60,6 +60,7 @@ #include <linux/netdevice.h> #include <linux/if_packet.h> #include <linux/wireless.h> +#include <linux/kernel.h> #include <linux/kmod.h> #include <net/ip.h> #include <net/protocol.h> @@ -215,7 +216,15 @@ struct packet_sock { #endif }; -#define PACKET_SKB_CB(__skb) ((struct tpacket_auxdata *)((__skb)->cb)) +struct packet_skb_cb { + unsigned int origlen; + union { + struct sockaddr_pkt pkt; + struct sockaddr_ll ll; + } sa; +}; + +#define PACKET_SKB_CB(__skb) ((struct packet_skb_cb *)((__skb)->cb)) #ifdef CONFIG_PACKET_MMAP @@ -296,7 +305,7 @@ static int packet_rcv_spkt(struct sk_buff *skb, struct net_device *dev, struct /* drop conntrack reference */ nf_reset(skb); - spkt = (struct sockaddr_pkt*)skb->cb; + spkt = &PACKET_SKB_CB(skb)->sa.pkt; skb_push(skb, skb->data-skb->mac.raw); @@ -465,7 +474,6 @@ static int packet_rcv(struct sk_buff *skb, struct net_device *dev, struct packet u8 * skb_head = skb->data; int skb_len = skb->len; unsigned int snaplen, res; - struct tpacket_auxdata *aux; if (skb->pkt_type == PACKET_LOOPBACK) goto drop; @@ -516,7 +524,10 @@ static int packet_rcv(struct sk_buff *skb, struct net_device *dev, struct packet skb = nskb; } - sll = (struct sockaddr_ll*)skb->cb; + BUILD_BUG_ON(sizeof(*PACKET_SKB_CB(skb)) + MAX_ADDR_LEN - 8 > + sizeof(skb->cb)); + + sll = &PACKET_SKB_CB(skb)->sa.ll; sll->sll_family = AF_PACKET; sll->sll_hatype = dev->type; sll->sll_protocol = skb->protocol; @@ -527,14 +538,7 @@ static int packet_rcv(struct sk_buff *skb, struct net_device *dev, struct packet if (dev->hard_header_parse) sll->sll_halen = dev->hard_header_parse(skb, sll->sll_addr); - aux = PACKET_SKB_CB(skb); - aux->tp_status = TP_STATUS_USER; - if (skb->ip_summed == CHECKSUM_PARTIAL) - aux->tp_status |= TP_STATUS_CSUMNOTREADY; - aux->tp_len = skb->len; - aux->tp_snaplen = snaplen; - aux->tp_mac = 0; - aux->tp_net = skb->nh.raw - skb->data; + PACKET_SKB_CB(skb)->origlen = skb->len; if (pskb_trim(skb, snaplen)) goto drop_n_acct; @@ -1106,7 +1110,7 @@ static int packet_recvmsg(struct kiocb *iocb, struct socket *sock, * it in now. */ - sll = (struct sockaddr_ll*)skb->cb; + sll = &PACKET_SKB_CB(skb)->sa.ll; if (sock->type == SOCK_PACKET) msg->msg_namelen = sizeof(struct sockaddr_pkt); else @@ -1131,11 +1135,21 @@ static int packet_recvmsg(struct kiocb *iocb, struct socket *sock, sock_recv_timestamp(msg, sk, skb); if (msg->msg_name) - memcpy(msg->msg_name, skb->cb, msg->msg_namelen); + memcpy(msg->msg_name, &PACKET_SKB_CB(skb)->sa, + msg->msg_namelen); if (pkt_sk(sk)->auxdata) { - struct tpacket_auxdata *aux = PACKET_SKB_CB(skb); - put_cmsg(msg, SOL_PACKET, PACKET_AUXDATA, sizeof(*aux), aux); + struct tpacket_auxdata aux; + + aux.tp_status = TP_STATUS_USER; + if (skb->ip_summed == CHECKSUM_PARTIAL) + aux.tp_status |= TP_STATUS_CSUMNOTREADY; + aux.tp_len = PACKET_SKB_CB(skb)->origlen; + aux.tp_snaplen = skb->len; + aux.tp_mac = 0; + aux.tp_net = skb->nh.raw - skb->data; + + put_cmsg(msg, SOL_PACKET, PACKET_AUXDATA, sizeof(aux), &aux); } /* |