aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv4
diff options
context:
space:
mode:
authorWANG Cong <xiyou.wangcong@gmail.com>2015-11-03 18:41:16 -0500
committerDavid S. Miller <davem@davemloft.net>2015-11-04 21:29:59 -0500
commit87e9f0315952b0dd8b5e51ba04beda03efc009d9 (patch)
tree0e7621915ad9824230e0a33d1027f6c7d8e35e36 /net/ipv4
parent4ee3bd4a8c7463cdef0b82ebc33fc94a9170a7e0 (diff)
ipv4: fix a potential deadlock in mcast getsockopt() path
Sasha reported the following lockdep warning: Possible unsafe locking scenario: CPU0 CPU1 ---- ---- lock(sk_lock-AF_INET); lock(rtnl_mutex); lock(sk_lock-AF_INET); lock(rtnl_mutex); This is due to that for IP_MSFILTER and MCAST_MSFILTER, we take rtnl lock before the socket lock in setsockopt() path, but take the socket lock before rtnl lock in getsockopt() path. All the rest optnames are setsockopt()-only. Fix this by aligning the getsockopt() path with the setsockopt() path, so that all mcast socket path would be locked in the same order. Note, IPv6 part is different where rtnl lock is not held. Fixes: 54ff9ef36bdf ("ipv4, ipv6: kill ip_mc_{join, leave}_group and ipv6_sock_mc_{join, drop}") Reported-by: Sasha Levin <sasha.levin@oracle.com> Cc: Marcelo Ricardo Leitner <marcelo.leitner@gmail.com> Signed-off-by: Cong Wang <xiyou.wangcong@gmail.com> Reviewed-by: Marcelo Ricardo Leitner <marcelo.leitner@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4')
-rw-r--r--net/ipv4/igmp.c12
-rw-r--r--net/ipv4/ip_sockglue.c45
2 files changed, 34 insertions, 23 deletions
diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c
index 64aaf3522a59..6baf36e11808 100644
--- a/net/ipv4/igmp.c
+++ b/net/ipv4/igmp.c
@@ -2392,11 +2392,11 @@ int ip_mc_msfget(struct sock *sk, struct ip_msfilter *msf,
2392 struct ip_sf_socklist *psl; 2392 struct ip_sf_socklist *psl;
2393 struct net *net = sock_net(sk); 2393 struct net *net = sock_net(sk);
2394 2394
2395 ASSERT_RTNL();
2396
2395 if (!ipv4_is_multicast(addr)) 2397 if (!ipv4_is_multicast(addr))
2396 return -EINVAL; 2398 return -EINVAL;
2397 2399
2398 rtnl_lock();
2399
2400 imr.imr_multiaddr.s_addr = msf->imsf_multiaddr; 2400 imr.imr_multiaddr.s_addr = msf->imsf_multiaddr;
2401 imr.imr_address.s_addr = msf->imsf_interface; 2401 imr.imr_address.s_addr = msf->imsf_interface;
2402 imr.imr_ifindex = 0; 2402 imr.imr_ifindex = 0;
@@ -2417,7 +2417,6 @@ int ip_mc_msfget(struct sock *sk, struct ip_msfilter *msf,
2417 goto done; 2417 goto done;
2418 msf->imsf_fmode = pmc->sfmode; 2418 msf->imsf_fmode = pmc->sfmode;
2419 psl = rtnl_dereference(pmc->sflist); 2419 psl = rtnl_dereference(pmc->sflist);
2420 rtnl_unlock();
2421 if (!psl) { 2420 if (!psl) {
2422 len = 0; 2421 len = 0;
2423 count = 0; 2422 count = 0;
@@ -2436,7 +2435,6 @@ int ip_mc_msfget(struct sock *sk, struct ip_msfilter *msf,
2436 return -EFAULT; 2435 return -EFAULT;
2437 return 0; 2436 return 0;
2438done: 2437done:
2439 rtnl_unlock();
2440 return err; 2438 return err;
2441} 2439}
2442 2440
@@ -2450,6 +2448,8 @@ int ip_mc_gsfget(struct sock *sk, struct group_filter *gsf,
2450 struct inet_sock *inet = inet_sk(sk); 2448 struct inet_sock *inet = inet_sk(sk);
2451 struct ip_sf_socklist *psl; 2449 struct ip_sf_socklist *psl;
2452 2450
2451 ASSERT_RTNL();
2452
2453 psin = (struct sockaddr_in *)&gsf->gf_group; 2453 psin = (struct sockaddr_in *)&gsf->gf_group;
2454 if (psin->sin_family != AF_INET) 2454 if (psin->sin_family != AF_INET)
2455 return -EINVAL; 2455 return -EINVAL;
@@ -2457,8 +2457,6 @@ int ip_mc_gsfget(struct sock *sk, struct group_filter *gsf,
2457 if (!ipv4_is_multicast(addr)) 2457 if (!ipv4_is_multicast(addr))
2458 return -EINVAL; 2458 return -EINVAL;
2459 2459
2460 rtnl_lock();
2461
2462 err = -EADDRNOTAVAIL; 2460 err = -EADDRNOTAVAIL;
2463 2461
2464 for_each_pmc_rtnl(inet, pmc) { 2462 for_each_pmc_rtnl(inet, pmc) {
@@ -2470,7 +2468,6 @@ int ip_mc_gsfget(struct sock *sk, struct group_filter *gsf,
2470 goto done; 2468 goto done;
2471 gsf->gf_fmode = pmc->sfmode; 2469 gsf->gf_fmode = pmc->sfmode;
2472 psl = rtnl_dereference(pmc->sflist); 2470 psl = rtnl_dereference(pmc->sflist);
2473 rtnl_unlock();
2474 count = psl ? psl->sl_count : 0; 2471 count = psl ? psl->sl_count : 0;
2475 copycount = count < gsf->gf_numsrc ? count : gsf->gf_numsrc; 2472 copycount = count < gsf->gf_numsrc ? count : gsf->gf_numsrc;
2476 gsf->gf_numsrc = count; 2473 gsf->gf_numsrc = count;
@@ -2490,7 +2487,6 @@ int ip_mc_gsfget(struct sock *sk, struct group_filter *gsf,
2490 } 2487 }
2491 return 0; 2488 return 0;
2492done: 2489done:
2493 rtnl_unlock();
2494 return err; 2490 return err;
2495} 2491}
2496 2492
diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c
index c3c359ad66e3..5f73a7c03e27 100644
--- a/net/ipv4/ip_sockglue.c
+++ b/net/ipv4/ip_sockglue.c
@@ -1251,11 +1251,22 @@ EXPORT_SYMBOL(compat_ip_setsockopt);
1251 * the _received_ ones. The set sets the _sent_ ones. 1251 * the _received_ ones. The set sets the _sent_ ones.
1252 */ 1252 */
1253 1253
1254static bool getsockopt_needs_rtnl(int optname)
1255{
1256 switch (optname) {
1257 case IP_MSFILTER:
1258 case MCAST_MSFILTER:
1259 return true;
1260 }
1261 return false;
1262}
1263
1254static int do_ip_getsockopt(struct sock *sk, int level, int optname, 1264static int do_ip_getsockopt(struct sock *sk, int level, int optname,
1255 char __user *optval, int __user *optlen, unsigned int flags) 1265 char __user *optval, int __user *optlen, unsigned int flags)
1256{ 1266{
1257 struct inet_sock *inet = inet_sk(sk); 1267 struct inet_sock *inet = inet_sk(sk);
1258 int val; 1268 bool needs_rtnl = getsockopt_needs_rtnl(optname);
1269 int val, err = 0;
1259 int len; 1270 int len;
1260 1271
1261 if (level != SOL_IP) 1272 if (level != SOL_IP)
@@ -1269,6 +1280,8 @@ static int do_ip_getsockopt(struct sock *sk, int level, int optname,
1269 if (len < 0) 1280 if (len < 0)
1270 return -EINVAL; 1281 return -EINVAL;
1271 1282
1283 if (needs_rtnl)
1284 rtnl_lock();
1272 lock_sock(sk); 1285 lock_sock(sk);
1273 1286
1274 switch (optname) { 1287 switch (optname) {
@@ -1386,39 +1399,35 @@ static int do_ip_getsockopt(struct sock *sk, int level, int optname,
1386 case IP_MSFILTER: 1399 case IP_MSFILTER:
1387 { 1400 {
1388 struct ip_msfilter msf; 1401 struct ip_msfilter msf;
1389 int err;
1390 1402
1391 if (len < IP_MSFILTER_SIZE(0)) { 1403 if (len < IP_MSFILTER_SIZE(0)) {
1392 release_sock(sk); 1404 err = -EINVAL;
1393 return -EINVAL; 1405 goto out;
1394 } 1406 }
1395 if (copy_from_user(&msf, optval, IP_MSFILTER_SIZE(0))) { 1407 if (copy_from_user(&msf, optval, IP_MSFILTER_SIZE(0))) {
1396 release_sock(sk); 1408 err = -EFAULT;
1397 return -EFAULT; 1409 goto out;
1398 } 1410 }
1399 err = ip_mc_msfget(sk, &msf, 1411 err = ip_mc_msfget(sk, &msf,
1400 (struct ip_msfilter __user *)optval, optlen); 1412 (struct ip_msfilter __user *)optval, optlen);
1401 release_sock(sk); 1413 goto out;
1402 return err;
1403 } 1414 }
1404 case MCAST_MSFILTER: 1415 case MCAST_MSFILTER:
1405 { 1416 {
1406 struct group_filter gsf; 1417 struct group_filter gsf;
1407 int err;
1408 1418
1409 if (len < GROUP_FILTER_SIZE(0)) { 1419 if (len < GROUP_FILTER_SIZE(0)) {
1410 release_sock(sk); 1420 err = -EINVAL;
1411 return -EINVAL; 1421 goto out;
1412 } 1422 }
1413 if (copy_from_user(&gsf, optval, GROUP_FILTER_SIZE(0))) { 1423 if (copy_from_user(&gsf, optval, GROUP_FILTER_SIZE(0))) {
1414 release_sock(sk); 1424 err = -EFAULT;
1415 return -EFAULT; 1425 goto out;
1416 } 1426 }
1417 err = ip_mc_gsfget(sk, &gsf, 1427 err = ip_mc_gsfget(sk, &gsf,
1418 (struct group_filter __user *)optval, 1428 (struct group_filter __user *)optval,
1419 optlen); 1429 optlen);
1420 release_sock(sk); 1430 goto out;
1421 return err;
1422 } 1431 }
1423 case IP_MULTICAST_ALL: 1432 case IP_MULTICAST_ALL:
1424 val = inet->mc_all; 1433 val = inet->mc_all;
@@ -1485,6 +1494,12 @@ static int do_ip_getsockopt(struct sock *sk, int level, int optname,
1485 return -EFAULT; 1494 return -EFAULT;
1486 } 1495 }
1487 return 0; 1496 return 0;
1497
1498out:
1499 release_sock(sk);
1500 if (needs_rtnl)
1501 rtnl_unlock();
1502 return err;
1488} 1503}
1489 1504
1490int ip_getsockopt(struct sock *sk, int level, 1505int ip_getsockopt(struct sock *sk, int level,