UPGRADE YOUR BROWSER

We have detected your current browser version is not the latest one. Xilinx.com uses the latest web technologies to bring you the best online experience possible. Please upgrade to a Xilinx.com supported browser:Chrome, Firefox, Internet Explorer 11, Safari. Thank you!

cancel
Showing results for 
Search instead for 
Did you mean: 
Observer baetzs
Observer
10,483 Views
Registered: ‎04-07-2015

Ethernet Lite Linux Driver Bug: "skbuff: skb_over_panic"

I'm using the 3.18 mainline Kernel form the Xilinx Git master branch in combination with a microblaze system + an ethernet mac lite.

 

The kenel panics everytime it receives a malformed IP-Packet. (see attached .pcap file)

 

malformed_packet.PNG

 

Kernel Panic Log:

 

 

~ # skbuff: skb_over_panic: text:c01bea80 len:39184 put:39184 head:c709f4e0 data:c709f502 tail:0xc70a8e12 end:0xc709fb00 dev:eth0
BUG: failure at net/core/skbuff.c:100/skb_panic()!
Kernel panic - not syncing: BUG!
---[ end Kernel panic - not syncing: BUG!

 

 

Seems to me that i'm not the only one with this problem:

http://forums.xilinx.com/t5/Embedded-Linux/skb-over-panic/m-p/376681#M7201

 

After some research in the kernel sources i might found the problem which causes the kernel panic.

 

net/ethernet/xilinx/xilinx_emaclite.c : 374

 

/**
 * xemaclite_recv_data - Receive a frame
 * @drvdata:	Pointer to the Emaclite device private data
 * @data:	Address where the data is to be received
 *
 * This function is intended to be called from the interrupt context or
 * with a wrapper which waits for the receive frame to be available.
 *
 * Return:	Total number of bytes received
 */
static u16 xemaclite_recv_data(struct net_local *drvdata, u8 *data)
{
	void __iomem *addr;
	u16 length, proto_type;
	u32 reg_data;

	/* Determine the expected buffer address */
	addr = (drvdata->base_addr + drvdata->next_rx_buf_to_use);

	/* Verify which buffer has valid data */
	reg_data = __raw_readl(addr + XEL_RSR_OFFSET);

	if ((reg_data & XEL_RSR_RECV_DONE_MASK) == XEL_RSR_RECV_DONE_MASK) {
		if (drvdata->rx_ping_pong != 0)
			drvdata->next_rx_buf_to_use ^= XEL_BUFFER_OFFSET;
	} else {
		/* The instance is out of sync, try other buffer if other
		 * buffer is configured, return 0 otherwise. If the instance is
		 * out of sync, do not update the 'next_rx_buf_to_use' since it
		 * will correct on subsequent calls */
		if (drvdata->rx_ping_pong != 0)
			addr = (void __iomem __force *)((u32 __force)addr ^
							 XEL_BUFFER_OFFSET);
		else
			return 0;	/* No data was available */

		/* Verify that buffer has valid data */
		reg_data = __raw_readl(addr + XEL_RSR_OFFSET);
		if ((reg_data & XEL_RSR_RECV_DONE_MASK) !=
		     XEL_RSR_RECV_DONE_MASK)
			return 0;	/* No data was available */
	}

	/* Get the protocol type of the ethernet frame that arrived */
	proto_type = ((ntohl(__raw_readl(addr + XEL_HEADER_OFFSET +
			XEL_RXBUFF_OFFSET)) >> XEL_HEADER_SHIFT) &
			XEL_RPLR_LENGTH_MASK);

	/* Check if received ethernet frame is a raw ethernet frame
	 * or an IP packet or an ARP packet */
	if (proto_type > (ETH_FRAME_LEN + ETH_FCS_LEN)) {

		if (proto_type == ETH_P_IP) {
			length = ((ntohl(__raw_readl(addr +
					XEL_HEADER_IP_LENGTH_OFFSET +
					XEL_RXBUFF_OFFSET)) >>
					XEL_HEADER_SHIFT) &
					XEL_RPLR_LENGTH_MASK);
			length += ETH_HLEN + ETH_FCS_LEN;

		} else if (proto_type == ETH_P_ARP)
			length = XEL_ARP_PACKET_SIZE + ETH_HLEN + ETH_FCS_LEN;
		else
			/* Field contains type other than IP or ARP, use max
			 * frame size and let user parse it */
			length = ETH_FRAME_LEN + ETH_FCS_LEN;
	} else
		/* Use the length in the frame, plus the header and trailer */
		length = proto_type + ETH_HLEN + ETH_FCS_LEN;

	/* Read from the EmacLite device */
	xemaclite_aligned_read((u32 __force *) (addr + XEL_RXBUFF_OFFSET),
				data, length);

	/* Acknowledge the frame */
	reg_data = __raw_readl(addr + XEL_RSR_OFFSET);
	reg_data &= ~XEL_RSR_RECV_DONE_MASK;
	__raw_writel(reg_data, addr + XEL_RSR_OFFSET);

	return length;
}

 

 

The packet is a valid IP-Packet => length is just read out of the IP-Header:

 

length = ((ntohl(__raw_readl(addr +
	XEL_HEADER_IP_LENGTH_OFFSET +
	XEL_RXBUFF_OFFSET)) >>
	XEL_HEADER_SHIFT) &
	XEL_RPLR_LENGTH_MASK);
length += ETH_HLEN + ETH_FCS_LEN;

 

 

but there seems to be the problem, the packet length filed contains a length of 39166 bytes which doesn't fit in the allocated skb_buffer.

len = ETH_FRAME_LEN + ETH_FCS_LEN;
skb = netdev_alloc_skb(dev, len + ALIGNMENT)

 

in file:

 

drivers/net/ethernet/xilinx/xilinx_emaclite.c: 581

 

static void xemaclite_rx_handler(struct net_device *dev)
{
	struct net_local *lp = netdev_priv(dev);
	struct sk_buff *skb;
	unsigned int align;
	u32 len;

	len = ETH_FRAME_LEN + ETH_FCS_LEN;
	skb = netdev_alloc_skb(dev, len + ALIGNMENT);
	if (!skb) {
		/* Couldn't get memory. */
		dev->stats.rx_dropped++;
		dev_err(&lp->ndev->dev, "Could not allocate receive buffer\n");
		return;
	}

	/*
	 * A new skb should have the data halfword aligned, but this code is
	 * here just in case that isn't true. Calculate how many
	 * bytes we should reserve to get the data to start on a word
	 * boundary */
	align = BUFFER_ALIGN(skb->data);
	if (align)
		skb_reserve(skb, align);

	skb_reserve(skb, 2);

	len = xemaclite_recv_data(lp, (u8 *) skb->data);

	if (!len) {
		dev->stats.rx_errors++;
		dev_kfree_skb_irq(skb);
		return;
	}

	skb_put(skb, len);	/* Tell the skb how much data we got */

	skb->protocol = eth_type_trans(skb, dev);
	skb_checksum_none_assert(skb);

	dev->stats.rx_packets++;
	dev->stats.rx_bytes += len;

	if (!skb_defer_rx_timestamp(skb))
		netif_rx(skb);	/* Send the packet upstream */
}

 

In my opinion there is just a validation for the return length of xemaclite_recv missing

len = xemaclite_recv_data(lp, (u8 *) skb->data);

It would be nice if a kernel driver developer could take look on the problem, fix it and commit to the main line.

 

Regards

Steffen

0 Kudos
3 Replies
10,004 Views
Registered: ‎01-20-2012

Re: Ethernet Lite Linux Driver Bug: "skbuff: skb_over_panic"

Did you try your suggested fix?

I'm seeing the same problem at boot, during DHCP:

INIT: Entering runlevel: 5
Configuring network interfaces... udhcpc (v1.23.2) started
Sending discover...
skbuff: skb_over_panic: text:c02e49ec len:4078 put:4078 head:f5831500 data:f5831540 tail:0xf583252e end:0xf5831b40 dev:eth0
xemacps e000b000.ethernet: Set clk to 124999999 Hz
xemacps e000b000.ethernet: link up (1000/FULL)
INFO: rcu_preempt self-detected stall on CPU { 0}  (t=6000 jiffies g=4294967120 c=4294967119 q=5)
CPU: 0 PID: 0 Comm: swapper/0 Not tainted 3.15.0-xilinx #5
[<c0013f00>] (unwind_backtrace) from [<c0010570>] (show_stack+0x10/0x14)

etc.

Has anyone else got any insight into this?

0 Kudos
Observer baetzs
Observer
9,971 Views
Registered: ‎04-07-2015

Re: Ethernet Lite Linux Driver Bug: "skbuff: skb_over_panic"

Hey,

 

yes i've done a quick and dirty fix for this, see red code below. I just discharge the whole package if the length exceed the maximum ethernet frame size. This worked for me very well, but i can't guarantee that this work out for every case.

 

drivers/net/ethernet/xilinx/xilinx_emaclite.c: 401

 

static u16 xemaclite_recv_data(struct net_local *drvdata, u8 *data)
{
	void __iomem *addr;
	u16 length, proto_type;
	u32 reg_data;

	/* Determine the expected buffer address */
	addr = (drvdata->base_addr + drvdata->next_rx_buf_to_use);

	/* Verify which buffer has valid data */
	reg_data = __raw_readl(addr + XEL_RSR_OFFSET);

	if ((reg_data & XEL_RSR_RECV_DONE_MASK) == XEL_RSR_RECV_DONE_MASK) {
		if (drvdata->rx_ping_pong != 0)
			drvdata->next_rx_buf_to_use ^= XEL_BUFFER_OFFSET;
	} else {
		/* The instance is out of sync, try other buffer if other
		 * buffer is configured, return 0 otherwise. If the instance is
		 * out of sync, do not update the 'next_rx_buf_to_use' since it
		 * will correct on subsequent calls */
		if (drvdata->rx_ping_pong != 0)
			addr = (void __iomem __force *)((u32 __force)addr ^
							 XEL_BUFFER_OFFSET);
		else
			return 0;	/* No data was available */

		/* Verify that buffer has valid data */
		reg_data = __raw_readl(addr + XEL_RSR_OFFSET);
		if ((reg_data & XEL_RSR_RECV_DONE_MASK) !=
		     XEL_RSR_RECV_DONE_MASK)
			return 0;	/* No data was available */
	}

	/* Get the protocol type of the ethernet frame that arrived */
	proto_type = ((ntohl(__raw_readl(addr + XEL_HEADER_OFFSET +
			XEL_RXBUFF_OFFSET)) >> XEL_HEADER_SHIFT) &
			XEL_RPLR_LENGTH_MASK);

	/* Check if received ethernet frame is a raw ethernet frame
	 * or an IP packet or an ARP packet */
	if (proto_type > (ETH_FRAME_LEN + ETH_FCS_LEN)) {

		if (proto_type == ETH_P_IP) {
			length = ((ntohl(__raw_readl(addr +
					XEL_HEADER_IP_LENGTH_OFFSET +
					XEL_RXBUFF_OFFSET)) >>
					XEL_HEADER_SHIFT) &
					XEL_RPLR_LENGTH_MASK);
			length += ETH_HLEN + ETH_FCS_LEN;

		} else if (proto_type == ETH_P_ARP)
			length = XEL_ARP_PACKET_SIZE + ETH_HLEN + ETH_FCS_LEN;
		else
			/* Field contains type other than IP or ARP, use max
			 * frame size and let user parse it */
			length = ETH_FRAME_LEN + ETH_FCS_LEN;
	} else
		/* Use the length in the frame, plus the header and trailer */
		length = proto_type + ETH_HLEN + ETH_FCS_LEN;

	if( length > ETH_FRAME_LEN + ETH_FCS_LEN)
		return 0;

	/* Read from the EmacLite device */
	xemaclite_aligned_read((u32 __force *) (addr + XEL_RXBUFF_OFFSET),
				data, length);

	/* Acknowledge the frame */
	reg_data = __raw_readl(addr + XEL_RSR_OFFSET);
	reg_data &= ~XEL_RSR_RECV_DONE_MASK;
	__raw_writel(reg_data, addr + XEL_RSR_OFFSET);

	return length;
}

But keep in mind, that i'm using the axi_ethernet_lite and you the gigabit mac, so this fix won't work for you. Maybe you can use the idea and work out your own solution.

 

Regards

Steffen

 

0 Kudos
Highlighted
9,796 Views
Registered: ‎01-20-2012

Re: Ethernet Lite Linux Driver Bug: "skbuff: skb_over_panic"

Thanks Steffen, that did give me a bit of insight. I described my issue and solution here:

https://forums.xilinx.com/t5/Embedded-Linux/skb-over-panic-during-DHCP-2-problems-fixed/td-p/655549

 

0 Kudos