Skip to content

Commit 3b23a32

Browse files
Cong Wangdavem330
authored andcommitted
net: fix dev_ifsioc_locked() race condition
dev_ifsioc_locked() is called with only RCU read lock, so when there is a parallel writer changing the mac address, it could get a partially updated mac address, as shown below: Thread 1 Thread 2 // eth_commit_mac_addr_change() memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN); // dev_ifsioc_locked() memcpy(ifr->ifr_hwaddr.sa_data, dev->dev_addr,...); Close this race condition by guarding them with a RW semaphore, like netdev_get_name(). We can not use seqlock here as it does not allow blocking. The writers already take RTNL anyway, so this does not affect the slow path. To avoid bothering existing dev_set_mac_address() callers in drivers, introduce a new wrapper just for user-facing callers on ioctl and rtnetlink paths. Note, bonding also changes slave mac addresses but that requires a separate patch due to the complexity of bonding code. Fixes: 3710bec ("net: RCU locking for simple ioctl()") Reported-by: "Gong, Sishuai" <[email protected]> Cc: Eric Dumazet <[email protected]> Cc: Jakub Kicinski <[email protected]> Signed-off-by: Cong Wang <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 7867299 commit 3b23a32

File tree

6 files changed

+58
-21
lines changed

6 files changed

+58
-21
lines changed

drivers/net/tap.c

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1090,10 +1090,9 @@ static long tap_ioctl(struct file *file, unsigned int cmd,
10901090
return -ENOLINK;
10911091
}
10921092
ret = 0;
1093-
u = tap->dev->type;
1093+
dev_get_mac_address(&sa, dev_net(tap->dev), tap->dev->name);
10941094
if (copy_to_user(&ifr->ifr_name, tap->dev->name, IFNAMSIZ) ||
1095-
copy_to_user(&ifr->ifr_hwaddr.sa_data, tap->dev->dev_addr, ETH_ALEN) ||
1096-
put_user(u, &ifr->ifr_hwaddr.sa_family))
1095+
copy_to_user(&ifr->ifr_hwaddr, &sa, sizeof(sa)))
10971096
ret = -EFAULT;
10981097
tap_put_tap_dev(tap);
10991098
rtnl_unlock();
@@ -1108,7 +1107,7 @@ static long tap_ioctl(struct file *file, unsigned int cmd,
11081107
rtnl_unlock();
11091108
return -ENOLINK;
11101109
}
1111-
ret = dev_set_mac_address(tap->dev, &sa, NULL);
1110+
ret = dev_set_mac_address_user(tap->dev, &sa, NULL);
11121111
tap_put_tap_dev(tap);
11131112
rtnl_unlock();
11141113
return ret;

drivers/net/tun.c

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3107,15 +3107,14 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd,
31073107

31083108
case SIOCGIFHWADDR:
31093109
/* Get hw address */
3110-
memcpy(ifr.ifr_hwaddr.sa_data, tun->dev->dev_addr, ETH_ALEN);
3111-
ifr.ifr_hwaddr.sa_family = tun->dev->type;
3110+
dev_get_mac_address(&ifr.ifr_hwaddr, net, tun->dev->name);
31123111
if (copy_to_user(argp, &ifr, ifreq_len))
31133112
ret = -EFAULT;
31143113
break;
31153114

31163115
case SIOCSIFHWADDR:
31173116
/* Set hw address */
3118-
ret = dev_set_mac_address(tun->dev, &ifr.ifr_hwaddr, NULL);
3117+
ret = dev_set_mac_address_user(tun->dev, &ifr.ifr_hwaddr, NULL);
31193118
break;
31203119

31213120
case TUNGETSNDBUF:

include/linux/netdevice.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3902,6 +3902,9 @@ int dev_pre_changeaddr_notify(struct net_device *dev, const char *addr,
39023902
struct netlink_ext_ack *extack);
39033903
int dev_set_mac_address(struct net_device *dev, struct sockaddr *sa,
39043904
struct netlink_ext_ack *extack);
3905+
int dev_set_mac_address_user(struct net_device *dev, struct sockaddr *sa,
3906+
struct netlink_ext_ack *extack);
3907+
int dev_get_mac_address(struct sockaddr *sa, struct net *net, char *dev_name);
39053908
int dev_change_carrier(struct net_device *, bool new_carrier);
39063909
int dev_get_phys_port_id(struct net_device *dev,
39073910
struct netdev_phys_item_id *ppid);

net/core/dev.c

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8937,6 +8937,48 @@ int dev_set_mac_address(struct net_device *dev, struct sockaddr *sa,
89378937
}
89388938
EXPORT_SYMBOL(dev_set_mac_address);
89398939

