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:
Родитель
5e5069b41d
Коммит
3a7da39d16
|
@ -13,6 +13,9 @@
|
|||
#ifndef _LINUX_ETHTOOL_H
|
||||
#define _LINUX_ETHTOOL_H
|
||||
|
||||
#ifdef __KERNEL__
|
||||
#include <linux/compat.h>
|
||||
#endif
|
||||
#include <linux/types.h>
|
||||
#include <linux/if_ether.h>
|
||||
|
||||
|
@ -450,6 +453,37 @@ struct ethtool_rxnfc {
|
|||
__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
|
||||
* @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)
|
||||
{
|
||||
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;
|
||||
u32 rule_cnt = 0, actual_rule_cnt;
|
||||
u32 ethcmd;
|
||||
u32 data;
|
||||
void __user *datap;
|
||||
|
||||
ifr = compat_alloc_user_space(sizeof(*ifr));
|
||||
|
||||
if (copy_in_user(&ifr->ifr_name, &ifr32->ifr_name, IFNAMSIZ))
|
||||
return -EFAULT;
|
||||
int ret;
|
||||
|
||||
if (get_user(data, &ifr32->ifr_ifru.ifru_data))
|
||||
return -EFAULT;
|
||||
|
||||
datap = compat_ptr(data);
|
||||
if (put_user(datap, &ifr->ifr_ifru.ifru_data))
|
||||
compat_rxnfc = compat_ptr(data);
|
||||
|
||||
if (get_user(ethcmd, &compat_rxnfc->cmd))
|
||||
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)
|
||||
|
|
Загрузка…
Ссылка в новой задаче