diff options
author | Sabrina Dubroca <sd@queasysnail.net> | 2017-09-26 10:16:43 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2017-09-26 13:24:34 -0400 |
commit | 62b982eeb4589b2e6d7c01a90590e3a4c2b2ca19 (patch) | |
tree | 49dabd03d76177db735d196ecf9883191d6ac5cc | |
parent | 36f6ee22d2d66046e369757ec6bbe1c482957ba6 (diff) |
l2tp: fix race condition in l2tp_tunnel_delete
If we try to delete the same tunnel twice, the first delete operation
does a lookup (l2tp_tunnel_get), finds the tunnel, calls
l2tp_tunnel_delete, which queues it for deletion by
l2tp_tunnel_del_work.
The second delete operation also finds the tunnel and calls
l2tp_tunnel_delete. If the workqueue has already fired and started
running l2tp_tunnel_del_work, then l2tp_tunnel_delete will queue the
same tunnel a second time, and try to free the socket again.
Add a dead flag to prevent firing the workqueue twice. Then we can
remove the check of queue_work's result that was meant to prevent that
race but doesn't.
Reproducer:
ip l2tp add tunnel tunnel_id 3000 peer_tunnel_id 4000 local 192.168.0.2 remote 192.168.0.1 encap udp udp_sport 5000 udp_dport 6000
ip l2tp add session name l2tp1 tunnel_id 3000 session_id 1000 peer_session_id 2000
ip link set l2tp1 up
ip l2tp del tunnel tunnel_id 3000
ip l2tp del tunnel tunnel_id 3000
Fixes: f8ccac0e4493 ("l2tp: put tunnel socket release on a workqueue")
Reported-by: Jianlin Shi <jishi@redhat.com>
Signed-off-by: Sabrina Dubroca <sd@queasysnail.net>
Acked-by: Guillaume Nault <g.nault@alphalink.fr>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | net/l2tp/l2tp_core.c | 10 | ||||
-rw-r--r-- | net/l2tp/l2tp_core.h | 5 |
2 files changed, 8 insertions, 7 deletions
diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c index d8c2a89a76e1..02d61101b108 100644 --- a/net/l2tp/l2tp_core.c +++ b/net/l2tp/l2tp_core.c | |||
@@ -1688,14 +1688,12 @@ EXPORT_SYMBOL_GPL(l2tp_tunnel_create); | |||
1688 | 1688 | ||
1689 | /* This function is used by the netlink TUNNEL_DELETE command. | 1689 | /* This function is used by the netlink TUNNEL_DELETE command. |
1690 | */ | 1690 | */ |
1691 | int l2tp_tunnel_delete(struct l2tp_tunnel *tunnel) | 1691 | void l2tp_tunnel_delete(struct l2tp_tunnel *tunnel) |
1692 | { | 1692 | { |
1693 | l2tp_tunnel_inc_refcount(tunnel); | 1693 | if (!test_and_set_bit(0, &tunnel->dead)) { |
1694 | if (false == queue_work(l2tp_wq, &tunnel->del_work)) { | 1694 | l2tp_tunnel_inc_refcount(tunnel); |
1695 | l2tp_tunnel_dec_refcount(tunnel); | 1695 | queue_work(l2tp_wq, &tunnel->del_work); |
1696 | return 1; | ||
1697 | } | 1696 | } |
1698 | return 0; | ||
1699 | } | 1697 | } |
1700 | EXPORT_SYMBOL_GPL(l2tp_tunnel_delete); | 1698 | EXPORT_SYMBOL_GPL(l2tp_tunnel_delete); |
1701 | 1699 | ||
diff --git a/net/l2tp/l2tp_core.h b/net/l2tp/l2tp_core.h index 70a12df40a5f..67c79d9b5c6c 100644 --- a/net/l2tp/l2tp_core.h +++ b/net/l2tp/l2tp_core.h | |||
@@ -161,6 +161,9 @@ struct l2tp_tunnel_cfg { | |||
161 | 161 | ||
162 | struct l2tp_tunnel { | 162 | struct l2tp_tunnel { |
163 | int magic; /* Should be L2TP_TUNNEL_MAGIC */ | 163 | int magic; /* Should be L2TP_TUNNEL_MAGIC */ |
164 | |||
165 | unsigned long dead; | ||
166 | |||
164 | struct rcu_head rcu; | 167 | struct rcu_head rcu; |
165 | rwlock_t hlist_lock; /* protect session_hlist */ | 168 | rwlock_t hlist_lock; /* protect session_hlist */ |
166 | bool acpt_newsess; /* Indicates whether this | 169 | bool acpt_newsess; /* Indicates whether this |
@@ -255,7 +258,7 @@ int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, | |||
255 | u32 peer_tunnel_id, struct l2tp_tunnel_cfg *cfg, | 258 | u32 peer_tunnel_id, struct l2tp_tunnel_cfg *cfg, |
256 | struct l2tp_tunnel **tunnelp); | 259 | struct l2tp_tunnel **tunnelp); |
257 | void l2tp_tunnel_closeall(struct l2tp_tunnel *tunnel); | 260 | void l2tp_tunnel_closeall(struct l2tp_tunnel *tunnel); |
258 | int l2tp_tunnel_delete(struct l2tp_tunnel *tunnel); | 261 | void l2tp_tunnel_delete(struct l2tp_tunnel *tunnel); |
259 | struct l2tp_session *l2tp_session_create(int priv_size, | 262 | struct l2tp_session *l2tp_session_create(int priv_size, |
260 | struct l2tp_tunnel *tunnel, | 263 | struct l2tp_tunnel *tunnel, |
261 | u32 session_id, u32 peer_session_id, | 264 | u32 session_id, u32 peer_session_id, |