8940+
static DECLARE_RWSEM(dev_addr_sem);
8941+
8942+
int dev_set_mac_address_user(struct net_device *dev, struct sockaddr *sa,
8943+
struct netlink_ext_ack *extack)
8944+
{
8945+
int ret;
8946+
8947+
down_write(&dev_addr_sem);
8948+
ret = dev_set_mac_address(dev, sa, extack);
8949+
up_write(&dev_addr_sem);
8950+
return ret;
8951+
}
8952+
EXPORT_SYMBOL(dev_set_mac_address_user);
8953+
8954+
int dev_get_mac_address(struct sockaddr *sa, struct net *net, char *dev_name)
8955+
{
8956+
size_t size = sizeof(sa->sa_data);
8957+
struct net_device *dev;
8958+
int ret = 0;
8959+
8960+
down_read(&dev_addr_sem);
8961+
rcu_read_lock();
8962+
8963+
dev = dev_get_by_name_rcu(net, dev_name);
8964+
if (!dev) {
8965+
ret = -ENODEV;
8966+
goto unlock;
8967+
}
8968+
if (!dev->addr_len)
8969+
memset(sa->sa_data, 0, size);
8970+
else
8971+
memcpy(sa->sa_data, dev->dev_addr,
8972+
min_t(size_t, size, dev->addr_len));
8973+
sa->sa_family = dev->type;
8974+
8975+
unlock:
8976+
rcu_read_unlock();
8977+
up_read(&dev_addr_sem);
8978+
return ret;
8979+
}
8980+
EXPORT_SYMBOL(dev_get_mac_address);
8981+
89408982
/**
89418983
* dev_change_carrier - Change device carrier
89428984
* @dev: device

net/core/dev_ioctl.c

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -123,17 +123,6 @@ static int dev_ifsioc_locked(struct net *net, struct ifreq *ifr, unsigned int cm
123123
ifr->ifr_mtu = dev->mtu;
124124
return 0;
125125

126-
case SIOCGIFHWADDR:
127-
if (!dev->addr_len)
128-
memset(ifr->ifr_hwaddr.sa_data, 0,
129-
sizeof(ifr->ifr_hwaddr.sa_data));
130-
else
131-
memcpy(ifr->ifr_hwaddr.sa_data, dev->dev_addr,
132-
min(sizeof(ifr->ifr_hwaddr.sa_data),
133-
(size_t)dev->addr_len));
134-
ifr->ifr_hwaddr.sa_family = dev->type;
135-
return 0;
136-
137126
case SIOCGIFSLAVE:
138127
err = -EINVAL;
139128
break;
@@ -274,7 +263,7 @@ static int dev_ifsioc(struct net *net, struct ifreq *ifr, unsigned int cmd)
274263
case SIOCSIFHWADDR:
275264
if (dev->addr_len > sizeof(struct sockaddr))
276265
return -EINVAL;
277-
return dev_set_mac_address(dev, &ifr->ifr_hwaddr, NULL);
266+
return dev_set_mac_address_user(dev, &ifr->ifr_hwaddr, NULL);
278267

279268
case SIOCSIFHWBROADCAST:
280269
if (ifr->ifr_hwaddr.sa_family != dev->type)
@@ -418,6 +407,12 @@ int dev_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr, bool *need_c
418407
*/
419408

420409
switch (cmd) {
410+
case SIOCGIFHWADDR:
411+
dev_load(net, ifr->ifr_name);
412+
ret = dev_get_mac_address(&ifr->ifr_hwaddr, net, ifr->ifr_name);
413+
if (colon)
414+
*colon = ':';
415+
return ret;
421416
/*
422417
* These ioctl calls:
423418
* - can be done by all.
@@ -427,7 +422,6 @@ int dev_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr, bool *need_c
427422
case SIOCGIFFLAGS:
428423
case SIOCGIFMETRIC:
429424
case SIOCGIFMTU:
430-
case SIOCGIFHWADDR:
431425
case SIOCGIFSLAVE:
432426
case SIOCGIFMAP:
433427
case SIOCGIFINDEX:

net/core/rtnetlink.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2660,7 +2660,7 @@ static int do_setlink(const struct sk_buff *skb,
26602660
sa->sa_family = dev->type;
26612661
memcpy(sa->sa_data, nla_data(tb[IFLA_ADDRESS]),
26622662
dev->addr_len);
2663-
err = dev_set_mac_address(dev, sa, extack);
2663+
err = dev_set_mac_address_user(dev, sa, extack);
26642664
kfree(sa);
26652665
if (err)
26662666
goto errout;

0 commit comments

Comments
 (0)