Skip to content

Commit 29c4964

Browse files
arndbdavem330
authored andcommitted
net: socket: rework compat_ifreq_ioctl()
compat_ifreq_ioctl() is one of the last users of copy_in_user() and compat_alloc_user_space(), as it attempts to convert the 'struct ifreq' arguments from 32-bit to 64-bit format as used by dev_ioctl() and a couple of socket family specific interpretations. The current implementation works correctly when calling dev_ioctl(), inet_ioctl(), ieee802154_sock_ioctl(), atalk_ioctl(), qrtr_ioctl() and packet_ioctl(). The ioctl handlers for x25, netrom, rose and x25 do not interpret the arguments and only block the corresponding commands, so they do not care. For af_inet6 and af_decnet however, the compat conversion is slightly incorrect, as it will copy more data than the native handler accesses, both of them use a structure that is shorter than ifreq. Replace the copy_in_user() conversion with a pair of accessor functions to read and write the ifreq data in place with the correct length where needed, while leaving the other ones to copy the (already compatible) structures directly. Signed-off-by: Arnd Bergmann <arnd@arndb.de> Reviewed-by: Christoph Hellwig <hch@lst.de> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent 876f0bf commit 29c4964

File tree

6 files changed

+76
-47
lines changed

6 files changed

+76
-47
lines changed

include/linux/netdevice.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4006,6 +4006,8 @@ int netdev_rx_handler_register(struct net_device *dev,
40064006
void netdev_rx_handler_unregister(struct net_device *dev);
40074007

40084008
bool dev_valid_name(const char *name);
4009+
int get_user_ifreq(struct ifreq *ifr, void __user **ifrdata, void __user *arg);
4010+
int put_user_ifreq(struct ifreq *ifr, void __user *arg);
40094011
int dev_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr,
40104012
bool *need_copyout);
40114013
int dev_ifconf(struct net *net, struct ifconf __user *ifc);

net/appletalk/ddp.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -666,7 +666,7 @@ static int atif_ioctl(int cmd, void __user *arg)
666666
struct rtentry rtdef;
667667
int add_route;
668668

669-
if (copy_from_user(&atreq, arg, sizeof(atreq)))
669+
if (get_user_ifreq(&atreq, NULL, arg))
670670
return -EFAULT;
671671

672672
dev = __dev_get_by_name(&init_net, atreq.ifr_name);
@@ -865,7 +865,7 @@ static int atif_ioctl(int cmd, void __user *arg)
865865
return 0;
866866
}
867867

868-
return copy_to_user(arg, &atreq, sizeof(atreq)) ? -EFAULT : 0;
868+
return put_user_ifreq(&atreq, arg);
869869
}
870870

871871
static int atrtr_ioctl_addrt(struct rtentry *rt)

