Commit a554bf96 authored by Arnd Bergmann's avatar Arnd Bergmann Committed by David S. Miller

dev_ioctl: pass SIOCDEVPRIVATE data separately

The compat handlers for SIOCDEVPRIVATE are incorrect for any driver that
passes data as part of struct ifreq rather than as an ifr_data pointer, or
that passes data back this way, since the compat_ifr_data_ioctl() helper
overwrites the ifr_data pointer and does not copy anything back out.

Since all drivers using devprivate commands are now converted to the
new .ndo_siocdevprivate callback, fix this by adding the missing piece
and passing the pointer separately the whole way.

This further unifies the native and compat logic for socket ioctls,
as the new code now passes the correct pointer as well as the correct
data for both native and compat ioctls.
Signed-off-by: default avatarArnd Bergmann <arnd@arndb.de>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 8fb75b79
...@@ -4012,9 +4012,9 @@ bool dev_valid_name(const char *name); ...@@ -4012,9 +4012,9 @@ bool dev_valid_name(const char *name);
int get_user_ifreq(struct ifreq *ifr, void __user **ifrdata, void __user *arg); int get_user_ifreq(struct ifreq *ifr, void __user **ifrdata, void __user *arg);
int put_user_ifreq(struct ifreq *ifr, void __user *arg); int put_user_ifreq(struct ifreq *ifr, void __user *arg);
int dev_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr, int dev_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr,
bool *need_copyout); void __user *data, bool *need_copyout);
int dev_ifconf(struct net *net, struct ifconf __user *ifc); int dev_ifconf(struct net *net, struct ifconf __user *ifc);
int dev_ethtool(struct net *net, struct ifreq *); int dev_ethtool(struct net *net, struct ifreq *ifr, void __user *userdata);
unsigned int dev_get_flags(const struct net_device *); unsigned int dev_get_flags(const struct net_device *);
int __dev_change_flags(struct net_device *dev, unsigned int flags, int __dev_change_flags(struct net_device *dev, unsigned int flags,
struct netlink_ext_ack *extack); struct netlink_ext_ack *extack);
......
...@@ -259,11 +259,10 @@ static int dev_do_ioctl(struct net_device *dev, ...@@ -259,11 +259,10 @@ static int dev_do_ioctl(struct net_device *dev,
return err; return err;
} }
static int dev_siocdevprivate(struct net_device *dev, static int dev_siocdevprivate(struct net_device *dev, struct ifreq *ifr,
struct ifreq *ifr, unsigned int cmd) void __user *data, unsigned int cmd)
{ {
const struct net_device_ops *ops = dev->netdev_ops; const struct net_device_ops *ops = dev->netdev_ops;
void __user *data = ifr->ifr_data;
if (ops->ndo_siocdevprivate) { if (ops->ndo_siocdevprivate) {
if (netif_device_present(dev)) if (netif_device_present(dev))
...@@ -273,13 +272,15 @@ static int dev_siocdevprivate(struct net_device *dev, ...@@ -273,13 +272,15 @@ static int dev_siocdevprivate(struct net_device *dev,
} }
/* fall back to do_ioctl for drivers not yet converted */ /* fall back to do_ioctl for drivers not yet converted */
ifr->ifr_data = data;
return dev_do_ioctl(dev, ifr, cmd); return dev_do_ioctl(dev, ifr, cmd);
} }
/* /*
* Perform the SIOCxIFxxx calls, inside rtnl_lock() * Perform the SIOCxIFxxx calls, inside rtnl_lock()
*/ */
static int dev_ifsioc(struct net *net, struct ifreq *ifr, unsigned int cmd) static int dev_ifsioc(struct net *net, struct ifreq *ifr, void __user *data,
unsigned int cmd)
{ {
int err; int err;
struct net_device *dev = __dev_get_by_name(net, ifr->ifr_name); struct net_device *dev = __dev_get_by_name(net, ifr->ifr_name);
...@@ -355,7 +356,7 @@ static int dev_ifsioc(struct net *net, struct ifreq *ifr, unsigned int cmd) ...@@ -355,7 +356,7 @@ static int dev_ifsioc(struct net *net, struct ifreq *ifr, unsigned int cmd)
default: default:
if (cmd >= SIOCDEVPRIVATE && if (cmd >= SIOCDEVPRIVATE &&
cmd <= SIOCDEVPRIVATE + 15) cmd <= SIOCDEVPRIVATE + 15)
return dev_siocdevprivate(dev, ifr, cmd); return dev_siocdevprivate(dev, ifr, data, cmd);
if (cmd == SIOCBONDENSLAVE || if (cmd == SIOCBONDENSLAVE ||
cmd == SIOCBONDRELEASE || cmd == SIOCBONDRELEASE ||
...@@ -424,7 +425,8 @@ EXPORT_SYMBOL(dev_load); ...@@ -424,7 +425,8 @@ EXPORT_SYMBOL(dev_load);
* positive or a negative errno code on error. * positive or a negative errno code on error.
*/ */
int dev_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr, bool *need_copyout) int dev_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr,
void __user *data, bool *need_copyout)
{ {
int ret; int ret;
char *colon; char *colon;
...@@ -475,7 +477,7 @@ int dev_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr, bool *need_c ...@@ -475,7 +477,7 @@ int dev_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr, bool *need_c
case SIOCETHTOOL: case SIOCETHTOOL:
dev_load(net, ifr->ifr_name); dev_load(net, ifr->ifr_name);
rtnl_lock(); rtnl_lock();
ret = dev_ethtool(net, ifr); ret = dev_ethtool(net, ifr, data);
rtnl_unlock(); rtnl_unlock();
if (colon) if (colon)
*colon = ':'; *colon = ':';
...@@ -494,7 +496,7 @@ int dev_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr, bool *need_c ...@@ -494,7 +496,7 @@ int dev_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr, bool *need_c
if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
return -EPERM; return -EPERM;
rtnl_lock(); rtnl_lock();
ret = dev_ifsioc(net, ifr, cmd); ret = dev_ifsioc(net, ifr, data, cmd);
rtnl_unlock(); rtnl_unlock();
if (colon) if (colon)
*colon = ':'; *colon = ':';
...@@ -540,7 +542,7 @@ int dev_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr, bool *need_c ...@@ -540,7 +542,7 @@ int dev_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr, bool *need_c
case SIOCBONDINFOQUERY: case SIOCBONDINFOQUERY:
dev_load(net, ifr->ifr_name); dev_load(net, ifr->ifr_name);
rtnl_lock(); rtnl_lock();
ret = dev_ifsioc(net, ifr, cmd); ret = dev_ifsioc(net, ifr, data, cmd);
rtnl_unlock(); rtnl_unlock();
if (need_copyout) if (need_copyout)
*need_copyout = false; *need_copyout = false;
...@@ -565,7 +567,7 @@ int dev_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr, bool *need_c ...@@ -565,7 +567,7 @@ int dev_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr, bool *need_c
cmd <= SIOCDEVPRIVATE + 15)) { cmd <= SIOCDEVPRIVATE + 15)) {
dev_load(net, ifr->ifr_name); dev_load(net, ifr->ifr_name);
rtnl_lock(); rtnl_lock();
ret = dev_ifsioc(net, ifr, cmd); ret = dev_ifsioc(net, ifr, data, cmd);
rtnl_unlock(); rtnl_unlock();
return ret; return ret;
} }
......
...@@ -2685,10 +2685,9 @@ static int ethtool_set_fecparam(struct net_device *dev, void __user *useraddr) ...@@ -2685,10 +2685,9 @@ static int ethtool_set_fecparam(struct net_device *dev, void __user *useraddr)
/* The main entry point in this file. Called from net/core/dev_ioctl.c */ /* The main entry point in this file. Called from net/core/dev_ioctl.c */
int dev_ethtool(struct net *net, struct ifreq *ifr) int dev_ethtool(struct net *net, struct ifreq *ifr, void __user *useraddr)
{ {
struct net_device *dev = __dev_get_by_name(net, ifr->ifr_name); struct net_device *dev = __dev_get_by_name(net, ifr->ifr_name);
void __user *useraddr = ifr->ifr_data;
u32 ethcmd, sub_cmd; u32 ethcmd, sub_cmd;
int rc; int rc;
netdev_features_t old_features; netdev_features_t old_features;
......
...@@ -1092,6 +1092,7 @@ static long sock_do_ioctl(struct net *net, struct socket *sock, ...@@ -1092,6 +1092,7 @@ static long sock_do_ioctl(struct net *net, struct socket *sock,
bool need_copyout; bool need_copyout;
int err; int err;
void __user *argp = (void __user *)arg; void __user *argp = (void __user *)arg;
void __user *data;
err = sock->ops->ioctl(sock, cmd, arg); err = sock->ops->ioctl(sock, cmd, arg);
...@@ -1102,11 +1103,11 @@ static long sock_do_ioctl(struct net *net, struct socket *sock, ...@@ -1102,11 +1103,11 @@ static long sock_do_ioctl(struct net *net, struct socket *sock,
if (err != -ENOIOCTLCMD) if (err != -ENOIOCTLCMD)
return err; return err;
if (copy_from_user(&ifr, argp, sizeof(struct ifreq))) if (get_user_ifreq(&ifr, &data, argp))
return -EFAULT; return -EFAULT;
err = dev_ioctl(net, cmd, &ifr, &need_copyout); err = dev_ioctl(net, cmd, &ifr, data, &need_copyout);
if (!err && need_copyout) if (!err && need_copyout)
if (copy_to_user(argp, &ifr, sizeof(struct ifreq))) if (put_user_ifreq(&ifr, argp))
return -EFAULT; return -EFAULT;
return err; return err;
...@@ -1130,12 +1131,13 @@ static long sock_ioctl(struct file *file, unsigned cmd, unsigned long arg) ...@@ -1130,12 +1131,13 @@ static long sock_ioctl(struct file *file, unsigned cmd, unsigned long arg)
net = sock_net(sk); net = sock_net(sk);
if (unlikely(cmd >= SIOCDEVPRIVATE && cmd <= (SIOCDEVPRIVATE + 15))) { if (unlikely(cmd >= SIOCDEVPRIVATE && cmd <= (SIOCDEVPRIVATE + 15))) {
struct ifreq ifr; struct ifreq ifr;
void __user *data;
bool need_copyout; bool need_copyout;
if (copy_from_user(&ifr, argp, sizeof(struct ifreq))) if (get_user_ifreq(&ifr, &data, argp))
return -EFAULT; return -EFAULT;
err = dev_ioctl(net, cmd, &ifr, &need_copyout); err = dev_ioctl(net, cmd, &ifr, data, &need_copyout);
if (!err && need_copyout) if (!err && need_copyout)
if (copy_to_user(argp, &ifr, sizeof(struct ifreq))) if (put_user_ifreq(&ifr, argp))
return -EFAULT; return -EFAULT;
} else } else
#ifdef CONFIG_WEXT_CORE #ifdef CONFIG_WEXT_CORE
...@@ -3186,7 +3188,7 @@ static int compat_siocwandev(struct net *net, struct compat_ifreq __user *uifr32 ...@@ -3186,7 +3188,7 @@ static int compat_siocwandev(struct net *net, struct compat_ifreq __user *uifr32
saved = ifr.ifr_settings.ifs_ifsu.raw_hdlc; saved = ifr.ifr_settings.ifs_ifsu.raw_hdlc;
ifr.ifr_settings.ifs_ifsu.raw_hdlc = compat_ptr(uptr32); ifr.ifr_settings.ifs_ifsu.raw_hdlc = compat_ptr(uptr32);
err = dev_ioctl(net, SIOCWANDEV, &ifr, NULL); err = dev_ioctl(net, SIOCWANDEV, &ifr, NULL, NULL);
if (!err) { if (!err) {
ifr.ifr_settings.ifs_ifsu.raw_hdlc = saved; ifr.ifr_settings.ifs_ifsu.raw_hdlc = saved;
if (put_user_ifreq(&ifr, uifr32)) if (put_user_ifreq(&ifr, uifr32))
...@@ -3200,42 +3202,13 @@ static int compat_ifr_data_ioctl(struct net *net, unsigned int cmd, ...@@ -3200,42 +3202,13 @@ static int compat_ifr_data_ioctl(struct net *net, unsigned int cmd,
struct compat_ifreq __user *u_ifreq32) struct compat_ifreq __user *u_ifreq32)
{ {
struct ifreq ifreq; struct ifreq ifreq;
u32 data32; void __user *data;
if (copy_from_user(ifreq.ifr_name, u_ifreq32->ifr_name, IFNAMSIZ)) if (get_user_ifreq(&ifreq, &data, u_ifreq32))
return -EFAULT; return -EFAULT;
if (get_user(data32, &u_ifreq32->ifr_data)) ifreq.ifr_data = data;
return -EFAULT;
ifreq.ifr_data = compat_ptr(data32);
return dev_ioctl(net, cmd, &ifreq, NULL); return dev_ioctl(net, cmd, &ifreq, data, NULL);
}
static int compat_ifreq_ioctl(struct net *net, struct socket *sock,
unsigned int cmd,
unsigned long arg,
struct compat_ifreq __user *uifr32)
{
struct ifreq ifr;
bool need_copyout;
int err;
err = sock->ops->ioctl(sock, cmd, arg);
/* If this ioctl is unknown try to hand it down
* to the NIC driver.
*/
if (err != -ENOIOCTLCMD)
return err;
if (get_user_ifreq(&ifr, NULL, uifr32))
return -EFAULT;
err = dev_ioctl(net, cmd, &ifr, &need_copyout);
if (!err && need_copyout)
if (put_user_ifreq(&ifr, uifr32))
return -EFAULT;
return err;
} }
/* Since old style bridge ioctl's endup using SIOCDEVPRIVATE /* Since old style bridge ioctl's endup using SIOCDEVPRIVATE
...@@ -3337,8 +3310,6 @@ static int compat_sock_ioctl_trans(struct file *file, struct socket *sock, ...@@ -3337,8 +3310,6 @@ static int compat_sock_ioctl_trans(struct file *file, struct socket *sock,
case SIOCBONDRELEASE: case SIOCBONDRELEASE:
case SIOCBONDSETHWADDR: case SIOCBONDSETHWADDR:
case SIOCBONDCHANGEACTIVE: case SIOCBONDCHANGEACTIVE:
return compat_ifreq_ioctl(net, sock, cmd, arg, argp);
case SIOCSARP: case SIOCSARP:
case SIOCGARP: case SIOCGARP:
case SIOCDARP: case SIOCDARP:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment