Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge branch 'l2tp-session-creation-fixes'

Guillaume Nault says:

====================
l2tp: session creation fixes

The session creation process has a few issues wrt. concurrent tunnel
deletion.

Patch #1 avoids creating sessions in tunnels that are getting removed.
This prevents races where sessions could try to take tunnel resources
that were already released.

Patch #2 removes some racy l2tp_tunnel_find() calls in session creation
callbacks. Together with path #1 it ensures that sessions can only
access tunnel resources that are guaranteed to remain valid during the
session creation process.

There are other problems with how sessions are created: pseudo-wire
specific data are set after the session is added to the tunnel. So
the session can be used, or deleted, before it has been completely
initialised. Separating session allocation from session registration
would be necessary, but we'd still have circular dependencies
preventing race-free registration. I'll consider this issue in future
series.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>

+50 -39
+29 -14
net/l2tp/l2tp_core.c
··· 329 329 struct hlist_head *g_head; 330 330 struct hlist_head *head; 331 331 struct l2tp_net *pn; 332 + int err; 332 333 333 334 head = l2tp_session_id_hash(tunnel, session->session_id); 334 335 335 336 write_lock_bh(&tunnel->hlist_lock); 337 + if (!tunnel->acpt_newsess) { 338 + err = -ENODEV; 339 + goto err_tlock; 340 + } 341 + 336 342 hlist_for_each_entry(session_walk, head, hlist) 337 - if (session_walk->session_id == session->session_id) 338 - goto exist; 343 + if (session_walk->session_id == session->session_id) { 344 + err = -EEXIST; 345 + goto err_tlock; 346 + } 339 347 340 348 if (tunnel->version == L2TP_HDR_VER_3) { 341 349 pn = l2tp_pernet(tunnel->l2tp_net); ··· 351 343 session->session_id); 352 344 353 345 spin_lock_bh(&pn->l2tp_session_hlist_lock); 354 - hlist_for_each_entry(session_walk, g_head, global_hlist) 355 - if (session_walk->session_id == session->session_id) 356 - goto exist_glob; 357 346 347 + hlist_for_each_entry(session_walk, g_head, global_hlist) 348 + if (session_walk->session_id == session->session_id) { 349 + err = -EEXIST; 350 + goto err_tlock_pnlock; 351 + } 352 + 353 + l2tp_tunnel_inc_refcount(tunnel); 354 + sock_hold(tunnel->sock); 358 355 hlist_add_head_rcu(&session->global_hlist, g_head); 356 + 359 357 spin_unlock_bh(&pn->l2tp_session_hlist_lock); 358 + } else { 359 + l2tp_tunnel_inc_refcount(tunnel); 360 + sock_hold(tunnel->sock); 360 361 } 361 362 362 363 hlist_add_head(&session->hlist, head); ··· 373 356 374 357 return 0; 375 358 376 - exist_glob: 359 + err_tlock_pnlock: 377 360 spin_unlock_bh(&pn->l2tp_session_hlist_lock); 378 - exist: 361 + err_tlock: 379 362 write_unlock_bh(&tunnel->hlist_lock); 380 363 381 - return -EEXIST; 364 + return err; 382 365 } 383 366 384 367 /* Lookup a tunnel by id ··· 1268 1251 /* Remove hooks into tunnel socket */ 1269 1252 sk->sk_destruct = tunnel->old_sk_destruct; 1270 1253 sk->sk_user_data = NULL; 1271 - tunnel->sock = NULL; 1272 1254 1273 1255 /* Remove the tunnel struct from the tunnel list */ 1274 1256 pn = l2tp_pernet(tunnel->l2tp_net); ··· 1277 1261 atomic_dec(&l2tp_tunnel_count); 1278 1262 1279 1263 l2tp_tunnel_closeall(tunnel); 1264 + 1265 + tunnel->sock = NULL; 1280 1266 l2tp_tunnel_dec_refcount(tunnel); 1281 1267 1282 1268 /* Call the original destructor */ ··· 1303 1285 tunnel->name); 1304 1286 1305 1287 write_lock_bh(&tunnel->hlist_lock); 1288 + tunnel->acpt_newsess = false; 1306 1289 for (hash = 0; hash < L2TP_HASH_SIZE; hash++) { 1307 1290 again: 1308 1291 hlist_for_each_safe(walk, tmp, &tunnel->session_hlist[hash]) { ··· 1600 1581 tunnel->magic = L2TP_TUNNEL_MAGIC; 1601 1582 sprintf(&tunnel->name[0], "tunl %u", tunnel_id); 1602 1583 rwlock_init(&tunnel->hlist_lock); 1584 + tunnel->acpt_newsess = true; 1603 1585 1604 1586 /* The net we belong to */ 1605 1587 tunnel->l2tp_net = net; ··· 1848 1828 1849 1829 return ERR_PTR(err); 1850 1830 } 1851 - 1852 - l2tp_tunnel_inc_refcount(tunnel); 1853 - 1854 - /* Ensure tunnel socket isn't deleted */ 1855 - sock_hold(tunnel->sock); 1856 1831 1857 1832 /* Ignore management session in session count value */ 1858 1833 if (session->session_id != 0)
+7 -1
net/l2tp/l2tp_core.h
··· 162 162 int magic; /* Should be L2TP_TUNNEL_MAGIC */ 163 163 struct rcu_head rcu; 164 164 rwlock_t hlist_lock; /* protect session_hlist */ 165 + bool acpt_newsess; /* Indicates whether this 166 + * tunnel accepts new sessions. 167 + * Protected by hlist_lock. 168 + */ 165 169 struct hlist_head session_hlist[L2TP_HASH_SIZE]; 166 170 /* hashed list of sessions, 167 171 * hashed by id */ ··· 201 197 }; 202 198 203 199 struct l2tp_nl_cmd_ops { 204 - int (*session_create)(struct net *net, u32 tunnel_id, u32 session_id, u32 peer_session_id, struct l2tp_session_cfg *cfg); 200 + int (*session_create)(struct net *net, struct l2tp_tunnel *tunnel, 201 + u32 session_id, u32 peer_session_id, 202 + struct l2tp_session_cfg *cfg); 205 203 int (*session_delete)(struct l2tp_session *session); 206 204 }; 207 205
+3 -8
net/l2tp/l2tp_eth.c
··· 262 262 dev->needed_headroom += session->hdr_len; 263 263 } 264 264 265 - static int l2tp_eth_create(struct net *net, u32 tunnel_id, u32 session_id, u32 peer_session_id, struct l2tp_session_cfg *cfg) 265 + static int l2tp_eth_create(struct net *net, struct l2tp_tunnel *tunnel, 266 + u32 session_id, u32 peer_session_id, 267 + struct l2tp_session_cfg *cfg) 266 268 { 267 269 unsigned char name_assign_type; 268 270 struct net_device *dev; 269 271 char name[IFNAMSIZ]; 270 - struct l2tp_tunnel *tunnel; 271 272 struct l2tp_session *session; 272 273 struct l2tp_eth *priv; 273 274 struct l2tp_eth_sess *spriv; 274 275 int rc; 275 276 struct l2tp_eth_net *pn; 276 - 277 - tunnel = l2tp_tunnel_find(net, tunnel_id); 278 - if (!tunnel) { 279 - rc = -ENODEV; 280 - goto out; 281 - } 282 277 283 278 if (cfg->ifname) { 284 279 strlcpy(name, cfg->ifname, IFNAMSIZ);
+4 -4
net/l2tp/l2tp_netlink.c
··· 643 643 break; 644 644 } 645 645 646 - ret = -EPROTONOSUPPORT; 647 - if (l2tp_nl_cmd_ops[cfg.pw_type]->session_create) 648 - ret = (*l2tp_nl_cmd_ops[cfg.pw_type]->session_create)(net, tunnel_id, 649 - session_id, peer_session_id, &cfg); 646 + ret = l2tp_nl_cmd_ops[cfg.pw_type]->session_create(net, tunnel, 647 + session_id, 648 + peer_session_id, 649 + &cfg); 650 650 651 651 if (ret >= 0) { 652 652 session = l2tp_session_get(net, tunnel, session_id, false);
+7 -12
net/l2tp/l2tp_ppp.c
··· 788 788 789 789 #ifdef CONFIG_L2TP_V3 790 790 791 - /* Called when creating sessions via the netlink interface. 792 - */ 793 - static int pppol2tp_session_create(struct net *net, u32 tunnel_id, u32 session_id, u32 peer_session_id, struct l2tp_session_cfg *cfg) 791 + /* Called when creating sessions via the netlink interface. */ 792 + static int pppol2tp_session_create(struct net *net, struct l2tp_tunnel *tunnel, 793 + u32 session_id, u32 peer_session_id, 794 + struct l2tp_session_cfg *cfg) 794 795 { 795 796 int error; 796 - struct l2tp_tunnel *tunnel; 797 797 struct l2tp_session *session; 798 798 struct pppol2tp_session *ps; 799 799 800 - tunnel = l2tp_tunnel_find(net, tunnel_id); 801 - 802 - /* Error if we can't find the tunnel */ 803 - error = -ENOENT; 804 - if (tunnel == NULL) 805 - goto out; 806 - 807 800 /* Error if tunnel socket is not prepped */ 808 - if (tunnel->sock == NULL) 801 + if (!tunnel->sock) { 802 + error = -ENOENT; 809 803 goto out; 804 + } 810 805 811 806 /* Default MTU values. */ 812 807 if (cfg->mtu == 0)