net/ieee802154/socket.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ static int ieee802154_dev_ioctl(struct sock *sk, struct ifreq __user *arg,
129129
int ret = -ENOIOCTLCMD;
130130
struct net_device *dev;
131131

132-
if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
132+
if (get_user_ifreq(&ifr, NULL, arg))
133133
return -EFAULT;
134134

135135
ifr.ifr_name[IFNAMSIZ-1] = 0;
@@ -143,7 +143,7 @@ static int ieee802154_dev_ioctl(struct sock *sk, struct ifreq __user *arg,
143143
if (dev->type == ARPHRD_IEEE802154 && dev->netdev_ops->ndo_do_ioctl)
144144
ret = dev->netdev_ops->ndo_do_ioctl(dev, &ifr, cmd);
145145

146-
if (!ret && copy_to_user(arg, &ifr, sizeof(struct ifreq)))
146+
if (!ret && put_user_ifreq(&ifr, arg))
147147
ret = -EFAULT;
148148
dev_put(dev);
149149

net/ipv4/af_inet.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -953,10 +953,10 @@ int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
953953
case SIOCGIFNETMASK:
954954
case SIOCGIFDSTADDR:
955955
case SIOCGIFPFLAGS:
956-
if (copy_from_user(&ifr, p, sizeof(struct ifreq)))
956+
if (get_user_ifreq(&ifr, NULL, p))
957957
return -EFAULT;
958958
err = devinet_ioctl(net, cmd, &ifr);
959-
if (!err && copy_to_user(p, &ifr, sizeof(struct ifreq)))
959+
if (!err && put_user_ifreq(&ifr, p))
960960
err = -EFAULT;
961961
break;
962962

@@ -966,7 +966,7 @@ int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
966966
case SIOCSIFDSTADDR:
967967
case SIOCSIFPFLAGS:
968968
case SIOCSIFFLAGS:
969-
if (copy_from_user(&ifr, p, sizeof(struct ifreq)))
969+
if (get_user_ifreq(&ifr, NULL, p))
970970
return -EFAULT;
971971
err = devinet_ioctl(net, cmd, &ifr);
972972
break;

net/qrtr/qrtr.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1153,14 +1153,14 @@ static int qrtr_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
11531153
rc = put_user(len, (int __user *)argp);
11541154
break;
11551155
case SIOCGIFADDR:
1156-
if (copy_from_user(&ifr, argp, sizeof(ifr))) {
1156+
if (get_user_ifreq(&ifr, NULL, argp)) {
11571157
rc = -EFAULT;
11581158
break;
11591159
}
11601160

11611161
sq = (struct sockaddr_qrtr *)&ifr.ifr_addr;
11621162
*sq = ipc->us;
1163-
if (copy_to_user(argp, &ifr, sizeof(ifr))) {
1163+
if (put_user_ifreq(&ifr, argp)) {
11641164
rc = -EFAULT;
11651165
break;
11661166
}

net/socket.c

Lines changed: 65 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -3121,6 +3121,54 @@ void socket_seq_show(struct seq_file *seq)
31213121
}
31223122
#endif /* CONFIG_PROC_FS */
31233123

3124+
/* Handle the fact that while struct ifreq has the same *layout* on
3125+
* 32/64 for everything but ifreq::ifru_ifmap and ifreq::ifru_data,
3126+
* which are handled elsewhere, it still has different *size* due to
3127+
* ifreq::ifru_ifmap (which is 16 bytes on 32 bit, 24 bytes on 64-bit,
3128+
* resulting in struct ifreq being 32 and 40 bytes respectively).
3129+
* As a result, if the struct happens to be at the end of a page and
3130+
* the next page isn't readable/writable, we get a fault. To prevent
3131+
* that, copy back and forth to the full size.
3132+
*/
3133+
int get_user_ifreq(struct ifreq *ifr, void __user **ifrdata, void __user *arg)
3134+
{
3135+
if (in_compat_syscall()) {
3136+
struct compat_ifreq *ifr32 = (struct compat_ifreq *)ifr;
3137+
3138+
memset(ifr, 0, sizeof(*ifr));
3139+
if (copy_from_user(ifr32, arg, sizeof(*ifr32)))
3140+
return -EFAULT;
3141+
3142+
if (ifrdata)
3143+
*ifrdata = compat_ptr(ifr32->ifr_data);
3144+
3145+
return 0;
3146+
}
3147+
3148+
if (copy_from_user(ifr, arg, sizeof(*ifr)))
3149+
return -EFAULT;
3150+
3151+
if (ifrdata)
3152+
*ifrdata = ifr->ifr_data;
3153+
3154+
return 0;
3155+
}
3156+
EXPORT_SYMBOL(get_user_ifreq);
3157+
3158+
int put_user_ifreq(struct ifreq *ifr, void __user *arg)
3159+
{
3160+
size_t size = sizeof(*ifr);
3161+
3162+
if (in_compat_syscall())
3163+
size = sizeof(struct compat_ifreq);
3164+
3165+
if (copy_to_user(arg, ifr, size))
3166+
return -EFAULT;
3167+
3168+
return 0;
3169+
}
3170+
EXPORT_SYMBOL(put_user_ifreq);
3171+
31243172
#ifdef CONFIG_COMPAT
31253173
static int compat_siocwandev(struct net *net, struct compat_ifreq __user *uifr32)
31263174
{
@@ -3129,7 +3177,7 @@ static int compat_siocwandev(struct net *net, struct compat_ifreq __user *uifr32
31293177
void __user *saved;
31303178
int err;
31313179

3132-
if (copy_from_user(&ifr, uifr32, sizeof(struct compat_ifreq)))
3180+
if (get_user_ifreq(&ifr, NULL, uifr32))
31333181
return -EFAULT;
31343182

31353183
if (get_user(uptr32, &uifr32->ifr_settings.ifs_ifsu))
@@ -3141,7 +3189,7 @@ static int compat_siocwandev(struct net *net, struct compat_ifreq __user *uifr32
31413189
err = dev_ioctl(net, SIOCWANDEV, &ifr, NULL);
31423190
if (!err) {
31433191
ifr.ifr_settings.ifs_ifsu.raw_hdlc = saved;
3144-
if (copy_to_user(uifr32, &ifr, sizeof(struct compat_ifreq)))
3192+
if (put_user_ifreq(&ifr, uifr32))
31453193
err = -EFAULT;
31463194
}
31473195
return err;
@@ -3165,49 +3213,28 @@ static int compat_ifr_data_ioctl(struct net *net, unsigned int cmd,
31653213

31663214
static int compat_ifreq_ioctl(struct net *net, struct socket *sock,
31673215
unsigned int cmd,
3216+
unsigned long arg,
31683217
struct compat_ifreq __user *uifr32)
31693218
{
3170-
struct ifreq __user *uifr;
3219+
struct ifreq ifr;
3220+
bool need_copyout;
31713221
int err;
31723222

3173-
/* Handle the fact that while struct ifreq has the same *layout* on
3174-
* 32/64 for everything but ifreq::ifru_ifmap and ifreq::ifru_data,
3175-
* which are handled elsewhere, it still has different *size* due to
3176-
* ifreq::ifru_ifmap (which is 16 bytes on 32 bit, 24 bytes on 64-bit,
3177-
* resulting in struct ifreq being 32 and 40 bytes respectively).
3178-
* As a result, if the struct happens to be at the end of a page and
3179-
* the next page isn't readable/writable, we get a fault. To prevent
3180-
* that, copy back and forth to the full size.
3223+
err = sock->ops->ioctl(sock, cmd, arg);
3224+
3225+
/* If this ioctl is unknown try to hand it down
3226+
* to the NIC driver.
31813227
*/
3228+
if (err != -ENOIOCTLCMD)
3229+
return err;
31823230

3183-
uifr = compat_alloc_user_space(sizeof(*uifr));
3184-
if (copy_in_user(uifr, uifr32, sizeof(*uifr32)))
3231+
if (get_user_ifreq(&ifr, NULL, uifr32))
31853232
return -EFAULT;
3233+
err = dev_ioctl(net, cmd, &ifr, &need_copyout);
3234+
if (!err && need_copyout)
3235+
if (put_user_ifreq(&ifr, uifr32))
3236+
return -EFAULT;
31863237

3187-
err = sock_do_ioctl(net, sock, cmd, (unsigned long)uifr);
3188-
3189-
if (!err) {
3190-
switch (cmd) {
3191-
case SIOCGIFFLAGS:
3192-
case SIOCGIFMETRIC:
3193-
case SIOCGIFMTU:
3194-
case SIOCGIFMEM:
3195-
case SIOCGIFHWADDR:
3196-
case SIOCGIFINDEX:
3197-
case SIOCGIFADDR:
3198-
case SIOCGIFBRDADDR:
3199-
case SIOCGIFDSTADDR:
3200-
case SIOCGIFNETMASK:
3201-
case SIOCGIFPFLAGS:
3202-
case SIOCGIFTXQLEN:
3203-
case SIOCGMIIPHY:
3204-
case SIOCGMIIREG:
3205-
case SIOCGIFNAME:
3206-
if (copy_in_user(uifr32, uifr, sizeof(*uifr32)))
3207-
err = -EFAULT;
3208-
break;
3209-
}
3210-
}
32113238
return err;
32123239
}
32133240

@@ -3310,7 +3337,7 @@ static int compat_sock_ioctl_trans(struct file *file, struct socket *sock,
33103337
case SIOCBONDRELEASE:
33113338
case SIOCBONDSETHWADDR:
33123339
case SIOCBONDCHANGEACTIVE:
3313-
return compat_ifreq_ioctl(net, sock, cmd, argp);
3340+
return compat_ifreq_ioctl(net, sock, cmd, arg, argp);
33143341

33153342
case SIOCSARP:
33163343
case SIOCGARP:

0 commit comments

Comments
 (0)