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
