diff options
author | Cong Wang <xiyou.wangcong@gmail.com> | 2019-01-31 16:05:49 -0500 |
---|---|---|
committer | Steffen Klassert <steffen.klassert@secunet.com> | 2019-02-05 00:29:20 -0500 |
commit | f75a2804da391571563c4b6b29e7797787332673 (patch) | |
tree | ab0938d51019534539a0e2585cca20a82f8557af /include/net/xfrm.h | |
parent | 09db51241118aeb06e1c8cd393b45879ce099b36 (diff) |
xfrm: destroy xfrm_state synchronously on net exit path
xfrm_state_put() moves struct xfrm_state to the GC list
and schedules the GC work to clean it up. On net exit call
path, xfrm_state_flush() is called to clean up and
xfrm_flush_gc() is called to wait for the GC work to complete
before exit.
However, this doesn't work because one of the ->destructor(),
ipcomp_destroy(), schedules the same GC work again inside
the GC work. It is hard to wait for such a nested async
callback. This is also why syzbot still reports the following
warning:
WARNING: CPU: 1 PID: 33 at net/ipv6/xfrm6_tunnel.c:351 xfrm6_tunnel_net_exit+0x2cb/0x500 net/ipv6/xfrm6_tunnel.c:351
...
ops_exit_list.isra.0+0xb0/0x160 net/core/net_namespace.c:153
cleanup_net+0x51d/0xb10 net/core/net_namespace.c:551
process_one_work+0xd0c/0x1ce0 kernel/workqueue.c:2153
worker_thread+0x143/0x14a0 kernel/workqueue.c:2296
kthread+0x357/0x430 kernel/kthread.c:246
ret_from_fork+0x3a/0x50 arch/x86/entry/entry_64.S:352
In fact, it is perfectly fine to bypass GC and destroy xfrm_state
synchronously on net exit call path, because it is in process context
and doesn't need a work struct to do any blocking work.
This patch introduces xfrm_state_put_sync() which simply bypasses
GC, and lets its callers to decide whether to use this synchronous
version. On net exit path, xfrm_state_fini() and
xfrm6_tunnel_net_exit() use it. And, as ipcomp_destroy() itself is
blocking, it can use xfrm_state_put_sync() directly too.
Also rename xfrm_state_gc_destroy() to ___xfrm_state_destroy() to
reflect this change.
Fixes: b48c05ab5d32 ("xfrm: Fix warning in xfrm6_tunnel_net_exit.")
Reported-and-tested-by: syzbot+e9aebef558e3ed673934@syzkaller.appspotmail.com
Cc: Steffen Klassert <steffen.klassert@secunet.com>
Signed-off-by: Cong Wang <xiyou.wangcong@gmail.com>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
Diffstat (limited to 'include/net/xfrm.h')
-rw-r--r-- | include/net/xfrm.h | 12 |
1 files changed, 9 insertions, 3 deletions
diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 7298a53b9702..85386becbaea 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h | |||
@@ -853,7 +853,7 @@ static inline void xfrm_pols_put(struct xfrm_policy **pols, int npols) | |||
853 | xfrm_pol_put(pols[i]); | 853 | xfrm_pol_put(pols[i]); |
854 | } | 854 | } |
855 | 855 | ||
856 | void __xfrm_state_destroy(struct xfrm_state *); | 856 | void __xfrm_state_destroy(struct xfrm_state *, bool); |
857 | 857 | ||
858 | static inline void __xfrm_state_put(struct xfrm_state *x) | 858 | static inline void __xfrm_state_put(struct xfrm_state *x) |
859 | { | 859 | { |
@@ -863,7 +863,13 @@ static inline void __xfrm_state_put(struct xfrm_state *x) | |||
863 | static inline void xfrm_state_put(struct xfrm_state *x) | 863 | static inline void xfrm_state_put(struct xfrm_state *x) |
864 | { | 864 | { |
865 | if (refcount_dec_and_test(&x->refcnt)) | 865 | if (refcount_dec_and_test(&x->refcnt)) |
866 | __xfrm_state_destroy(x); | 866 | __xfrm_state_destroy(x, false); |
867 | } | ||
868 | |||
869 | static inline void xfrm_state_put_sync(struct xfrm_state *x) | ||
870 | { | ||
871 | if (refcount_dec_and_test(&x->refcnt)) | ||
872 | __xfrm_state_destroy(x, true); | ||
867 | } | 873 | } |
868 | 874 | ||
869 | static inline void xfrm_state_hold(struct xfrm_state *x) | 875 | static inline void xfrm_state_hold(struct xfrm_state *x) |
@@ -1590,7 +1596,7 @@ struct xfrmk_spdinfo { | |||
1590 | 1596 | ||
1591 | struct xfrm_state *xfrm_find_acq_byseq(struct net *net, u32 mark, u32 seq); | 1597 | struct xfrm_state *xfrm_find_acq_byseq(struct net *net, u32 mark, u32 seq); |
1592 | int xfrm_state_delete(struct xfrm_state *x); | 1598 | int xfrm_state_delete(struct xfrm_state *x); |
1593 | int xfrm_state_flush(struct net *net, u8 proto, bool task_valid); | 1599 | int xfrm_state_flush(struct net *net, u8 proto, bool task_valid, bool sync); |
1594 | int xfrm_dev_state_flush(struct net *net, struct net_device *dev, bool task_valid); | 1600 | int xfrm_dev_state_flush(struct net *net, struct net_device *dev, bool task_valid); |
1595 | void xfrm_sad_getinfo(struct net *net, struct xfrmk_sadinfo *si); | 1601 | void xfrm_sad_getinfo(struct net *net, struct xfrmk_sadinfo *si); |
1596 | void xfrm_spd_getinfo(struct net *net, struct xfrmk_spdinfo *si); | 1602 | void xfrm_spd_getinfo(struct net *net, struct xfrmk_spdinfo *si); |