diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 5be20a7..6a26d2c 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -3338,7 +3338,7 @@ void netdev_state_change(struct net_device *dev); void netdev_notify_peers(struct net_device *dev); void netdev_features_change(struct net_device *dev); /* Load a device via the kmod */ -void dev_load(struct net *net, const char *name); +int dev_load(struct net *net, const char *name); struct rtnl_link_stats64 *dev_get_stats(struct net_device *dev, struct rtnl_link_stats64 *storage); void netdev_stats_to_stats64(struct rtnl_link_stats64 *stats64, diff --git a/net/core/dev_ioctl.c b/net/core/dev_ioctl.c index cf999e0..00a60f8 100644 --- a/net/core/dev_ioctl.c +++ b/net/core/dev_ioctl.c @@ -353,11 +353,14 @@ static int dev_ifsioc(struct net *net, struct ifreq *ifr, unsigned int cmd) * available in this kernel then it becomes a nop. */ -void dev_load(struct net *net, const char *name) +int dev_load(struct net *net, const char *name) { struct net_device *dev; int no_module; + if (!dev_valid_name(name)) + return -EINVAL; + rcu_read_lock(); dev = dev_get_by_name_rcu(net, name); rcu_read_unlock(); @@ -366,10 +369,14 @@ void dev_load(struct net *net, const char *name) if (no_module && capable(CAP_NET_ADMIN)) no_module = request_module("netdev-%s", name); if (no_module && capable(CAP_SYS_MODULE)) { - if (!request_module("%s", name)) + int err = request_module("%s", name); + if (!err) pr_warn("Loading kernel module for a network device with CAP_SYS_MODULE (deprecated). Use CAP_NET_ADMIN and alias netdev-%s instead.\n", name); + return err; } + + return no_module; } EXPORT_SYMBOL(dev_load); @@ -438,7 +445,9 @@ int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg) case SIOCGIFMAP: case SIOCGIFINDEX: case SIOCGIFTXQLEN: - dev_load(net, ifr.ifr_name); + ret = dev_load(net, ifr.ifr_name); + if (ret) + return ret; rcu_read_lock(); ret = dev_ifsioc_locked(net, &ifr, cmd); rcu_read_unlock(); @@ -452,7 +461,9 @@ int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg) return ret; case SIOCETHTOOL: - dev_load(net, ifr.ifr_name); + ret = dev_load(net, ifr.ifr_name); + if (ret) + return ret; rtnl_lock(); ret = dev_ethtool(net, &ifr); rtnl_unlock(); @@ -476,7 +487,9 @@ int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg) case SIOCSIFNAME: if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) return -EPERM; - dev_load(net, ifr.ifr_name); + ret = dev_load(net, ifr.ifr_name); + if (ret) + return ret; rtnl_lock(); ret = dev_ifsioc(net, &ifr, cmd); rtnl_unlock(); @@ -527,7 +540,9 @@ int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg) /* fall through */ case SIOCBONDSLAVEINFOQUERY: case SIOCBONDINFOQUERY: - dev_load(net, ifr.ifr_name); + ret = dev_load(net, ifr.ifr_name); + if (ret) + return ret; rtnl_lock(); ret = dev_ifsioc(net, &ifr, cmd); rtnl_unlock(); @@ -550,7 +565,9 @@ int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg) cmd == SIOCGHWTSTAMP || (cmd >= SIOCDEVPRIVATE && cmd <= SIOCDEVPRIVATE + 15)) { - dev_load(net, ifr.ifr_name); + ret = dev_load(net, ifr.ifr_name); + if (ret) + return ret; rtnl_lock(); ret = dev_ifsioc(net, &ifr, cmd); rtnl_unlock(); diff --git a/net/decnet/dn_dev.c b/net/decnet/dn_dev.c index 4400da7..484a5de 100644 --- a/net/decnet/dn_dev.c +++ b/net/decnet/dn_dev.c @@ -424,7 +424,9 @@ int dn_dev_ioctl(unsigned int cmd, void __user *arg) return -EFAULT; ifr->ifr_name[IFNAMSIZ-1] = 0; - dev_load(&init_net, ifr->ifr_name); + ret = dev_load(&init_net, ifr->ifr_name); + if (ret) + return ret; switch (cmd) { case SIOCGIFADDR: diff --git a/net/ieee802154/af_ieee802154.c b/net/ieee802154/af_ieee802154.c index 29e0de6..6c525a8 100644 --- a/net/ieee802154/af_ieee802154.c +++ b/net/ieee802154/af_ieee802154.c @@ -148,7 +148,9 @@ static int ieee802154_dev_ioctl(struct sock *sk, struct ifreq __user *arg, ifr.ifr_name[IFNAMSIZ-1] = 0; - dev_load(sock_net(sk), ifr.ifr_name); + ret = dev_load(sock_net(sk), ifr.ifr_name); + if (ret) + return ret; dev = dev_get_by_name(sock_net(sk), ifr.ifr_name); if (!dev) diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index 214882e..ec48f1e 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -909,7 +909,9 @@ int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg) if (colon) *colon = 0; - dev_load(net, ifr.ifr_name); + ret = dev_load(net, ifr.ifr_name); + if (ret) + return ret; switch (cmd) { case SIOCGIFADDR: /* Get interface address */ diff --git a/net/wireless/wext-core.c b/net/wireless/wext-core.c index c8717c1..8fea5fe 100644 --- a/net/wireless/wext-core.c +++ b/net/wireless/wext-core.c @@ -954,7 +954,9 @@ static int wext_ioctl_dispatch(struct net *net, struct ifreq *ifr, if (ret) return ret; - dev_load(net, ifr->ifr_name); + ret = dev_load(net, ifr->ifr_name); + if (ret) + return ret; rtnl_lock(); ret = wireless_process_ioctl(net, ifr, cmd, info, standard, private); rtnl_unlock();