Skip to content

Commit

Permalink
net: ipv4: fix drop handling in ip_list_rcv() and ip_list_rcv_finish()
Browse files Browse the repository at this point in the history
Since callees (ip_rcv_core() and ip_rcv_finish_core()) might free or steal
 the skb, we can't use the list_cut_before() method; we can't even do a
 list_del(&skb->list) in the drop case, because skb might have already been
 freed and reused.
So instead, take each skb off the source list before processing, and add it
 to the sublist afterwards if it wasn't freed or stolen.

Fixes: 5fa1273 net: ipv4: listify ip_rcv_finish
Fixes: 17266ee net: ipv4: listified version of ip_rcv
Signed-off-by: Edward Cree <ecree@solarflare.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Edward Cree authored and David S. Miller committed Jul 5, 2018
1 parent 0eaec62 commit a4ca8b7
Showing 1 changed file with 11 additions and 5 deletions.
16 changes: 11 additions & 5 deletions net/ipv4/ip_input.c
Original file line number Diff line number Diff line change
Expand Up @@ -540,24 +540,27 @@ static void ip_list_rcv_finish(struct net *net, struct sock *sk,
struct sk_buff *skb, *next;
struct list_head sublist;

INIT_LIST_HEAD(&sublist);
list_for_each_entry_safe(skb, next, head, list) {
struct dst_entry *dst;

list_del(&skb->list);
if (ip_rcv_finish_core(net, sk, skb) == NET_RX_DROP)
continue;

dst = skb_dst(skb);
if (curr_dst != dst) {
/* dispatch old sublist */
list_cut_before(&sublist, head, &skb->list);
if (!list_empty(&sublist))
ip_sublist_rcv_finish(&sublist);
/* start new sublist */
INIT_LIST_HEAD(&sublist);
curr_dst = dst;
}
list_add_tail(&skb->list, &sublist);
}
/* dispatch final sublist */
ip_sublist_rcv_finish(head);
ip_sublist_rcv_finish(&sublist);
}

static void ip_sublist_rcv(struct list_head *head, struct net_device *dev,
Expand All @@ -577,24 +580,27 @@ void ip_list_rcv(struct list_head *head, struct packet_type *pt,
struct sk_buff *skb, *next;
struct list_head sublist;

INIT_LIST_HEAD(&sublist);
list_for_each_entry_safe(skb, next, head, list) {
struct net_device *dev = skb->dev;
struct net *net = dev_net(dev);

list_del(&skb->list);
skb = ip_rcv_core(skb, net);
if (skb == NULL)
continue;

if (curr_dev != dev || curr_net != net) {
/* dispatch old sublist */
list_cut_before(&sublist, head, &skb->list);
if (!list_empty(&sublist))
ip_sublist_rcv(&sublist, dev, net);
ip_sublist_rcv(&sublist, curr_dev, curr_net);
/* start new sublist */
INIT_LIST_HEAD(&sublist);
curr_dev = dev;
curr_net = net;
}
list_add_tail(&skb->list, &sublist);
}
/* dispatch final sublist */
ip_sublist_rcv(head, curr_dev, curr_net);
ip_sublist_rcv(&sublist, curr_dev, curr_net);
}

0 comments on commit a4ca8b7

Please sign in to comment.