diff options
author | Liping Zhang <zlpnobody@gmail.com> | 2017-04-17 09:18:56 -0400 |
---|---|---|
committer | Pablo Neira Ayuso <pablo@netfilter.org> | 2017-04-24 14:06:28 -0400 |
commit | 88be4c09d9008f9ff337cbf48c5d0f06c8f872e7 (patch) | |
tree | 58116fa74feaafe98c9abe3d18434cdff177ddd7 | |
parent | 14e567615679a9999ce6bf4f23d6c9e00f03e00e (diff) |
netfilter: ctnetlink: fix deadlock due to acquire _expect_lock twice
Currently, ctnetlink_change_conntrack is always protected by _expect_lock,
but this will cause a deadlock when deleting the helper from a conntrack,
as the _expect_lock will be acquired again by nf_ct_remove_expectations:
CPU0
----
lock(nf_conntrack_expect_lock);
lock(nf_conntrack_expect_lock);
*** DEADLOCK ***
May be due to missing lock nesting notation
2 locks held by lt-conntrack_gr/12853:
#0: (&table[i].mutex){+.+.+.}, at: [<ffffffffa05e2009>]
nfnetlink_rcv_msg+0x399/0x6a9 [nfnetlink]
#1: (nf_conntrack_expect_lock){+.....}, at: [<ffffffffa05f2c1f>]
ctnetlink_new_conntrack+0x17f/0x408 [nf_conntrack_netlink]
Call Trace:
dump_stack+0x85/0xc2
__lock_acquire+0x1608/0x1680
? ctnetlink_parse_tuple_proto+0x10f/0x1c0 [nf_conntrack_netlink]
lock_acquire+0x100/0x1f0
? nf_ct_remove_expectations+0x32/0x90 [nf_conntrack]
_raw_spin_lock_bh+0x3f/0x50
? nf_ct_remove_expectations+0x32/0x90 [nf_conntrack]
nf_ct_remove_expectations+0x32/0x90 [nf_conntrack]
ctnetlink_change_helper+0xc6/0x190 [nf_conntrack_netlink]
ctnetlink_new_conntrack+0x1b2/0x408 [nf_conntrack_netlink]
nfnetlink_rcv_msg+0x60a/0x6a9 [nfnetlink]
? nfnetlink_rcv_msg+0x1b9/0x6a9 [nfnetlink]
? nfnetlink_bind+0x1a0/0x1a0 [nfnetlink]
netlink_rcv_skb+0xa4/0xc0
nfnetlink_rcv+0x87/0x770 [nfnetlink]
Since the operations are unrelated to nf_ct_expect, so we can drop the
_expect_lock. Also note, after removing the _expect_lock protection,
another CPU may invoke nf_conntrack_helper_unregister, so we should
use rcu_read_lock to protect __nf_conntrack_helper_find invoked by
ctnetlink_change_helper.
Fixes: ca7433df3a67 ("netfilter: conntrack: seperate expect locking from nf_conntrack_lock")
Signed-off-by: Liping Zhang <zlpnobody@gmail.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
-rw-r--r-- | net/netfilter/nf_conntrack_netlink.c | 24 |
1 files changed, 12 insertions, 12 deletions
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 48c184552de0..e5f97777b1f4 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c | |||
@@ -1510,23 +1510,29 @@ static int ctnetlink_change_helper(struct nf_conn *ct, | |||
1510 | return 0; | 1510 | return 0; |
1511 | } | 1511 | } |
1512 | 1512 | ||
1513 | rcu_read_lock(); | ||
1513 | helper = __nf_conntrack_helper_find(helpname, nf_ct_l3num(ct), | 1514 | helper = __nf_conntrack_helper_find(helpname, nf_ct_l3num(ct), |
1514 | nf_ct_protonum(ct)); | 1515 | nf_ct_protonum(ct)); |
1515 | if (helper == NULL) | 1516 | if (helper == NULL) { |
1517 | rcu_read_unlock(); | ||
1516 | return -EOPNOTSUPP; | 1518 | return -EOPNOTSUPP; |
1519 | } | ||
1517 | 1520 | ||
1518 | if (help) { | 1521 | if (help) { |
1519 | if (help->helper == helper) { | 1522 | if (help->helper == helper) { |
1520 | /* update private helper data if allowed. */ | 1523 | /* update private helper data if allowed. */ |
1521 | if (helper->from_nlattr) | 1524 | if (helper->from_nlattr) |
1522 | helper->from_nlattr(helpinfo, ct); | 1525 | helper->from_nlattr(helpinfo, ct); |
1523 | return 0; | 1526 | err = 0; |
1524 | } else | 1527 | } else |
1525 | return -EBUSY; | 1528 | err = -EBUSY; |
1529 | } else { | ||
1530 | /* we cannot set a helper for an existing conntrack */ | ||
1531 | err = -EOPNOTSUPP; | ||
1526 | } | 1532 | } |
1527 | 1533 | ||
1528 | /* we cannot set a helper for an existing conntrack */ | 1534 | rcu_read_unlock(); |
1529 | return -EOPNOTSUPP; | 1535 | return err; |
1530 | } | 1536 | } |
1531 | 1537 | ||
1532 | static int ctnetlink_change_timeout(struct nf_conn *ct, | 1538 | static int ctnetlink_change_timeout(struct nf_conn *ct, |
@@ -1945,9 +1951,7 @@ static int ctnetlink_new_conntrack(struct net *net, struct sock *ctnl, | |||
1945 | err = -EEXIST; | 1951 | err = -EEXIST; |
1946 | ct = nf_ct_tuplehash_to_ctrack(h); | 1952 | ct = nf_ct_tuplehash_to_ctrack(h); |
1947 | if (!(nlh->nlmsg_flags & NLM_F_EXCL)) { | 1953 | if (!(nlh->nlmsg_flags & NLM_F_EXCL)) { |
1948 | spin_lock_bh(&nf_conntrack_expect_lock); | ||
1949 | err = ctnetlink_change_conntrack(ct, cda); | 1954 | err = ctnetlink_change_conntrack(ct, cda); |
1950 | spin_unlock_bh(&nf_conntrack_expect_lock); | ||
1951 | if (err == 0) { | 1955 | if (err == 0) { |
1952 | nf_conntrack_eventmask_report((1 << IPCT_REPLY) | | 1956 | nf_conntrack_eventmask_report((1 << IPCT_REPLY) | |
1953 | (1 << IPCT_ASSURED) | | 1957 | (1 << IPCT_ASSURED) | |
@@ -2342,11 +2346,7 @@ ctnetlink_glue_parse(const struct nlattr *attr, struct nf_conn *ct) | |||
2342 | if (ret < 0) | 2346 | if (ret < 0) |
2343 | return ret; | 2347 | return ret; |
2344 | 2348 | ||
2345 | spin_lock_bh(&nf_conntrack_expect_lock); | 2349 | return ctnetlink_glue_parse_ct((const struct nlattr **)cda, ct); |
2346 | ret = ctnetlink_glue_parse_ct((const struct nlattr **)cda, ct); | ||
2347 | spin_unlock_bh(&nf_conntrack_expect_lock); | ||
2348 | |||
2349 | return ret; | ||
2350 | } | 2350 | } |
2351 | 2351 | ||
2352 | static int ctnetlink_glue_exp_parse(const struct nlattr * const *cda, | 2352 | static int ctnetlink_glue_exp_parse(const struct nlattr * const *cda, |