mirror of https://gitee.com/openkylin/linux.git
ethtool: Compat handling for struct ethtool_rxnfc
This structure was accidentally defined such that its layout can differ between 32-bit and 64-bit processes. Add compat structure definitions and an ioctl wrapper function. Signed-off-by: Ben Hutchings <bhutchings@solarflare.com> Acked-by: Alexander Duyck <alexander.h.duyck@intel.com> Cc: stable@kernel.org [2.6.30+] Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
5e5069b41d
commit
3a7da39d16
|
@ -13,6 +13,9 @@
|
||||||
#ifndef _LINUX_ETHTOOL_H
|
#ifndef _LINUX_ETHTOOL_H
|
||||||
#define _LINUX_ETHTOOL_H
|
#define _LINUX_ETHTOOL_H
|
||||||
|
|
||||||
|
#ifdef __KERNEL__
|
||||||
|
#include <linux/compat.h>
|
||||||
|
#endif
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/if_ether.h>
|
#include <linux/if_ether.h>
|
||||||
|
|
||||||
|
@ -450,6 +453,37 @@ struct ethtool_rxnfc {
|
||||||
__u32 rule_locs[0];
|
__u32 rule_locs[0];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef __KERNEL__
|
||||||
|
#ifdef CONFIG_COMPAT
|
||||||
|
|
||||||
|
struct compat_ethtool_rx_flow_spec {
|
||||||
|
u32 flow_type;
|
||||||
|
union {
|
||||||
|
struct ethtool_tcpip4_spec tcp_ip4_spec;
|
||||||
|
struct ethtool_tcpip4_spec udp_ip4_spec;
|
||||||
|
struct ethtool_tcpip4_spec sctp_ip4_spec;
|
||||||
|
struct ethtool_ah_espip4_spec ah_ip4_spec;
|
||||||
|
struct ethtool_ah_espip4_spec esp_ip4_spec;
|
||||||
|
struct ethtool_usrip4_spec usr_ip4_spec;
|
||||||
|
struct ethhdr ether_spec;
|
||||||
|
u8 hdata[72];
|
||||||
|
} h_u, m_u;
|
||||||
|
compat_u64 ring_cookie;
|
||||||
|
u32 location;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct compat_ethtool_rxnfc {
|
||||||
|
u32 cmd;
|
||||||
|
u32 flow_type;
|
||||||
|
compat_u64 data;
|
||||||
|
struct compat_ethtool_rx_flow_spec fs;
|
||||||
|
u32 rule_cnt;
|
||||||
|
u32 rule_locs[0];
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* CONFIG_COMPAT */
|
||||||
|
#endif /* __KERNEL__ */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct ethtool_rxfh_indir - command to get or set RX flow hash indirection
|
* struct ethtool_rxfh_indir - command to get or set RX flow hash indirection
|
||||||
* @cmd: Specific command number - %ETHTOOL_GRXFHINDIR or %ETHTOOL_SRXFHINDIR
|
* @cmd: Specific command number - %ETHTOOL_GRXFHINDIR or %ETHTOOL_SRXFHINDIR
|
||||||
|
|
118
net/socket.c
118
net/socket.c
|
@ -2588,23 +2588,123 @@ static int dev_ifconf(struct net *net, struct compat_ifconf __user *uifc32)
|
||||||
|
|
||||||
static int ethtool_ioctl(struct net *net, struct compat_ifreq __user *ifr32)
|
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;
|
struct ifreq __user *ifr;
|
||||||
|
u32 rule_cnt = 0, actual_rule_cnt;
|
||||||
|
u32 ethcmd;
|
||||||
u32 data;
|
u32 data;
|
||||||
void __user *datap;
|
int ret;
|
||||||
|
|
||||||
ifr = compat_alloc_user_space(sizeof(*ifr));
|
|
||||||
|
|
||||||
if (copy_in_user(&ifr->ifr_name, &ifr32->ifr_name, IFNAMSIZ))
|
|
||||||
return -EFAULT;
|
|
||||||
|
|
||||||
if (get_user(data, &ifr32->ifr_ifru.ifru_data))
|
if (get_user(data, &ifr32->ifr_ifru.ifru_data))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
datap = compat_ptr(data);
|
compat_rxnfc = compat_ptr(data);
|
||||||
if (put_user(datap, &ifr->ifr_ifru.ifru_data))
|
|
||||||
|
if (get_user(ethcmd, &compat_rxnfc->cmd))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
return dev_ioctl(net, SIOCETHTOOL, ifr);
|
/* Most ethtool structures are defined without padding.
|
||||||
|
* Unfortunately struct ethtool_rxnfc is an exception.
|
||||||
|
*/
|
||||||
|
switch (ethcmd) {
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
case ETHTOOL_GRXCLSRLALL:
|
||||||
|
/* Buffer size is variable */
|
||||||
|
if (get_user(rule_cnt, &compat_rxnfc->rule_cnt))
|
||||||
|
return -EFAULT;
|
||||||
|
if (rule_cnt > KMALLOC_MAX_SIZE / sizeof(u32))
|
||||||
|
return -ENOMEM;
|
||||||
|
buf_size += rule_cnt * sizeof(u32);
|
||||||
|
/* fall through */
|
||||||
|
case ETHTOOL_GRXRINGS:
|
||||||
|
case ETHTOOL_GRXCLSRLCNT:
|
||||||
|
case ETHTOOL_GRXCLSRULE:
|
||||||
|
convert_out = true;
|
||||||
|
/* fall through */
|
||||||
|
case ETHTOOL_SRXCLSRLDEL:
|
||||||
|
case ETHTOOL_SRXCLSRLINS:
|
||||||
|
buf_size += sizeof(struct ethtool_rxnfc);
|
||||||
|
convert_in = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ifr = compat_alloc_user_space(buf_size);
|
||||||
|
rxnfc = (void *)ifr + ALIGN(sizeof(struct ifreq), 8);
|
||||||
|
|
||||||
|
if (copy_in_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;
|
||||||
|
|
||||||
|
if (convert_in) {
|
||||||
|
/* We expect there to be holes between fs.m_u and
|
||||||
|
* fs.ring_cookie and at the end of fs, but nowhere else.
|
||||||
|
*/
|
||||||
|
BUILD_BUG_ON(offsetof(struct compat_ethtool_rxnfc, fs.m_u) +
|
||||||
|
sizeof(compat_rxnfc->fs.m_u) !=
|
||||||
|
offsetof(struct ethtool_rxnfc, fs.m_u) +
|
||||||
|
sizeof(rxnfc->fs.m_u));
|
||||||
|
BUILD_BUG_ON(
|
||||||
|
offsetof(struct compat_ethtool_rxnfc, fs.location) -
|
||||||
|
offsetof(struct compat_ethtool_rxnfc, fs.ring_cookie) !=
|
||||||
|
offsetof(struct ethtool_rxnfc, fs.location) -
|
||||||
|
offsetof(struct ethtool_rxnfc, fs.ring_cookie));
|
||||||
|
|
||||||
|
if (copy_in_user(rxnfc, compat_rxnfc,
|
||||||
|
(void *)(&rxnfc->fs.m_u + 1) -
|
||||||
|
(void *)rxnfc) ||
|
||||||
|
copy_in_user(&rxnfc->fs.ring_cookie,
|
||||||
|
&compat_rxnfc->fs.ring_cookie,
|
||||||
|
(void *)(&rxnfc->fs.location + 1) -
|
||||||
|
(void *)&rxnfc->fs.ring_cookie) ||
|
||||||
|
copy_in_user(&rxnfc->rule_cnt, &compat_rxnfc->rule_cnt,
|
||||||
|
sizeof(rxnfc->rule_cnt)))
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = dev_ioctl(net, SIOCETHTOOL, ifr);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (convert_out) {
|
||||||
|
if (copy_in_user(compat_rxnfc, rxnfc,
|
||||||
|
(const void *)(&rxnfc->fs.m_u + 1) -
|
||||||
|
(const void *)rxnfc) ||
|
||||||
|
copy_in_user(&compat_rxnfc->fs.ring_cookie,
|
||||||
|
&rxnfc->fs.ring_cookie,
|
||||||
|
(const void *)(&rxnfc->fs.location + 1) -
|
||||||
|
(const void *)&rxnfc->fs.ring_cookie) ||
|
||||||
|
copy_in_user(&compat_rxnfc->rule_cnt, &rxnfc->rule_cnt,
|
||||||
|
sizeof(rxnfc->rule_cnt)))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
if (ethcmd == ETHTOOL_GRXCLSRLALL) {
|
||||||
|
/* As an optimisation, we only copy the actual
|
||||||
|
* number of rules that the underlying
|
||||||
|
* function returned. Since Mallory might
|
||||||
|
* change the rule count in user memory, we
|
||||||
|
* check that it is less than the rule count
|
||||||
|
* originally given (as the user buffer size),
|
||||||
|
* which has been range-checked.
|
||||||
|
*/
|
||||||
|
if (get_user(actual_rule_cnt, &rxnfc->rule_cnt))
|
||||||
|
return -EFAULT;
|
||||||
|
if (actual_rule_cnt < rule_cnt)
|
||||||
|
rule_cnt = actual_rule_cnt;
|
||||||
|
if (copy_in_user(&compat_rxnfc->rule_locs[0],
|
||||||
|
&rxnfc->rule_locs[0],
|
||||||
|
rule_cnt * sizeof(u32)))
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int compat_siocwandev(struct net *net, struct compat_ifreq __user *uifr32)
|
static int compat_siocwandev(struct net *net, struct compat_ifreq __user *uifr32)
|
||||||
|
|
Loading…
Reference in New Issue