diff options
| author | David S. Miller <davem@davemloft.net> | 2019-05-28 17:22:15 -0700 |
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2019-05-28 17:22:15 -0700 |
| commit | 2e56571d820d46d9d54c1ae0e113a91d1c07814f (patch) | |
| tree | 3a2cd75dc3f1b69cae61b597b8f1f42c006757a1 /net | |
| parent | 3fb321fde22ddc3adc83e1f690b1fad9dfbd24ef (diff) | |
| parent | dc93f46bc4e00899eaf4579962cfac8cf2f9966d (diff) | |
Merge branch 'inet-frags-followup'
Eric Dumazet says:
====================
inet: frags: followup to 'inet-frags-avoid-possible-races-at-netns-dismantle'
Latest patch series ('inet-frags-avoid-possible-races-at-netns-dismantle')
brought another syzbot report shown in the third patch changelog.
While fixing the issue, I had to call inet_frags_fini() later
in IPv6 and ilowpan.
Also I believe a completion is needed to ensure proper dismantle
at module removal.
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
| -rw-r--r-- | net/ieee802154/6lowpan/reassembly.c | 2 | ||||
| -rw-r--r-- | net/ipv4/inet_fragment.c | 39 | ||||
| -rw-r--r-- | net/ipv6/reassembly.c | 2 |
3 files changed, 39 insertions, 4 deletions
diff --git a/net/ieee802154/6lowpan/reassembly.c b/net/ieee802154/6lowpan/reassembly.c index e59c3b708969..5b56f16ed86b 100644 --- a/net/ieee802154/6lowpan/reassembly.c +++ b/net/ieee802154/6lowpan/reassembly.c @@ -540,7 +540,7 @@ err_sysctl: void lowpan_net_frag_exit(void) { - inet_frags_fini(&lowpan_frags); lowpan_frags_sysctl_unregister(); unregister_pernet_subsys(&lowpan_frags_ops); + inet_frags_fini(&lowpan_frags); } diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c index 6ca9523374da..2b816f1ebbb4 100644 --- a/net/ipv4/inet_fragment.c +++ b/net/ipv4/inet_fragment.c @@ -110,14 +110,18 @@ int inet_frags_init(struct inet_frags *f) if (!f->frags_cachep) return -ENOMEM; + refcount_set(&f->refcnt, 1); + init_completion(&f->completion); return 0; } EXPORT_SYMBOL(inet_frags_init); void inet_frags_fini(struct inet_frags *f) { - /* We must wait that all inet_frag_destroy_rcu() have completed. */ - rcu_barrier(); + if (refcount_dec_and_test(&f->refcnt)) + complete(&f->completion); + + wait_for_completion(&f->completion); kmem_cache_destroy(f->frags_cachep); f->frags_cachep = NULL; @@ -149,11 +153,42 @@ static void fqdir_rwork_fn(struct work_struct *work) { struct fqdir *fqdir = container_of(to_rcu_work(work), struct fqdir, destroy_rwork); + struct inet_frags *f = fqdir->f; rhashtable_free_and_destroy(&fqdir->rhashtable, inet_frags_free_cb, NULL); + + /* We need to make sure all ongoing call_rcu(..., inet_frag_destroy_rcu) + * have completed, since they need to dereference fqdir. + * Would it not be nice to have kfree_rcu_barrier() ? :) + */ + rcu_barrier(); + + if (refcount_dec_and_test(&f->refcnt)) + complete(&f->completion); + kfree(fqdir); } +int fqdir_init(struct fqdir **fqdirp, struct inet_frags *f, struct net *net) +{ + struct fqdir *fqdir = kzalloc(sizeof(*fqdir), GFP_KERNEL); + int res; + + if (!fqdir) + return -ENOMEM; + fqdir->f = f; + fqdir->net = net; + res = rhashtable_init(&fqdir->rhashtable, &fqdir->f->rhash_params); + if (res < 0) { + kfree(fqdir); + return res; + } + refcount_inc(&f->refcnt); + *fqdirp = fqdir; + return 0; +} +EXPORT_SYMBOL(fqdir_init); + void fqdir_exit(struct fqdir *fqdir) { fqdir->high_thresh = 0; /* prevent creation of new frags */ diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c index 836ea964cf14..ff5b6d8de2c6 100644 --- a/net/ipv6/reassembly.c +++ b/net/ipv6/reassembly.c @@ -583,8 +583,8 @@ err_protocol: void ipv6_frag_exit(void) { - inet_frags_fini(&ip6_frags); ip6_frags_sysctl_unregister(); unregister_pernet_subsys(&ip6_frags_ops); inet6_del_protocol(&frag_protocol, IPPROTO_FRAGMENT); + inet_frags_fini(&ip6_frags); } |
