|
@@ -961,9 +961,28 @@ static long sock_do_ioctl(struct net *net, struct socket *sock,
|
|
|
* If this ioctl is unknown try to hand it down
|
|
|
* to the NIC driver.
|
|
|
*/
|
|
|
- if (err == -ENOIOCTLCMD)
|
|
|
- err = dev_ioctl(net, cmd, argp);
|
|
|
+ if (err != -ENOIOCTLCMD)
|
|
|
+ return err;
|
|
|
|
|
|
+ if (cmd == SIOCGIFCONF) {
|
|
|
+ struct ifconf ifc;
|
|
|
+ if (copy_from_user(&ifc, argp, sizeof(struct ifconf)))
|
|
|
+ return -EFAULT;
|
|
|
+ rtnl_lock();
|
|
|
+ err = dev_ifconf(net, &ifc, sizeof(struct ifreq));
|
|
|
+ rtnl_unlock();
|
|
|
+ if (!err && copy_to_user(argp, &ifc, sizeof(struct ifconf)))
|
|
|
+ err = -EFAULT;
|
|
|
+ } 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;
|
|
|
}
|
|
|
|
|
@@ -988,12 +1007,19 @@ static long sock_ioctl(struct file *file, unsigned cmd, unsigned long arg)
|
|
|
sock = file->private_data;
|
|
|
sk = sock->sk;
|
|
|
net = sock_net(sk);
|
|
|
- if (cmd >= SIOCDEVPRIVATE && cmd <= (SIOCDEVPRIVATE + 15)) {
|
|
|
- err = dev_ioctl(net, cmd, argp);
|
|
|
+ if (unlikely(cmd >= SIOCDEVPRIVATE && cmd <= (SIOCDEVPRIVATE + 15))) {
|
|
|
+ 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
|
|
|
#ifdef CONFIG_WEXT_CORE
|
|
|
if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) {
|
|
|
- err = dev_ioctl(net, cmd, argp);
|
|
|
+ err = wext_handle_ioctl(net, cmd, argp);
|
|
|
} else
|
|
|
#endif
|
|
|
switch (cmd) {
|
|
@@ -2654,89 +2680,25 @@ static int do_siocgstampns(struct net *net, struct socket *sock,
|
|
|
return err;
|
|
|
}
|
|
|
|
|
|
-static int dev_ifname32(struct net *net, struct compat_ifreq __user *uifr32)
|
|
|
-{
|
|
|
- struct ifreq __user *uifr;
|
|
|
- int err;
|
|
|
-
|
|
|
- uifr = compat_alloc_user_space(sizeof(struct ifreq));
|
|
|
- if (copy_in_user(uifr, uifr32, sizeof(struct compat_ifreq)))
|
|
|
- return -EFAULT;
|
|
|
-
|
|
|
- err = dev_ioctl(net, SIOCGIFNAME, uifr);
|
|
|
- if (err)
|
|
|
- return err;
|
|
|
-
|
|
|
- if (copy_in_user(uifr32, uifr, sizeof(struct compat_ifreq)))
|
|
|
- return -EFAULT;
|
|
|
-
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-static int dev_ifconf(struct net *net, struct compat_ifconf __user *uifc32)
|
|
|
+static int compat_dev_ifconf(struct net *net, struct compat_ifconf __user *uifc32)
|
|
|
{
|
|
|
struct compat_ifconf ifc32;
|
|
|
struct ifconf ifc;
|
|
|
- struct ifconf __user *uifc;
|
|
|
- struct compat_ifreq __user *ifr32;
|
|
|
- struct ifreq __user *ifr;
|
|
|
- unsigned int i, j;
|
|
|
int err;
|
|
|
|
|
|
if (copy_from_user(&ifc32, uifc32, sizeof(struct compat_ifconf)))
|
|
|
return -EFAULT;
|
|
|
|
|
|
- memset(&ifc, 0, sizeof(ifc));
|
|
|
- if (ifc32.ifcbuf == 0) {
|
|
|
- ifc32.ifc_len = 0;
|
|
|
- ifc.ifc_len = 0;
|
|
|
- ifc.ifc_req = NULL;
|
|
|
- uifc = compat_alloc_user_space(sizeof(struct ifconf));
|
|
|
- } else {
|
|
|
- size_t len = ((ifc32.ifc_len / sizeof(struct compat_ifreq)) + 1) *
|
|
|
- sizeof(struct ifreq);
|
|
|
- uifc = compat_alloc_user_space(sizeof(struct ifconf) + len);
|
|
|
- ifc.ifc_len = len;
|
|
|
- ifr = ifc.ifc_req = (void __user *)(uifc + 1);
|
|
|
- ifr32 = compat_ptr(ifc32.ifcbuf);
|
|
|
- for (i = 0; i < ifc32.ifc_len; i += sizeof(struct compat_ifreq)) {
|
|
|
- if (copy_in_user(ifr, ifr32, sizeof(struct compat_ifreq)))
|
|
|
- return -EFAULT;
|
|
|
- ifr++;
|
|
|
- ifr32++;
|
|
|
- }
|
|
|
- }
|
|
|
- if (copy_to_user(uifc, &ifc, sizeof(struct ifconf)))
|
|
|
- return -EFAULT;
|
|
|
+ ifc.ifc_len = ifc32.ifc_len;
|
|
|
+ ifc.ifc_req = compat_ptr(ifc32.ifcbuf);
|
|
|
|
|
|
- err = dev_ioctl(net, SIOCGIFCONF, uifc);
|
|
|
+ rtnl_lock();
|
|
|
+ err = dev_ifconf(net, &ifc, sizeof(struct compat_ifreq));
|
|
|
+ rtnl_unlock();
|
|
|
if (err)
|
|
|
return err;
|
|
|
|
|
|
- if (copy_from_user(&ifc, uifc, sizeof(struct ifconf)))
|
|
|
- return -EFAULT;
|
|
|
-
|
|
|
- ifr = ifc.ifc_req;
|
|
|
- ifr32 = compat_ptr(ifc32.ifcbuf);
|
|
|
- for (i = 0, j = 0;
|
|
|
- i + sizeof(struct compat_ifreq) <= ifc32.ifc_len && j < ifc.ifc_len;
|
|
|
- i += sizeof(struct compat_ifreq), j += sizeof(struct ifreq)) {
|
|
|
- if (copy_in_user(ifr32, ifr, sizeof(struct compat_ifreq)))
|
|
|
- return -EFAULT;
|
|
|
- ifr32++;
|
|
|
- ifr++;
|
|
|
- }
|
|
|
-
|
|
|
- if (ifc32.ifcbuf == 0) {
|
|
|
- /* Translate from 64-bit structure multiple to
|
|
|
- * a 32-bit one.
|
|
|
- */
|
|
|
- i = ifc.ifc_len;
|
|
|
- i = ((i / sizeof(struct ifreq)) * sizeof(struct compat_ifreq));
|
|
|
- ifc32.ifc_len = i;
|
|
|
- } else {
|
|
|
- ifc32.ifc_len = i;
|
|
|
- }
|
|
|
+ ifc32.ifc_len = ifc.ifc_len;
|
|
|
if (copy_to_user(uifc32, &ifc32, sizeof(struct compat_ifconf)))
|
|
|
return -EFAULT;
|
|
|
|
|
@@ -2747,9 +2709,9 @@ static int ethtool_ioctl(struct net *net, struct compat_ifreq __user *ifr32)
|
|
|
{
|
|
|
struct compat_ethtool_rxnfc __user *compat_rxnfc;
|
|
|
bool convert_in = false, convert_out = false;
|
|
|
- size_t buf_size = ALIGN(sizeof(struct ifreq), 8);
|
|
|
- struct ethtool_rxnfc __user *rxnfc;
|
|
|
- struct ifreq __user *ifr;
|
|
|
+ size_t buf_size = 0;
|
|
|
+ struct ethtool_rxnfc __user *rxnfc = NULL;
|
|
|
+ struct ifreq ifr;
|
|
|
u32 rule_cnt = 0, actual_rule_cnt;
|
|
|
u32 ethcmd;
|
|
|
u32 data;
|
|
@@ -2786,18 +2748,14 @@ static int ethtool_ioctl(struct net *net, struct compat_ifreq __user *ifr32)
|
|
|
case ETHTOOL_SRXCLSRLDEL:
|
|
|
buf_size += sizeof(struct ethtool_rxnfc);
|
|
|
convert_in = true;
|
|
|
+ rxnfc = compat_alloc_user_space(buf_size);
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
- ifr = compat_alloc_user_space(buf_size);
|
|
|
- rxnfc = (void __user *)ifr + ALIGN(sizeof(struct ifreq), 8);
|
|
|
-
|
|
|
- if (copy_in_user(&ifr->ifr_name, &ifr32->ifr_name, IFNAMSIZ))
|
|
|
+ if (copy_from_user(&ifr.ifr_name, &ifr32->ifr_name, IFNAMSIZ))
|
|
|
return -EFAULT;
|
|
|
|
|
|
- if (put_user(convert_in ? rxnfc : compat_ptr(data),
|
|
|
- &ifr->ifr_ifru.ifru_data))
|
|
|
- return -EFAULT;
|
|
|
+ ifr.ifr_data = convert_in ? rxnfc : (void __user *)compat_rxnfc;
|
|
|
|
|
|
if (convert_in) {
|
|
|
/* We expect there to be holes between fs.m_ext and
|
|
@@ -2825,7 +2783,7 @@ static int ethtool_ioctl(struct net *net, struct compat_ifreq __user *ifr32)
|
|
|
return -EFAULT;
|
|
|
}
|
|
|
|
|
|
- ret = dev_ioctl(net, SIOCETHTOOL, ifr);
|
|
|
+ ret = dev_ioctl(net, SIOCETHTOOL, &ifr, NULL);
|
|
|
if (ret)
|
|
|
return ret;
|
|
|
|
|
@@ -2866,113 +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)
|
|
|
{
|
|
|
- void __user *uptr;
|
|
|
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_in_user(uifr, uifr32, sizeof(struct compat_ifreq)))
|
|
|
+ if (copy_from_user(&ifr, uifr32, sizeof(struct compat_ifreq)))
|
|
|
return -EFAULT;
|
|
|
|
|
|
if (get_user(uptr32, &uifr32->ifr_settings.ifs_ifsu))
|
|
|
return -EFAULT;
|
|
|
|
|
|
- uptr = compat_ptr(uptr32);
|
|
|
-
|
|
|
- if (put_user(uptr, &uifr->ifr_settings.ifs_ifsu.raw_hdlc))
|
|
|
- return -EFAULT;
|
|
|
-
|
|
|
- return dev_ioctl(net, SIOCWANDEV, uifr);
|
|
|
-}
|
|
|
-
|
|
|
-static int bond_ioctl(struct net *net, unsigned int cmd,
|
|
|
- struct compat_ifreq __user *ifr32)
|
|
|
-{
|
|
|
- struct ifreq kifr;
|
|
|
- mm_segment_t old_fs;
|
|
|
- int err;
|
|
|
-
|
|
|
- switch (cmd) {
|
|
|
- case SIOCBONDENSLAVE:
|
|
|
- case SIOCBONDRELEASE:
|
|
|
- case SIOCBONDSETHWADDR:
|
|
|
- case SIOCBONDCHANGEACTIVE:
|
|
|
- if (copy_from_user(&kifr, ifr32, sizeof(struct compat_ifreq)))
|
|
|
- return -EFAULT;
|
|
|
-
|
|
|
- old_fs = get_fs();
|
|
|
- set_fs(KERNEL_DS);
|
|
|
- err = dev_ioctl(net, cmd,
|
|
|
- (struct ifreq __user __force *) &kifr);
|
|
|
- set_fs(old_fs);
|
|
|
+ saved = ifr.ifr_settings.ifs_ifsu.raw_hdlc;
|
|
|
+ ifr.ifr_settings.ifs_ifsu.raw_hdlc = compat_ptr(uptr32);
|
|
|
|
|
|
- return err;
|
|
|
- default:
|
|
|
- return -ENOIOCTLCMD;
|
|
|
+ err = dev_ioctl(net, SIOCWANDEV, &ifr, NULL);
|
|
|
+ if (!err) {
|
|
|
+ ifr.ifr_settings.ifs_ifsu.raw_hdlc = saved;
|
|
|
+ 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 */
|
|
|
static int compat_ifr_data_ioctl(struct net *net, unsigned int cmd,
|
|
|
struct compat_ifreq __user *u_ifreq32)
|
|
|
{
|
|
|
- struct ifreq __user *u_ifreq64;
|
|
|
- char tmp_buf[IFNAMSIZ];
|
|
|
- void __user *data64;
|
|
|
+ struct ifreq ifreq;
|
|
|
u32 data32;
|
|
|
|
|
|
- if (copy_from_user(&tmp_buf[0], &(u_ifreq32->ifr_ifrn.ifrn_name[0]),
|
|
|
- IFNAMSIZ))
|
|
|
- return -EFAULT;
|
|
|
- if (get_user(data32, &u_ifreq32->ifr_ifru.ifru_data))
|
|
|
- return -EFAULT;
|
|
|
- data64 = compat_ptr(data32);
|
|
|
-
|
|
|
- u_ifreq64 = compat_alloc_user_space(sizeof(*u_ifreq64));
|
|
|
-
|
|
|
- if (copy_to_user(&u_ifreq64->ifr_ifrn.ifrn_name[0], &tmp_buf[0],
|
|
|
- IFNAMSIZ))
|
|
|
+ if (copy_from_user(ifreq.ifr_name, u_ifreq32->ifr_name, IFNAMSIZ))
|
|
|
return -EFAULT;
|
|
|
- if (put_user(data64, &u_ifreq64->ifr_ifru.ifru_data))
|
|
|
+ if (get_user(data32, &u_ifreq32->ifr_data))
|
|
|
return -EFAULT;
|
|
|
+ ifreq.ifr_data = compat_ptr(data32);
|
|
|
|
|
|
- return dev_ioctl(net, cmd, u_ifreq64);
|
|
|
-}
|
|
|
-
|
|
|
-static int dev_ifsioc(struct net *net, struct socket *sock,
|
|
|
- unsigned int cmd, struct compat_ifreq __user *uifr32)
|
|
|
-{
|
|
|
- struct ifreq __user *uifr;
|
|
|
- int err;
|
|
|
-
|
|
|
- uifr = compat_alloc_user_space(sizeof(*uifr));
|
|
|
- if (copy_in_user(uifr, uifr32, sizeof(*uifr32)))
|
|
|
- return -EFAULT;
|
|
|
-
|
|
|
- err = sock_do_ioctl(net, sock, cmd, (unsigned long)uifr);
|
|
|
-
|
|
|
- if (!err) {
|
|
|
- switch (cmd) {
|
|
|
- case SIOCGIFFLAGS:
|
|
|
- case SIOCGIFMETRIC:
|
|
|
- case SIOCGIFMTU:
|
|
|
- case SIOCGIFMEM:
|
|
|
- case SIOCGIFHWADDR:
|
|
|
- case SIOCGIFINDEX:
|
|
|
- case SIOCGIFADDR:
|
|
|
- case SIOCGIFBRDADDR:
|
|
|
- case SIOCGIFDSTADDR:
|
|
|
- case SIOCGIFNETMASK:
|
|
|
- case SIOCGIFPFLAGS:
|
|
|
- case SIOCGIFTXQLEN:
|
|
|
- case SIOCGMIIPHY:
|
|
|
- case SIOCGMIIREG:
|
|
|
- if (copy_in_user(uifr32, uifr, sizeof(*uifr32)))
|
|
|
- err = -EFAULT;
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- return err;
|
|
|
+ return dev_ioctl(net, cmd, &ifreq, NULL);
|
|
|
}
|
|
|
|
|
|
static int compat_sioc_ifmap(struct net *net, unsigned int cmd,
|
|
@@ -2980,7 +2868,6 @@ static int compat_sioc_ifmap(struct net *net, unsigned int cmd,
|
|
|
{
|
|
|
struct ifreq ifr;
|
|
|
struct compat_ifmap __user *uifmap32;
|
|
|
- mm_segment_t old_fs;
|
|
|
int err;
|
|
|
|
|
|
uifmap32 = &uifr32->ifr_ifru.ifru_map;
|
|
@@ -2994,10 +2881,7 @@ static int compat_sioc_ifmap(struct net *net, unsigned int cmd,
|
|
|
if (err)
|
|
|
return -EFAULT;
|
|
|
|
|
|
- old_fs = get_fs();
|
|
|
- set_fs(KERNEL_DS);
|
|
|
- err = dev_ioctl(net, cmd, (void __user __force *)&ifr);
|
|
|
- set_fs(old_fs);
|
|
|
+ err = dev_ioctl(net, cmd, &ifr, NULL);
|
|
|
|
|
|
if (cmd == SIOCGIFMAP && !err) {
|
|
|
err = copy_to_user(uifr32, &ifr, sizeof(ifr.ifr_name));
|
|
@@ -3130,10 +3014,8 @@ static int compat_sock_ioctl_trans(struct file *file, struct socket *sock,
|
|
|
case SIOCSIFBR:
|
|
|
case SIOCGIFBR:
|
|
|
return old_bridge_ioctl(argp);
|
|
|
- case SIOCGIFNAME:
|
|
|
- return dev_ifname32(net, argp);
|
|
|
case SIOCGIFCONF:
|
|
|
- return dev_ifconf(net, argp);
|
|
|
+ return compat_dev_ifconf(net, argp);
|
|
|
case SIOCETHTOOL:
|
|
|
return ethtool_ioctl(net, argp);
|
|
|
case SIOCWANDEV:
|
|
@@ -3141,11 +3023,6 @@ static int compat_sock_ioctl_trans(struct file *file, struct socket *sock,
|
|
|
case SIOCGIFMAP:
|
|
|
case SIOCSIFMAP:
|
|
|
return compat_sioc_ifmap(net, cmd, argp);
|
|
|
- case SIOCBONDENSLAVE:
|
|
|
- case SIOCBONDRELEASE:
|
|
|
- case SIOCBONDSETHWADDR:
|
|
|
- case SIOCBONDCHANGEACTIVE:
|
|
|
- return bond_ioctl(net, cmd, argp);
|
|
|
case SIOCADDRT:
|
|
|
case SIOCDELRT:
|
|
|
return routing_ioctl(net, sock, cmd, argp);
|
|
@@ -3205,12 +3082,15 @@ static int compat_sock_ioctl_trans(struct file *file, struct socket *sock,
|
|
|
case SIOCGMIIPHY:
|
|
|
case SIOCGMIIREG:
|
|
|
case SIOCSMIIREG:
|
|
|
- return dev_ifsioc(net, sock, cmd, argp);
|
|
|
-
|
|
|
case SIOCSARP:
|
|
|
case SIOCGARP:
|
|
|
case SIOCDARP:
|
|
|
case SIOCATMARK:
|
|
|
+ case SIOCBONDENSLAVE:
|
|
|
+ case SIOCBONDRELEASE:
|
|
|
+ case SIOCBONDSETHWADDR:
|
|
|
+ case SIOCBONDCHANGEACTIVE:
|
|
|
+ case SIOCGIFNAME:
|
|
|
return sock_do_ioctl(net, sock, cmd, arg);
|
|
|
}
|
|
|
|
|
@@ -3365,19 +3245,6 @@ int kernel_sendpage_locked(struct sock *sk, struct page *page, int offset,
|
|
|
}
|
|
|
EXPORT_SYMBOL(kernel_sendpage_locked);
|
|
|
|
|
|
-int kernel_sock_ioctl(struct socket *sock, int cmd, unsigned long arg)
|
|
|
-{
|
|
|
- mm_segment_t oldfs = get_fs();
|
|
|
- int err;
|
|
|
-
|
|
|
- set_fs(KERNEL_DS);
|
|
|
- err = sock->ops->ioctl(sock, cmd, arg);
|
|
|
- set_fs(oldfs);
|
|
|
-
|
|
|
- return err;
|
|
|
-}
|
|
|
-EXPORT_SYMBOL(kernel_sock_ioctl);
|
|
|
-
|
|
|
int kernel_sock_shutdown(struct socket *sock, enum sock_shutdown_cmd how)
|
|
|
{
|
|
|
return sock->ops->shutdown(sock, how);
|