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

Merge branch 'bridge-ioctl-fixes'

Nikolay Aleksandrov says:

====================
net: bridge: fix recent ioctl changes

These are three fixes for the recent bridge removal of ndo_do_ioctl
done by commit ad2f99aedf8f ("net: bridge: move bridge ioctls out of
.ndo_do_ioctl"). Patch 01 fixes a deadlock of the new bridge ioctl
hook lock and rtnl by taking a netdev reference and always taking the
bridge ioctl lock first then rtnl from within the bridge hook.
Patch 02 fixes old_deviceless() bridge calls device name argument, and
patch 03 checks in dev_ifsioc()'s SIOCBRADD/DELIF cases if the netdevice is
actually a bridge before interpreting its private ptr as net_bridge.

Patch 01 was tested by running old bridge-utils commands with lockdep
enabled. Patch 02 was tested again by using bridge-utils and using the
respective ioctl calls on a "up" bridge device. Patch 03 was tested by
using the addif ioctl on a non-bridge device (e.g. loopback).
====================

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

+34 -18
+1 -3
net/bridge/br_if.c
··· 456 456 dev_net_set(dev, net); 457 457 dev->rtnl_link_ops = &br_link_ops; 458 458 459 - res = register_netdev(dev); 459 + res = register_netdevice(dev); 460 460 if (res) 461 461 free_netdev(dev); 462 462 return res; ··· 467 467 struct net_device *dev; 468 468 int ret = 0; 469 469 470 - rtnl_lock(); 471 470 dev = __dev_get_by_name(net, name); 472 471 if (dev == NULL) 473 472 ret = -ENXIO; /* Could not find device */ ··· 484 485 else 485 486 br_dev_delete(dev, NULL); 486 487 487 - rtnl_unlock(); 488 488 return ret; 489 489 } 490 490
+25 -14
net/bridge/br_ioctl.c
··· 351 351 if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) 352 352 return -EPERM; 353 353 354 - if (copy_from_user(buf, uarg, IFNAMSIZ)) 354 + if (copy_from_user(buf, (void __user *)args[1], IFNAMSIZ)) 355 355 return -EFAULT; 356 356 357 357 buf[IFNAMSIZ-1] = 0; ··· 369 369 int br_ioctl_stub(struct net *net, struct net_bridge *br, unsigned int cmd, 370 370 struct ifreq *ifr, void __user *uarg) 371 371 { 372 + int ret = -EOPNOTSUPP; 373 + 374 + rtnl_lock(); 375 + 372 376 switch (cmd) { 373 377 case SIOCGIFBR: 374 378 case SIOCSIFBR: 375 - return old_deviceless(net, uarg); 376 - 379 + ret = old_deviceless(net, uarg); 380 + break; 377 381 case SIOCBRADDBR: 378 382 case SIOCBRDELBR: 379 383 { 380 384 char buf[IFNAMSIZ]; 381 385 382 - if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) 383 - return -EPERM; 386 + if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) { 387 + ret = -EPERM; 388 + break; 389 + } 384 390 385 - if (copy_from_user(buf, uarg, IFNAMSIZ)) 386 - return -EFAULT; 391 + if (copy_from_user(buf, uarg, IFNAMSIZ)) { 392 + ret = -EFAULT; 393 + break; 394 + } 387 395 388 396 buf[IFNAMSIZ-1] = 0; 389 397 if (cmd == SIOCBRADDBR) 390 - return br_add_bridge(net, buf); 391 - 392 - return br_del_bridge(net, buf); 398 + ret = br_add_bridge(net, buf); 399 + else 400 + ret = br_del_bridge(net, buf); 393 401 } 394 - 402 + break; 395 403 case SIOCBRADDIF: 396 404 case SIOCBRDELIF: 397 - return add_del_if(br, ifr->ifr_ifindex, cmd == SIOCBRADDIF); 398 - 405 + ret = add_del_if(br, ifr->ifr_ifindex, cmd == SIOCBRADDIF); 406 + break; 399 407 } 400 - return -EOPNOTSUPP; 408 + 409 + rtnl_unlock(); 410 + 411 + return ret; 401 412 }
+8 -1
net/core/dev_ioctl.c
··· 379 379 case SIOCBRDELIF: 380 380 if (!netif_device_present(dev)) 381 381 return -ENODEV; 382 - return br_ioctl_call(net, netdev_priv(dev), cmd, ifr, NULL); 382 + if (!netif_is_bridge_master(dev)) 383 + return -EOPNOTSUPP; 384 + dev_hold(dev); 385 + rtnl_unlock(); 386 + err = br_ioctl_call(net, netdev_priv(dev), cmd, ifr, NULL); 387 + dev_put(dev); 388 + rtnl_lock(); 389 + return err; 383 390 384 391 case SIOCSHWTSTAMP: 385 392 err = net_hwtstamp_validate(ifr);