| From foo@baz Wed May 31 09:13:34 JST 2017 |
| From: Gao Feng <gfree.wind@vip.163.com> |
| Date: Tue, 9 May 2017 18:27:33 +0800 |
| Subject: driver: vrf: Fix one possible use-after-free issue |
| |
| From: Gao Feng <gfree.wind@vip.163.com> |
| |
| |
| [ Upstream commit 1a4a5bf52a4adb477adb075e5afce925824ad132 ] |
| |
| The current codes only deal with the case that the skb is dropped, it |
| may meet one use-after-free issue when NF_HOOK returns 0 that means |
| the skb is stolen by one netfilter rule or hook. |
| |
| When one netfilter rule or hook stoles the skb and return NF_STOLEN, |
| it means the skb is taken by the rule, and other modules should not |
| touch this skb ever. Maybe the skb is queued or freed directly by the |
| rule. |
| |
| Now uses the nf_hook instead of NF_HOOK to get the result of netfilter, |
| and check the return value of nf_hook. Only when its value equals 1, it |
| means the skb could go ahead. Or reset the skb as NULL. |
| |
| BTW, because vrf_rcv_finish is empty function, so needn't invoke it |
| even though nf_hook returns 1. But we need to modify vrf_rcv_finish |
| to deal with the NF_STOLEN case. |
| |
| There are two cases when skb is stolen. |
| 1. The skb is stolen and freed directly. |
| There is nothing we need to do, and vrf_rcv_finish isn't invoked. |
| 2. The skb is queued and reinjected again. |
| The vrf_rcv_finish would be invoked as okfn, so need to free the |
| skb in it. |
| |
| Signed-off-by: Gao Feng <gfree.wind@vip.163.com> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| drivers/net/vrf.c | 3 ++- |
| 1 file changed, 2 insertions(+), 1 deletion(-) |
| |
| --- a/drivers/net/vrf.c |
| +++ b/drivers/net/vrf.c |
| @@ -850,6 +850,7 @@ static u32 vrf_fib_table(const struct ne |
| |
| static int vrf_rcv_finish(struct net *net, struct sock *sk, struct sk_buff *skb) |
| { |
| + kfree_skb(skb); |
| return 0; |
| } |
| |
| @@ -859,7 +860,7 @@ static struct sk_buff *vrf_rcv_nfhook(u8 |
| { |
| struct net *net = dev_net(dev); |
| |
| - if (NF_HOOK(pf, hook, net, NULL, skb, dev, NULL, vrf_rcv_finish) < 0) |
| + if (nf_hook(pf, hook, net, NULL, skb, dev, NULL, vrf_rcv_finish) != 1) |
| skb = NULL; /* kfree_skb(skb) handled by nf code */ |
| |
| return skb; |