Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 5dbe7c1

Browse files
nschichandavem330
authored andcommitted
net: fix kernel deadlock with interface rename and netdev name retrieval.
When the kernel (compiled with CONFIG_PREEMPT=n) is performing the rename of a network interface, it can end up waiting for a workqueue to complete. If userland is able to invoke a SIOCGIFNAME ioctl or a SO_BINDTODEVICE getsockopt in between, the kernel will deadlock due to the fact that read_secklock_begin() will spin forever waiting for the writer process (the one doing the interface rename) to update the devnet_rename_seq sequence. This patch fixes the problem by adding a helper (netdev_get_name()) and using it in the code handling the SIOCGIFNAME ioctl and SO_BINDTODEVICE setsockopt. The netdev_get_name() helper uses raw_seqcount_begin() to avoid spinning forever, waiting for devnet_rename_seq->sequence to become even. cond_resched() is used in the contended case, before retrying the access to give the writer process a chance to finish. The use of raw_seqcount_begin() will incur some unneeded work in the reader process in the contended case, but this is better than deadlocking the system. Signed-off-by: Nicolas Schichan <[email protected]> Acked-by: Eric Dumazet <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 6d446ec commit 5dbe7c1

File tree

4 files changed

+41
-30
lines changed

4 files changed

+41
-30
lines changed

include/linux/netdevice.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1695,6 +1695,7 @@ extern int init_dummy_netdev(struct net_device *dev);
16951695
extern struct net_device *dev_get_by_index(struct net *net, int ifindex);
16961696
extern struct net_device *__dev_get_by_index(struct net *net, int ifindex);
16971697
extern struct net_device *dev_get_by_index_rcu(struct net *net, int ifindex);
1698+
extern int netdev_get_name(struct net *net, char *name, int ifindex);
16981699
extern int dev_restart(struct net_device *dev);
16991700
#ifdef CONFIG_NETPOLL_TRAP
17001701
extern int netpoll_trap(void);

net/core/dev.c

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -791,6 +791,40 @@ struct net_device *dev_get_by_index(struct net *net, int ifindex)
791791
}
792792
EXPORT_SYMBOL(dev_get_by_index);
793793

794+
/**
795+
* netdev_get_name - get a netdevice name, knowing its ifindex.
796+
* @net: network namespace
797+
* @name: a pointer to the buffer where the name will be stored.
798+
* @ifindex: the ifindex of the interface to get the name from.
799+
*
800+
* The use of raw_seqcount_begin() and cond_resched() before
801+
* retrying is required as we want to give the writers a chance
802+
* to complete when CONFIG_PREEMPT is not set.
803+
*/
804+
int netdev_get_name(struct net *net, char *name, int ifindex)
805+
{
806+
struct net_device *dev;
807+
unsigned int seq;
808+
809+
retry:
810+
seq = raw_seqcount_begin(&devnet_rename_seq);
811+
rcu_read_lock();
812+
dev = dev_get_by_index_rcu(net, ifindex);
813+
if (!dev) {
814+
rcu_read_unlock();
815+
return -ENODEV;
816+
}
817+
818+
strcpy(name, dev->name);
819+
rcu_read_unlock();
820+
if (read_seqcount_retry(&devnet_rename_seq, seq)) {
821+
cond_resched();
822+
goto retry;
823+
}
824+
825+
return 0;
826+
}
827+
794828
/**
795829
* dev_getbyhwaddr_rcu - find a device by its hardware address
796830
* @net: the applicable net namespace

net/core/dev_ioctl.c

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,8 @@
1919

2020
static int dev_ifname(struct net *net, struct ifreq __user *arg)
2121
{
22-
struct net_device *dev;
2322
struct ifreq ifr;
24-
unsigned seq;
23+
int error;
2524

2625
/*
2726
* Fetch the caller's info block.
@@ -30,19 +29,9 @@ static int dev_ifname(struct net *net, struct ifreq __user *arg)
3029
if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
3130
return -EFAULT;
3231

33-
retry:
34-
seq = read_seqcount_begin(&devnet_rename_seq);
35-
rcu_read_lock();
36-
dev = dev_get_by_index_rcu(net, ifr.ifr_ifindex);
37-
if (!dev) {
38-
rcu_read_unlock();
39-
return -ENODEV;
40-
}
41-
42-
strcpy(ifr.ifr_name, dev->name);
43-
rcu_read_unlock();
44-
if (read_seqcount_retry(&devnet_rename_seq, seq))
45-
goto retry;
32+
error = netdev_get_name(net, ifr.ifr_name, ifr.ifr_ifindex);
33+
if (error)
34+
return error;
4635

4736
if (copy_to_user(arg, &ifr, sizeof(struct ifreq)))
4837
return -EFAULT;

net/core/sock.c

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -571,9 +571,7 @@ static int sock_getbindtodevice(struct sock *sk, char __user *optval,
571571
int ret = -ENOPROTOOPT;
572572
#ifdef CONFIG_NETDEVICES
573573
struct net *net = sock_net(sk);
574-
struct net_device *dev;
575574
char devname[IFNAMSIZ];
576-
unsigned seq;
577575

578576
if (sk->sk_bound_dev_if == 0) {
579577
len = 0;
@@ -584,20 +582,9 @@ static int sock_getbindtodevice(struct sock *sk, char __user *optval,
584582
if (len < IFNAMSIZ)
585583
goto out;
586584

587-
retry:
588-
seq = read_seqcount_begin(&devnet_rename_seq);
589-
rcu_read_lock();
590-
dev = dev_get_by_index_rcu(net, sk->sk_bound_dev_if);
591-
ret = -ENODEV;
592-
if (!dev) {
593-
rcu_read_unlock();
585+
ret = netdev_get_name(net, devname, sk->sk_bound_dev_if);
586+
if (ret)
594587
goto out;
595-
}
596-
597-
strcpy(devname, dev->name);
598-
rcu_read_unlock();
599-
if (read_seqcount_retry(&devnet_rename_seq, seq))
600-
goto retry;
601588

602589
len = strlen(devname) + 1;
603590

0 commit comments

Comments
 (0)