trivfs_S_io_write() set skb->len without advancing skb->tail, so an IPv6
packet reached ip6_input() with skb->tail == skb->data.  ip6_input()
derives its payload length from skb->tail - skb->h.raw, so icmpv6_rcv()
got a negative length; its unsigned check passed it to csum_partial(),
crashing pfinet.

The function also hardcoded ETH_P_IP, sending IPv6 to ip_rcv().  Classify
by the version nibble.

* pfinet/tunnel.c (trivfs_S_io_write): Use skb_put() so skb->tail and
skb->len advance together, and set skb->protocol from the version nibble.

---
 pfinet/tunnel.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/pfinet/tunnel.c b/pfinet/tunnel.c
index 6732ac5..8147f2d 100644
--- a/pfinet/tunnel.c
+++ b/pfinet/tunnel.c
@@ -393,14 +393,14 @@ trivfs_S_io_write (struct trivfs_protid *cred,
 
   skb = alloc_skb (NET_IP_ALIGN + datalen, GFP_ATOMIC);
   skb_reserve(skb, NET_IP_ALIGN);
-  skb->len = datalen;
   skb->dev = &tdev->dev;
 
-  memcpy (skb->data, data, datalen);
+  memcpy (skb_put (skb, datalen), data, datalen);
 
   /* Drop it on the queue. */
   skb->mac.raw = skb->data;
-  skb->protocol = htons (ETH_P_IP);
+  skb->protocol = htons (datalen > 0 && (data[0] >> 4) == 6
+                        ? ETH_P_IPV6 : ETH_P_IP);
   netif_rx (skb);
 
   pthread_mutex_unlock (&tdev->lock);
-- 
2.47.3


Reply via email to