asix: Ensure asix_rx_fixup_info members are all reset

There is a risk that the members of the structure asix_rx_fixup_info
become unsynchronised leading to the possibility of a malfunction.

For example, rx->split_head was not being set to false after an
error was detected so potentially could cause a malformed 32-bit
Data header word to be formed.

Therefore add function reset_asix_rx_fixup_info() to reset all the
members of asix_rx_fixup_info so that future processing will start
with known initial conditions.

Also, if (skb->len != offset) becomes true then call
reset_asix_rx_fixup_info() so that the processing of the next URB
starts with known initial conditions. Without the call, the check
does nothing which potentially could lead to a malfunction
when the next URB is processed.

In addition, for robustness, call reset_asix_rx_fixup_info() before
every error path's "return 0". This ensures that the next URB is
processed from known initial conditions.

Signed-off-by: Dean Jenkins <Dean_Jenkins@mentor.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Dean Jenkins 2017-08-07 09:50:15 +01:00 committed by David S. Miller
parent 22889dbbd9
commit 960eb4eeaa
1 changed files with 25 additions and 9 deletions

View File

@ -75,6 +75,27 @@ void asix_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value, u16 index,
value, index, data, size); value, index, data, size);
} }
static void reset_asix_rx_fixup_info(struct asix_rx_fixup_info *rx)
{
/* Reset the variables that have a lifetime outside of
* asix_rx_fixup_internal() so that future processing starts from a
* known set of initial conditions.
*/
if (rx->ax_skb) {
/* Discard any incomplete Ethernet frame in the netdev buffer */
kfree_skb(rx->ax_skb);
rx->ax_skb = NULL;
}
/* Assume the Data header 32-bit word is at the start of the current
* or next URB socket buffer so reset all the state variables.
*/
rx->remaining = 0;
rx->split_head = false;
rx->header = 0;
}
int asix_rx_fixup_internal(struct usbnet *dev, struct sk_buff *skb, int asix_rx_fixup_internal(struct usbnet *dev, struct sk_buff *skb,
struct asix_rx_fixup_info *rx) struct asix_rx_fixup_info *rx)
{ {
@ -99,15 +120,7 @@ int asix_rx_fixup_internal(struct usbnet *dev, struct sk_buff *skb,
if (size != ((~rx->header >> 16) & 0x7ff)) { if (size != ((~rx->header >> 16) & 0x7ff)) {
netdev_err(dev->net, "asix_rx_fixup() Data Header synchronisation was lost, remaining %d\n", netdev_err(dev->net, "asix_rx_fixup() Data Header synchronisation was lost, remaining %d\n",
rx->remaining); rx->remaining);
if (rx->ax_skb) { reset_asix_rx_fixup_info(rx);
kfree_skb(rx->ax_skb);
rx->ax_skb = NULL;
/* Discard the incomplete netdev Ethernet frame
* and assume the Data header is at the start of
* the current URB socket buffer.
*/
}
rx->remaining = 0;
} }
} }
@ -139,11 +152,13 @@ int asix_rx_fixup_internal(struct usbnet *dev, struct sk_buff *skb,
if (size != ((~rx->header >> 16) & 0x7ff)) { if (size != ((~rx->header >> 16) & 0x7ff)) {
netdev_err(dev->net, "asix_rx_fixup() Bad Header Length 0x%x, offset %d\n", netdev_err(dev->net, "asix_rx_fixup() Bad Header Length 0x%x, offset %d\n",
rx->header, offset); rx->header, offset);
reset_asix_rx_fixup_info(rx);
return 0; return 0;
} }
if (size > dev->net->mtu + ETH_HLEN + VLAN_HLEN) { if (size > dev->net->mtu + ETH_HLEN + VLAN_HLEN) {
netdev_dbg(dev->net, "asix_rx_fixup() Bad RX Length %d\n", netdev_dbg(dev->net, "asix_rx_fixup() Bad RX Length %d\n",
size); size);
reset_asix_rx_fixup_info(rx);
return 0; return 0;
} }
@ -180,6 +195,7 @@ int asix_rx_fixup_internal(struct usbnet *dev, struct sk_buff *skb,
if (skb->len != offset) { if (skb->len != offset) {
netdev_err(dev->net, "asix_rx_fixup() Bad SKB Length %d, %d\n", netdev_err(dev->net, "asix_rx_fixup() Bad SKB Length %d, %d\n",
skb->len, offset); skb->len, offset);
reset_asix_rx_fixup_info(rx);
return 0; return 0;
} }