dev_ioctl(): move copyin/copyout to callers
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
parent
6a88fbe725
commit
44c02a2c3d
|
@ -3315,7 +3315,8 @@ int netdev_rx_handler_register(struct net_device *dev,
|
||||||
void netdev_rx_handler_unregister(struct net_device *dev);
|
void netdev_rx_handler_unregister(struct net_device *dev);
|
||||||
|
|
||||||
bool dev_valid_name(const char *name);
|
bool dev_valid_name(const char *name);
|
||||||
int dev_ioctl(struct net *net, unsigned int cmd, void __user *);
|
int dev_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr,
|
||||||
|
bool *need_copyout);
|
||||||
int dev_ifconf(struct net *net, struct ifconf *, int);
|
int dev_ifconf(struct net *net, struct ifconf *, int);
|
||||||
int dev_ethtool(struct net *net, struct ifreq *);
|
int dev_ethtool(struct net *net, struct ifreq *);
|
||||||
unsigned int dev_get_flags(const struct net_device *);
|
unsigned int dev_get_flags(const struct net_device *);
|
||||||
|
|
|
@ -18,26 +18,10 @@
|
||||||
* match. --pb
|
* match. --pb
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static int dev_ifname(struct net *net, struct ifreq __user *arg)
|
static int dev_ifname(struct net *net, struct ifreq *ifr)
|
||||||
{
|
{
|
||||||
struct ifreq ifr;
|
ifr->ifr_name[IFNAMSIZ-1] = 0;
|
||||||
int error;
|
return netdev_get_name(net, ifr->ifr_name, ifr->ifr_ifindex);
|
||||||
|
|
||||||
/*
|
|
||||||
* Fetch the caller's info block.
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
|
|
||||||
return -EFAULT;
|
|
||||||
ifr.ifr_name[IFNAMSIZ-1] = 0;
|
|
||||||
|
|
||||||
error = netdev_get_name(net, ifr.ifr_name, ifr.ifr_ifindex);
|
|
||||||
if (error)
|
|
||||||
return error;
|
|
||||||
|
|
||||||
if (copy_to_user(arg, &ifr, sizeof(struct ifreq)))
|
|
||||||
return -EFAULT;
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static gifconf_func_t *gifconf_list[NPROTO];
|
static gifconf_func_t *gifconf_list[NPROTO];
|
||||||
|
@ -402,24 +386,24 @@ 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, void __user *arg)
|
int dev_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr, bool *need_copyout)
|
||||||
{
|
{
|
||||||
struct ifreq ifr;
|
|
||||||
int ret;
|
int ret;
|
||||||
char *colon;
|
char *colon;
|
||||||
|
|
||||||
|
if (need_copyout)
|
||||||
|
*need_copyout = true;
|
||||||
if (cmd == SIOCGIFNAME)
|
if (cmd == SIOCGIFNAME)
|
||||||
return dev_ifname(net, (struct ifreq __user *)arg);
|
return dev_ifname(net, ifr);
|
||||||
|
|
||||||
if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
|
ifr->ifr_name[IFNAMSIZ-1] = 0;
|
||||||
return -EFAULT;
|
|
||||||
|
|
||||||
ifr.ifr_name[IFNAMSIZ-1] = 0;
|
colon = strchr(ifr->ifr_name, ':');
|
||||||
|
|
||||||
colon = strchr(ifr.ifr_name, ':');
|
|
||||||
if (colon)
|
if (colon)
|
||||||
*colon = 0;
|
*colon = 0;
|
||||||
|
|
||||||
|
dev_load(net, ifr->ifr_name);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* See which interface the caller is talking about.
|
* See which interface the caller is talking about.
|
||||||
*/
|
*/
|
||||||
|
@ -439,31 +423,19 @@ int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg)
|
||||||
case SIOCGIFMAP:
|
case SIOCGIFMAP:
|
||||||
case SIOCGIFINDEX:
|
case SIOCGIFINDEX:
|
||||||
case SIOCGIFTXQLEN:
|
case SIOCGIFTXQLEN:
|
||||||
dev_load(net, ifr.ifr_name);
|
|
||||||
rcu_read_lock();
|
rcu_read_lock();
|
||||||
ret = dev_ifsioc_locked(net, &ifr, cmd);
|
ret = dev_ifsioc_locked(net, ifr, cmd);
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
if (!ret) {
|
if (colon)
|
||||||
if (colon)
|
*colon = ':';
|
||||||
*colon = ':';
|
|
||||||
if (copy_to_user(arg, &ifr,
|
|
||||||
sizeof(struct ifreq)))
|
|
||||||
ret = -EFAULT;
|
|
||||||
}
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
case SIOCETHTOOL:
|
case SIOCETHTOOL:
|
||||||
dev_load(net, ifr.ifr_name);
|
|
||||||
rtnl_lock();
|
rtnl_lock();
|
||||||
ret = dev_ethtool(net, &ifr);
|
ret = dev_ethtool(net, ifr);
|
||||||
rtnl_unlock();
|
rtnl_unlock();
|
||||||
if (!ret) {
|
if (colon)
|
||||||
if (colon)
|
*colon = ':';
|
||||||
*colon = ':';
|
|
||||||
if (copy_to_user(arg, &ifr,
|
|
||||||
sizeof(struct ifreq)))
|
|
||||||
ret = -EFAULT;
|
|
||||||
}
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -477,17 +449,11 @@ int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg)
|
||||||
case SIOCSIFNAME:
|
case SIOCSIFNAME:
|
||||||
if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
|
if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
|
||||||
return -EPERM;
|
return -EPERM;
|
||||||
dev_load(net, ifr.ifr_name);
|
|
||||||
rtnl_lock();
|
rtnl_lock();
|
||||||
ret = dev_ifsioc(net, &ifr, cmd);
|
ret = dev_ifsioc(net, ifr, cmd);
|
||||||
rtnl_unlock();
|
rtnl_unlock();
|
||||||
if (!ret) {
|
if (colon)
|
||||||
if (colon)
|
*colon = ':';
|
||||||
*colon = ':';
|
|
||||||
if (copy_to_user(arg, &ifr,
|
|
||||||
sizeof(struct ifreq)))
|
|
||||||
ret = -EFAULT;
|
|
||||||
}
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -528,10 +494,11 @@ int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg)
|
||||||
/* fall through */
|
/* fall through */
|
||||||
case SIOCBONDSLAVEINFOQUERY:
|
case SIOCBONDSLAVEINFOQUERY:
|
||||||
case SIOCBONDINFOQUERY:
|
case SIOCBONDINFOQUERY:
|
||||||
dev_load(net, ifr.ifr_name);
|
|
||||||
rtnl_lock();
|
rtnl_lock();
|
||||||
ret = dev_ifsioc(net, &ifr, cmd);
|
ret = dev_ifsioc(net, ifr, cmd);
|
||||||
rtnl_unlock();
|
rtnl_unlock();
|
||||||
|
if (need_copyout)
|
||||||
|
*need_copyout = false;
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
case SIOCGIFMEM:
|
case SIOCGIFMEM:
|
||||||
|
@ -551,13 +518,9 @@ int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg)
|
||||||
cmd == SIOCGHWTSTAMP ||
|
cmd == SIOCGHWTSTAMP ||
|
||||||
(cmd >= SIOCDEVPRIVATE &&
|
(cmd >= SIOCDEVPRIVATE &&
|
||||||
cmd <= SIOCDEVPRIVATE + 15)) {
|
cmd <= SIOCDEVPRIVATE + 15)) {
|
||||||
dev_load(net, ifr.ifr_name);
|
|
||||||
rtnl_lock();
|
rtnl_lock();
|
||||||
ret = dev_ifsioc(net, &ifr, cmd);
|
ret = dev_ifsioc(net, ifr, cmd);
|
||||||
rtnl_unlock();
|
rtnl_unlock();
|
||||||
if (!ret && copy_to_user(arg, &ifr,
|
|
||||||
sizeof(struct ifreq)))
|
|
||||||
ret = -EFAULT;
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
return -ENOTTY;
|
return -ENOTTY;
|
||||||
|
|
91
net/socket.c
91
net/socket.c
|
@ -973,10 +973,17 @@ static long sock_do_ioctl(struct net *net, struct socket *sock,
|
||||||
rtnl_unlock();
|
rtnl_unlock();
|
||||||
if (!err && copy_to_user(argp, &ifc, sizeof(struct ifconf)))
|
if (!err && copy_to_user(argp, &ifc, sizeof(struct ifconf)))
|
||||||
err = -EFAULT;
|
err = -EFAULT;
|
||||||
return err;
|
} else {
|
||||||
|
struct ifreq ifr;
|
||||||
|
bool need_copyout;
|
||||||
|
if (copy_from_user(&ifr, argp, sizeof(struct ifreq)))
|
||||||
|
return -EFAULT;
|
||||||
|
err = dev_ioctl(net, cmd, &ifr, &need_copyout);
|
||||||
|
if (!err && need_copyout)
|
||||||
|
if (copy_to_user(argp, &ifr, sizeof(struct ifreq)))
|
||||||
|
return -EFAULT;
|
||||||
}
|
}
|
||||||
|
return err;
|
||||||
return dev_ioctl(net, cmd, argp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1000,8 +1007,15 @@ static long sock_ioctl(struct file *file, unsigned cmd, unsigned long arg)
|
||||||
sock = file->private_data;
|
sock = file->private_data;
|
||||||
sk = sock->sk;
|
sk = sock->sk;
|
||||||
net = sock_net(sk);
|
net = sock_net(sk);
|
||||||
if (cmd >= SIOCDEVPRIVATE && cmd <= (SIOCDEVPRIVATE + 15)) {
|
if (unlikely(cmd >= SIOCDEVPRIVATE && cmd <= (SIOCDEVPRIVATE + 15))) {
|
||||||
err = dev_ioctl(net, cmd, argp);
|
struct ifreq ifr;
|
||||||
|
bool need_copyout;
|
||||||
|
if (copy_from_user(&ifr, argp, sizeof(struct ifreq)))
|
||||||
|
return -EFAULT;
|
||||||
|
err = dev_ioctl(net, cmd, &ifr, &need_copyout);
|
||||||
|
if (!err && need_copyout)
|
||||||
|
if (copy_to_user(argp, &ifr, sizeof(struct ifreq)))
|
||||||
|
return -EFAULT;
|
||||||
} else
|
} else
|
||||||
#ifdef CONFIG_WEXT_CORE
|
#ifdef CONFIG_WEXT_CORE
|
||||||
if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) {
|
if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) {
|
||||||
|
@ -2695,9 +2709,9 @@ static int ethtool_ioctl(struct net *net, struct compat_ifreq __user *ifr32)
|
||||||
{
|
{
|
||||||
struct compat_ethtool_rxnfc __user *compat_rxnfc;
|
struct compat_ethtool_rxnfc __user *compat_rxnfc;
|
||||||
bool convert_in = false, convert_out = false;
|
bool convert_in = false, convert_out = false;
|
||||||
size_t buf_size = ALIGN(sizeof(struct ifreq), 8);
|
size_t buf_size = 0;
|
||||||
struct ethtool_rxnfc __user *rxnfc;
|
struct ethtool_rxnfc __user *rxnfc = NULL;
|
||||||
struct ifreq __user *ifr;
|
struct ifreq ifr;
|
||||||
u32 rule_cnt = 0, actual_rule_cnt;
|
u32 rule_cnt = 0, actual_rule_cnt;
|
||||||
u32 ethcmd;
|
u32 ethcmd;
|
||||||
u32 data;
|
u32 data;
|
||||||
|
@ -2734,18 +2748,14 @@ static int ethtool_ioctl(struct net *net, struct compat_ifreq __user *ifr32)
|
||||||
case ETHTOOL_SRXCLSRLDEL:
|
case ETHTOOL_SRXCLSRLDEL:
|
||||||
buf_size += sizeof(struct ethtool_rxnfc);
|
buf_size += sizeof(struct ethtool_rxnfc);
|
||||||
convert_in = true;
|
convert_in = true;
|
||||||
|
rxnfc = compat_alloc_user_space(buf_size);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
ifr = compat_alloc_user_space(buf_size);
|
if (copy_from_user(&ifr.ifr_name, &ifr32->ifr_name, IFNAMSIZ))
|
||||||
rxnfc = (void __user *)ifr + ALIGN(sizeof(struct ifreq), 8);
|
|
||||||
|
|
||||||
if (copy_in_user(&ifr->ifr_name, &ifr32->ifr_name, IFNAMSIZ))
|
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
if (put_user(convert_in ? rxnfc : compat_ptr(data),
|
ifr.ifr_data = convert_in ? rxnfc : (void __user *)compat_rxnfc;
|
||||||
&ifr->ifr_ifru.ifru_data))
|
|
||||||
return -EFAULT;
|
|
||||||
|
|
||||||
if (convert_in) {
|
if (convert_in) {
|
||||||
/* We expect there to be holes between fs.m_ext and
|
/* We expect there to be holes between fs.m_ext and
|
||||||
|
@ -2773,7 +2783,7 @@ static int ethtool_ioctl(struct net *net, struct compat_ifreq __user *ifr32)
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = dev_ioctl(net, SIOCETHTOOL, ifr);
|
ret = dev_ioctl(net, SIOCETHTOOL, &ifr, NULL);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
@ -2814,50 +2824,43 @@ static int ethtool_ioctl(struct net *net, struct compat_ifreq __user *ifr32)
|
||||||
|
|
||||||
static int compat_siocwandev(struct net *net, struct compat_ifreq __user *uifr32)
|
static int compat_siocwandev(struct net *net, struct compat_ifreq __user *uifr32)
|
||||||
{
|
{
|
||||||
void __user *uptr;
|
|
||||||
compat_uptr_t uptr32;
|
compat_uptr_t uptr32;
|
||||||
struct ifreq __user *uifr;
|
struct ifreq ifr;
|
||||||
|
void __user *saved;
|
||||||
|
int err;
|
||||||
|
|
||||||
uifr = compat_alloc_user_space(sizeof(*uifr));
|
if (copy_from_user(&ifr, uifr32, sizeof(struct compat_ifreq)))
|
||||||
if (copy_in_user(uifr, uifr32, sizeof(struct compat_ifreq)))
|
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
if (get_user(uptr32, &uifr32->ifr_settings.ifs_ifsu))
|
if (get_user(uptr32, &uifr32->ifr_settings.ifs_ifsu))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
uptr = compat_ptr(uptr32);
|
saved = ifr.ifr_settings.ifs_ifsu.raw_hdlc;
|
||||||
|
ifr.ifr_settings.ifs_ifsu.raw_hdlc = compat_ptr(uptr32);
|
||||||
|
|
||||||
if (put_user(uptr, &uifr->ifr_settings.ifs_ifsu.raw_hdlc))
|
err = dev_ioctl(net, SIOCWANDEV, &ifr, NULL);
|
||||||
return -EFAULT;
|
if (!err) {
|
||||||
|
ifr.ifr_settings.ifs_ifsu.raw_hdlc = saved;
|
||||||
return dev_ioctl(net, SIOCWANDEV, uifr);
|
if (copy_to_user(uifr32, &ifr, sizeof(struct compat_ifreq)))
|
||||||
|
err = -EFAULT;
|
||||||
|
}
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Handle ioctls that use ifreq::ifr_data and just need struct ifreq converted */
|
/* Handle ioctls that use ifreq::ifr_data and just need struct ifreq converted */
|
||||||
static int compat_ifr_data_ioctl(struct net *net, unsigned int cmd,
|
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 __user *u_ifreq64;
|
struct ifreq ifreq;
|
||||||
char tmp_buf[IFNAMSIZ];
|
|
||||||
void __user *data64;
|
|
||||||
u32 data32;
|
u32 data32;
|
||||||
|
|
||||||
if (copy_from_user(&tmp_buf[0], &(u_ifreq32->ifr_ifrn.ifrn_name[0]),
|
if (copy_from_user(ifreq.ifr_name, u_ifreq32->ifr_name, IFNAMSIZ))
|
||||||
IFNAMSIZ))
|
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
if (get_user(data32, &u_ifreq32->ifr_ifru.ifru_data))
|
if (get_user(data32, &u_ifreq32->ifr_data))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
data64 = compat_ptr(data32);
|
ifreq.ifr_data = compat_ptr(data32);
|
||||||
|
|
||||||
u_ifreq64 = compat_alloc_user_space(sizeof(*u_ifreq64));
|
return dev_ioctl(net, cmd, &ifreq, NULL);
|
||||||
|
|
||||||
if (copy_to_user(&u_ifreq64->ifr_ifrn.ifrn_name[0], &tmp_buf[0],
|
|
||||||
IFNAMSIZ))
|
|
||||||
return -EFAULT;
|
|
||||||
if (put_user(data64, &u_ifreq64->ifr_ifru.ifru_data))
|
|
||||||
return -EFAULT;
|
|
||||||
|
|
||||||
return dev_ioctl(net, cmd, u_ifreq64);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int compat_sioc_ifmap(struct net *net, unsigned int cmd,
|
static int compat_sioc_ifmap(struct net *net, unsigned int cmd,
|
||||||
|
@ -2865,7 +2868,6 @@ static int compat_sioc_ifmap(struct net *net, unsigned int cmd,
|
||||||
{
|
{
|
||||||
struct ifreq ifr;
|
struct ifreq ifr;
|
||||||
struct compat_ifmap __user *uifmap32;
|
struct compat_ifmap __user *uifmap32;
|
||||||
mm_segment_t old_fs;
|
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
uifmap32 = &uifr32->ifr_ifru.ifru_map;
|
uifmap32 = &uifr32->ifr_ifru.ifru_map;
|
||||||
|
@ -2879,10 +2881,7 @@ static int compat_sioc_ifmap(struct net *net, unsigned int cmd,
|
||||||
if (err)
|
if (err)
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
old_fs = get_fs();
|
err = dev_ioctl(net, cmd, &ifr, NULL);
|
||||||
set_fs(KERNEL_DS);
|
|
||||||
err = dev_ioctl(net, cmd, (void __user __force *)&ifr);
|
|
||||||
set_fs(old_fs);
|
|
||||||
|
|
||||||
if (cmd == SIOCGIFMAP && !err) {
|
if (cmd == SIOCGIFMAP && !err) {
|
||||||
err = copy_to_user(uifr32, &ifr, sizeof(ifr.ifr_name));
|
err = copy_to_user(uifr32, &ifr, sizeof(ifr.ifr_name));
|
||||||
|
|
Loading…
Reference in New Issue