Merge branch 'net-remove-compat-alloc-user-space'

Arnd Bergmann says:

====================
remove compat_alloc_user_space()

This is the fifth version of my series, now spanning four patches
instead of two, with a new approach for handling struct ifreq
compatibility after I realized that my earlier approach introduces
additional problems.

The idea here is to always push down the compat conversion
deeper into the call stack: rather than pretending to be
native mode with a modified copy of the original data on
the user space stack, have the code that actually works on
the data understand the difference between native and compat
versions.

I have spent a long time looking at all drivers that implement
an ndo_do_ioctl callback to verify that my assumptions are
correct. This has led to a series of ~30 additional patches
that I am not including here but will post separately, fixing
a number of bugs in SIOCDEVPRIVATE ioctls, removing dead
code, and splitting ndo_do_ioctl into multiple new ndo callbacks
for private and ethernet specific commands.

      Arnd

Link: https://lore.kernel.org/netdev/20201124151828.169152-1-arnd@kernel.org/

Changes in v6:
 - Split out and expand linux/compat.h rework
 - Split ifconf change into two patches
 - Rebase on latest net-next/master

Changes in v5:
 - Rebase to v5.14-rc2
 - Fix a few build issues

Changes in v4:
 - build fix without CONFIG_INET
 - build fix without CONFIG_COMPAT
 - style fixes pointed out by hch

Changes in v3:
 - complete rewrite of the series
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2021-07-23 14:20:46 +01:00
commit 090597b4a9
21 changed files with 372 additions and 451 deletions

View File

@ -5,6 +5,9 @@
#ifndef __ASM_COMPAT_H
#define __ASM_COMPAT_H
#define compat_mode_t compat_mode_t
typedef u16 compat_mode_t;
#include <asm-generic/compat.h>
#ifdef CONFIG_COMPAT
@ -27,13 +30,9 @@ typedef u16 __compat_uid_t;
typedef u16 __compat_gid_t;
typedef u16 __compat_uid16_t;
typedef u16 __compat_gid16_t;
typedef u32 __compat_uid32_t;
typedef u32 __compat_gid32_t;
typedef u16 compat_mode_t;
typedef u32 compat_dev_t;
typedef s32 compat_nlink_t;
typedef u16 compat_ipc_pid_t;
typedef u32 compat_caddr_t;
typedef __kernel_fsid_t compat_fsid_t;
struct compat_stat {
@ -103,13 +102,6 @@ struct compat_statfs {
#define COMPAT_RLIM_INFINITY 0xffffffff
typedef u32 compat_old_sigset_t;
#define _COMPAT_NSIG 64
#define _COMPAT_NSIG_BPW 32
typedef u32 compat_sigset_word;
#define COMPAT_OFF_T_MAX 0x7fffffff
#define compat_user_stack_pointer() (user_stack_pointer(task_pt_regs(current)))

View File

@ -9,20 +9,25 @@
#include <asm/page.h>
#include <asm/ptrace.h>
typedef s32 __compat_uid_t;
typedef s32 __compat_gid_t;
typedef __compat_uid_t __compat_uid32_t;
typedef __compat_gid_t __compat_gid32_t;
#define __compat_uid32_t __compat_uid32_t
#define __compat_gid32_t __compat_gid32_t
#define _COMPAT_NSIG 128 /* Don't ask !$@#% ... */
#define _COMPAT_NSIG_BPW 32
typedef u32 compat_sigset_word;
#include <asm-generic/compat.h>
#define COMPAT_USER_HZ 100
#define COMPAT_UTS_MACHINE "mips\0\0\0"
typedef s32 __compat_uid_t;
typedef s32 __compat_gid_t;
typedef __compat_uid_t __compat_uid32_t;
typedef __compat_gid_t __compat_gid32_t;
typedef u32 compat_mode_t;
typedef u32 compat_dev_t;
typedef u32 compat_nlink_t;
typedef s32 compat_ipc_pid_t;
typedef s32 compat_caddr_t;
typedef struct {
s32 val[2];
} compat_fsid_t;
@ -89,13 +94,6 @@ struct compat_statfs {
#define COMPAT_RLIM_INFINITY 0x7fffffffUL
typedef u32 compat_old_sigset_t; /* at least 32 bits */
#define _COMPAT_NSIG 128 /* Don't ask !$@#% ... */
#define _COMPAT_NSIG_BPW 32
typedef u32 compat_sigset_word;
#define COMPAT_OFF_T_MAX 0x7fffffff
static inline void __user *arch_compat_alloc_user_space(long len)

View File

@ -8,6 +8,9 @@
#include <linux/sched.h>
#include <linux/thread_info.h>
#define compat_mode_t compat_mode_t
typedef u16 compat_mode_t;
#include <asm-generic/compat.h>
#define COMPAT_USER_HZ 100
@ -15,13 +18,9 @@
typedef u32 __compat_uid_t;
typedef u32 __compat_gid_t;
typedef u32 __compat_uid32_t;
typedef u32 __compat_gid32_t;
typedef u16 compat_mode_t;
typedef u32 compat_dev_t;
typedef u16 compat_nlink_t;
typedef u16 compat_ipc_pid_t;
typedef u32 compat_caddr_t;
struct compat_stat {
compat_dev_t st_dev; /* dev_t is 32 bits on parisc */
@ -96,13 +95,6 @@ struct compat_sigcontext {
#define COMPAT_RLIM_INFINITY 0xffffffff
typedef u32 compat_old_sigset_t; /* at least 32 bits */
#define _COMPAT_NSIG 64
#define _COMPAT_NSIG_BPW 32
typedef u32 compat_sigset_word;
#define COMPAT_OFF_T_MAX 0x7fffffff
struct compat_ipc64_perm {

View File

@ -19,13 +19,9 @@
typedef u32 __compat_uid_t;
typedef u32 __compat_gid_t;
typedef u32 __compat_uid32_t;
typedef u32 __compat_gid32_t;
typedef u32 compat_mode_t;
typedef u32 compat_dev_t;
typedef s16 compat_nlink_t;
typedef u16 compat_ipc_pid_t;
typedef u32 compat_caddr_t;
typedef __kernel_fsid_t compat_fsid_t;
struct compat_stat {
@ -85,13 +81,6 @@ struct compat_statfs {
#define COMPAT_RLIM_INFINITY 0xffffffff
typedef u32 compat_old_sigset_t;
#define _COMPAT_NSIG 64
#define _COMPAT_NSIG_BPW 32
typedef u32 compat_sigset_word;
#define COMPAT_OFF_T_MAX 0x7fffffff
static inline void __user *arch_compat_alloc_user_space(long len)

View File

@ -9,6 +9,9 @@
#include <linux/sched/task_stack.h>
#include <linux/thread_info.h>
#define compat_mode_t compat_mode_t
typedef u16 compat_mode_t;
#include <asm-generic/compat.h>
#define __TYPE_IS_PTR(t) (!__builtin_types_compatible_p( \
@ -55,13 +58,9 @@
typedef u16 __compat_uid_t;
typedef u16 __compat_gid_t;
typedef u32 __compat_uid32_t;
typedef u32 __compat_gid32_t;
typedef u16 compat_mode_t;
typedef u16 compat_dev_t;
typedef u16 compat_nlink_t;
typedef u16 compat_ipc_pid_t;
typedef u32 compat_caddr_t;
typedef __kernel_fsid_t compat_fsid_t;
typedef struct {
@ -155,13 +154,6 @@ struct compat_statfs64 {
#define COMPAT_RLIM_INFINITY 0xffffffff
typedef u32 compat_old_sigset_t; /* at least 32 bits */
#define _COMPAT_NSIG 64
#define _COMPAT_NSIG_BPW 32
typedef u32 compat_sigset_word;
#define COMPAT_OFF_T_MAX 0x7fffffff
/*

View File

@ -6,6 +6,9 @@
*/
#include <linux/types.h>
#define compat_mode_t compat_mode_t
typedef u16 compat_mode_t;
#include <asm-generic/compat.h>
#define COMPAT_USER_HZ 100
@ -13,13 +16,9 @@
typedef u16 __compat_uid_t;
typedef u16 __compat_gid_t;
typedef u32 __compat_uid32_t;
typedef u32 __compat_gid32_t;
typedef u16 compat_mode_t;
typedef u16 compat_dev_t;
typedef s16 compat_nlink_t;
typedef u16 compat_ipc_pid_t;
typedef u32 compat_caddr_t;
typedef __kernel_fsid_t compat_fsid_t;
struct compat_stat {
@ -115,13 +114,6 @@ struct compat_statfs {
#define COMPAT_RLIM_INFINITY 0x7fffffff
typedef u32 compat_old_sigset_t;
#define _COMPAT_NSIG 64
#define _COMPAT_NSIG_BPW 32
typedef u32 compat_sigset_word;
#define COMPAT_OFF_T_MAX 0x7fffffff
#ifdef CONFIG_COMPAT

View File

@ -12,6 +12,9 @@
#include <asm/user32.h>
#include <asm/unistd.h>
#define compat_mode_t compat_mode_t
typedef u16 compat_mode_t;
#include <asm-generic/compat.h>
#define COMPAT_USER_HZ 100
@ -19,13 +22,9 @@
typedef u16 __compat_uid_t;
typedef u16 __compat_gid_t;
typedef u32 __compat_uid32_t;
typedef u32 __compat_gid32_t;
typedef u16 compat_mode_t;
typedef u16 compat_dev_t;
typedef u16 compat_nlink_t;
typedef u16 compat_ipc_pid_t;
typedef u32 compat_caddr_t;
typedef __kernel_fsid_t compat_fsid_t;
struct compat_stat {
@ -92,13 +91,6 @@ struct compat_statfs {
#define COMPAT_RLIM_INFINITY 0xffffffff
typedef u32 compat_old_sigset_t; /* at least 32 bits */
#define _COMPAT_NSIG 64
#define _COMPAT_NSIG_BPW 32
typedef u32 compat_sigset_word;
#define COMPAT_OFF_T_MAX 0x7fffffff
struct compat_ipc64_perm {

View File

@ -29,6 +29,7 @@ typedef struct {
#define SA_X32_ABI 0x01000000u
#ifndef CONFIG_COMPAT
#define compat_sigset_t compat_sigset_t
typedef sigset_t compat_sigset_t;
#endif

View File

@ -20,7 +20,18 @@ typedef u16 compat_ushort_t;
typedef u32 compat_uint_t;
typedef u32 compat_ulong_t;
typedef u32 compat_uptr_t;
typedef u32 compat_caddr_t;
typedef u32 compat_aio_context_t;
typedef u32 compat_old_sigset_t;
#ifndef __compat_uid32_t
typedef u32 __compat_uid32_t;
typedef u32 __compat_gid32_t;
#endif
#ifndef compat_mode_t
typedef u32 compat_mode_t;
#endif
#ifdef CONFIG_COMPAT_FOR_U64_ALIGNMENT
typedef s64 __attribute__((aligned(4))) compat_s64;
@ -30,4 +41,10 @@ typedef s64 compat_s64;
typedef u64 compat_u64;
#endif
#ifndef _COMPAT_NSIG
typedef u32 compat_sigset_word;
#define _COMPAT_NSIG _NSIG
#define _COMPAT_NSIG_BPW 32
#endif
#endif

View File

@ -20,11 +20,8 @@
#include <linux/unistd.h>
#include <asm/compat.h>
#ifdef CONFIG_COMPAT
#include <asm/siginfo.h>
#include <asm/signal.h>
#endif
#ifdef CONFIG_ARCH_HAS_SYSCALL_WRAPPER
/*
@ -95,8 +92,6 @@ struct compat_iovec {
compat_size_t iov_len;
};
#ifdef CONFIG_COMPAT
#ifndef compat_user_stack_pointer
#define compat_user_stack_pointer() current_user_stack_pointer()
#endif
@ -131,9 +126,11 @@ struct compat_tms {
#define _COMPAT_NSIG_WORDS (_COMPAT_NSIG / _COMPAT_NSIG_BPW)
#ifndef compat_sigset_t
typedef struct {
compat_sigset_word sig[_COMPAT_NSIG_WORDS];
} compat_sigset_t;
#endif
int set_compat_user_sigmask(const compat_sigset_t __user *umask,
size_t sigsetsize);
@ -384,6 +381,7 @@ struct compat_keyctl_kdf_params {
__u32 __spare[8];
};
struct compat_stat;
struct compat_statfs;
struct compat_statfs64;
struct compat_old_linux_dirent;
@ -428,7 +426,7 @@ put_compat_sigset(compat_sigset_t __user *compat, const sigset_t *set,
unsigned int size)
{
/* size <= sizeof(compat_sigset_t) <= sizeof(sigset_t) */
#ifdef __BIG_ENDIAN
#if defined(__BIG_ENDIAN) && defined(CONFIG_64BIT)
compat_sigset_t v;
switch (_NSIG_WORDS) {
case 4: v.sig[7] = (set->sig[3] >> 32); v.sig[6] = set->sig[3];
@ -929,17 +927,6 @@ asmlinkage long compat_sys_socketcall(int call, u32 __user *args);
#endif /* CONFIG_ARCH_HAS_SYSCALL_WRAPPER */
/*
* For most but not all architectures, "am I in a compat syscall?" and
* "am I a compat task?" are the same question. For architectures on which
* they aren't the same question, arch code can override in_compat_syscall.
*/
#ifndef in_compat_syscall
static inline bool in_compat_syscall(void) { return is_compat_task(); }
#endif
/**
* ns_to_old_timeval32 - Compat version of ns_to_timeval
* @nsec: the nanoseconds value to be converted
@ -969,6 +956,17 @@ int kcompat_sys_statfs64(const char __user * pathname, compat_size_t sz,
int kcompat_sys_fstatfs64(unsigned int fd, compat_size_t sz,
struct compat_statfs64 __user * buf);
#ifdef CONFIG_COMPAT
/*
* For most but not all architectures, "am I in a compat syscall?" and
* "am I a compat task?" are the same question. For architectures on which
* they aren't the same question, arch code can override in_compat_syscall.
*/
#ifndef in_compat_syscall
static inline bool in_compat_syscall(void) { return is_compat_task(); }
#endif
#else /* !CONFIG_COMPAT */
#define is_compat_task() (0)

View File

@ -17,8 +17,6 @@
#include <linux/compat.h>
#include <uapi/linux/ethtool.h>
#ifdef CONFIG_COMPAT
struct compat_ethtool_rx_flow_spec {
u32 flow_type;
union ethtool_flow_union h_u;
@ -38,8 +36,6 @@ struct compat_ethtool_rxnfc {
u32 rule_locs[];
};
#endif /* CONFIG_COMPAT */
#include <linux/rculist.h>
/**

View File

@ -178,6 +178,15 @@ static inline struct net_device *ip_dev_find(struct net *net, __be32 addr)
int inet_addr_onlink(struct in_device *in_dev, __be32 a, __be32 b);
int devinet_ioctl(struct net *net, unsigned int cmd, struct ifreq *);
#ifdef CONFIG_INET
int inet_gifconf(struct net_device *dev, char __user *buf, int len, int size);
#else
static inline int inet_gifconf(struct net_device *dev, char __user *buf,
int len, int size)
{
return 0;
}
#endif
void devinet_init(void);
struct in_device *inetdev_by_index(struct net *, int);
__be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope);

View File

@ -3289,14 +3289,6 @@ static inline bool dev_has_header(const struct net_device *dev)
return dev->header_ops && dev->header_ops->create;
}
typedef int gifconf_func_t(struct net_device * dev, char __user * bufptr,
int len, int size);
int register_gifconf(unsigned int family, gifconf_func_t *gifconf);
static inline int unregister_gifconf(unsigned int family)
{
return register_gifconf(family, NULL);
}
#ifdef CONFIG_NET_FLOW_LIMIT
#define FLOW_LIMIT_HISTORY (1 << 7) /* must be ^2 and !overflow buckets */
struct sd_flow_limit {
@ -4014,9 +4006,11 @@ int netdev_rx_handler_register(struct net_device *dev,
void netdev_rx_handler_unregister(struct net_device *dev);
bool dev_valid_name(const char *name);
int get_user_ifreq(struct ifreq *ifr, void __user **ifrdata, 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,
bool *need_copyout);
int dev_ifconf(struct net *net, struct ifconf *, int);
int dev_ifconf(struct net *net, struct ifconf __user *ifc);
int dev_ethtool(struct net *net, struct ifreq *);
unsigned int dev_get_flags(const struct net_device *);
int __dev_change_flags(struct net_device *dev, unsigned int flags,

View File

@ -666,7 +666,7 @@ static int atif_ioctl(int cmd, void __user *arg)
struct rtentry rtdef;
int add_route;
if (copy_from_user(&atreq, arg, sizeof(atreq)))
if (get_user_ifreq(&atreq, NULL, arg))
return -EFAULT;
dev = __dev_get_by_name(&init_net, atreq.ifr_name);
@ -865,7 +865,7 @@ static int atif_ioctl(int cmd, void __user *arg)
return 0;
}
return copy_to_user(arg, &atreq, sizeof(atreq)) ? -EFAULT : 0;
return put_user_ifreq(&atreq, arg);
}
static int atrtr_ioctl_addrt(struct rtentry *rt)

View File

@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/kmod.h>
#include <linux/netdevice.h>
#include <linux/inetdevice.h>
#include <linux/etherdevice.h>
#include <linux/rtnetlink.h>
#include <linux/net_tstamp.h>
@ -25,79 +26,108 @@ static int dev_ifname(struct net *net, struct ifreq *ifr)
return netdev_get_name(net, ifr->ifr_name, ifr->ifr_ifindex);
}
static gifconf_func_t *gifconf_list[NPROTO];
/**
* register_gifconf - register a SIOCGIF handler
* @family: Address family
* @gifconf: Function handler
*
* Register protocol dependent address dumping routines. The handler
* that is passed must not be freed or reused until it has been replaced
* by another handler.
*/
int register_gifconf(unsigned int family, gifconf_func_t *gifconf)
{
if (family >= NPROTO)
return -EINVAL;
gifconf_list[family] = gifconf;
return 0;
}
EXPORT_SYMBOL(register_gifconf);
/*
* Perform a SIOCGIFCONF call. This structure will change
* size eventually, and there is nothing I can do about it.
* Thus we will need a 'compatibility mode'.
*/
int dev_ifconf(struct net *net, struct ifconf *ifc, int size)
int dev_ifconf(struct net *net, struct ifconf __user *uifc)
{
struct net_device *dev;
char __user *pos;
int len;
int total;
int i;
void __user *pos;
size_t size;
int len, total = 0, done;
/*
* Fetch the caller's info block.
*/
/* both the ifconf and the ifreq structures are slightly different */
if (in_compat_syscall()) {
struct compat_ifconf ifc32;
pos = ifc->ifc_buf;
len = ifc->ifc_len;
if (copy_from_user(&ifc32, uifc, sizeof(struct compat_ifconf)))
return -EFAULT;
/*
* Loop over the interfaces, and write an info block for each.
*/
pos = compat_ptr(ifc32.ifcbuf);
len = ifc32.ifc_len;
size = sizeof(struct compat_ifreq);
} else {
struct ifconf ifc;
total = 0;
for_each_netdev(net, dev) {
for (i = 0; i < NPROTO; i++) {
if (gifconf_list[i]) {
int done;
if (!pos)
done = gifconf_list[i](dev, NULL, 0, size);
else
done = gifconf_list[i](dev, pos + total,
len - total, size);
if (done < 0)
return -EFAULT;
total += done;
}
}
if (copy_from_user(&ifc, uifc, sizeof(struct ifconf)))
return -EFAULT;
pos = ifc.ifc_buf;
len = ifc.ifc_len;
size = sizeof(struct ifreq);
}
/*
* All done. Write the updated control block back to the caller.
*/
ifc->ifc_len = total;
/* Loop over the interfaces, and write an info block for each. */
rtnl_lock();
for_each_netdev(net, dev) {
if (!pos)
done = inet_gifconf(dev, NULL, 0, size);
else
done = inet_gifconf(dev, pos + total,
len - total, size);
if (done < 0) {
rtnl_unlock();
return -EFAULT;
}
total += done;
}
rtnl_unlock();
return put_user(total, &uifc->ifc_len);
}
static int dev_getifmap(struct net_device *dev, struct ifreq *ifr)
{
struct ifmap *ifmap = &ifr->ifr_map;
if (in_compat_syscall()) {
struct compat_ifmap *cifmap = (struct compat_ifmap *)ifmap;
cifmap->mem_start = dev->mem_start;
cifmap->mem_end = dev->mem_end;
cifmap->base_addr = dev->base_addr;
cifmap->irq = dev->irq;
cifmap->dma = dev->dma;
cifmap->port = dev->if_port;
return 0;
}
ifmap->mem_start = dev->mem_start;
ifmap->mem_end = dev->mem_end;
ifmap->base_addr = dev->base_addr;
ifmap->irq = dev->irq;
ifmap->dma = dev->dma;
ifmap->port = dev->if_port;
/*
* Both BSD and Solaris return 0 here, so we do too.
*/
return 0;
}
static int dev_setifmap(struct net_device *dev, struct ifreq *ifr)
{
struct compat_ifmap *cifmap = (struct compat_ifmap *)&ifr->ifr_map;
if (!dev->netdev_ops->ndo_set_config)
return -EOPNOTSUPP;
if (in_compat_syscall()) {
struct ifmap ifmap = {
.mem_start = cifmap->mem_start,
.mem_end = cifmap->mem_end,
.base_addr = cifmap->base_addr,
.irq = cifmap->irq,
.dma = cifmap->dma,
.port = cifmap->port,
};
return dev->netdev_ops->ndo_set_config(dev, &ifmap);
}
return dev->netdev_ops->ndo_set_config(dev, &ifr->ifr_map);
}
/*
* Perform the SIOCxIFxxx calls, inside rcu_read_lock()
*/
@ -128,13 +158,7 @@ static int dev_ifsioc_locked(struct net *net, struct ifreq *ifr, unsigned int cm
break;
case SIOCGIFMAP:
ifr->ifr_map.mem_start = dev->mem_start;
ifr->ifr_map.mem_end = dev->mem_end;
ifr->ifr_map.base_addr = dev->base_addr;
ifr->ifr_map.irq = dev->irq;
ifr->ifr_map.dma = dev->dma;
ifr->ifr_map.port = dev->if_port;
return 0;
return dev_getifmap(dev, ifr);
case SIOCGIFINDEX:
ifr->ifr_ifindex = dev->ifindex;
@ -275,12 +299,7 @@ static int dev_ifsioc(struct net *net, struct ifreq *ifr, unsigned int cmd)
return 0;
case SIOCSIFMAP:
if (ops->ndo_set_config) {
if (!netif_device_present(dev))
return -ENODEV;
return ops->ndo_set_config(dev, &ifr->ifr_map);
}
return -EOPNOTSUPP;
return dev_setifmap(dev, ifr);
case SIOCADDMULTI:
if (!ops->ndo_set_rx_mode ||

View File

@ -7,6 +7,7 @@
* the information ethtool needs.
*/
#include <linux/compat.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/capability.h>
@ -807,6 +808,120 @@ out:
return ret;
}
static noinline_for_stack int
ethtool_rxnfc_copy_from_compat(struct ethtool_rxnfc *rxnfc,
const struct compat_ethtool_rxnfc __user *useraddr,
size_t size)
{
struct compat_ethtool_rxnfc crxnfc = {};
/* We expect there to be holes between fs.m_ext and
* fs.ring_cookie and at the end of fs, but nowhere else.
* On non-x86, no conversion should be needed.
*/
BUILD_BUG_ON(!IS_ENABLED(CONFIG_X86_64) &&
sizeof(struct compat_ethtool_rxnfc) !=
sizeof(struct ethtool_rxnfc));
BUILD_BUG_ON(offsetof(struct compat_ethtool_rxnfc, fs.m_ext) +
sizeof(useraddr->fs.m_ext) !=
offsetof(struct ethtool_rxnfc, fs.m_ext) +
sizeof(rxnfc->fs.m_ext));
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_from_user(&crxnfc, useraddr, min(size, sizeof(crxnfc))))
return -EFAULT;
*rxnfc = (struct ethtool_rxnfc) {
.cmd = crxnfc.cmd,
.flow_type = crxnfc.flow_type,
.data = crxnfc.data,
.fs = {
.flow_type = crxnfc.fs.flow_type,
.h_u = crxnfc.fs.h_u,
.h_ext = crxnfc.fs.h_ext,
.m_u = crxnfc.fs.m_u,
.m_ext = crxnfc.fs.m_ext,
.ring_cookie = crxnfc.fs.ring_cookie,
.location = crxnfc.fs.location,
},
.rule_cnt = crxnfc.rule_cnt,
};
return 0;
}
static int ethtool_rxnfc_copy_from_user(struct ethtool_rxnfc *rxnfc,
const void __user *useraddr,
size_t size)
{
if (compat_need_64bit_alignment_fixup())
return ethtool_rxnfc_copy_from_compat(rxnfc, useraddr, size);
if (copy_from_user(rxnfc, useraddr, size))
return -EFAULT;
return 0;
}
static int ethtool_rxnfc_copy_to_compat(void __user *useraddr,
const struct ethtool_rxnfc *rxnfc,
size_t size, const u32 *rule_buf)
{
struct compat_ethtool_rxnfc crxnfc;
memset(&crxnfc, 0, sizeof(crxnfc));
crxnfc = (struct compat_ethtool_rxnfc) {
.cmd = rxnfc->cmd,
.flow_type = rxnfc->flow_type,
.data = rxnfc->data,
.fs = {
.flow_type = rxnfc->fs.flow_type,
.h_u = rxnfc->fs.h_u,
.h_ext = rxnfc->fs.h_ext,
.m_u = rxnfc->fs.m_u,
.m_ext = rxnfc->fs.m_ext,
.ring_cookie = rxnfc->fs.ring_cookie,
.location = rxnfc->fs.location,
},
.rule_cnt = rxnfc->rule_cnt,
};
if (copy_to_user(useraddr, &crxnfc, min(size, sizeof(crxnfc))))
return -EFAULT;
return 0;
}
static int ethtool_rxnfc_copy_to_user(void __user *useraddr,
const struct ethtool_rxnfc *rxnfc,
size_t size, const u32 *rule_buf)
{
int ret;
if (compat_need_64bit_alignment_fixup()) {
ret = ethtool_rxnfc_copy_to_compat(useraddr, rxnfc, size,
rule_buf);
useraddr += offsetof(struct compat_ethtool_rxnfc, rule_locs);
} else {
ret = copy_to_user(useraddr, &rxnfc, size);
useraddr += offsetof(struct ethtool_rxnfc, rule_locs);
}
if (ret)
return -EFAULT;
if (rule_buf) {
if (copy_to_user(useraddr, rule_buf,
rxnfc->rule_cnt * sizeof(u32)))
return -EFAULT;
}
return 0;
}
static noinline_for_stack int ethtool_set_rxnfc(struct net_device *dev,
u32 cmd, void __user *useraddr)
{
@ -825,7 +940,7 @@ static noinline_for_stack int ethtool_set_rxnfc(struct net_device *dev,
info_size = (offsetof(struct ethtool_rxnfc, data) +
sizeof(info.data));
if (copy_from_user(&info, useraddr, info_size))
if (ethtool_rxnfc_copy_from_user(&info, useraddr, info_size))
return -EFAULT;
rc = dev->ethtool_ops->set_rxnfc(dev, &info);
@ -833,7 +948,7 @@ static noinline_for_stack int ethtool_set_rxnfc(struct net_device *dev,
return rc;
if (cmd == ETHTOOL_SRXCLSRLINS &&
copy_to_user(useraddr, &info, info_size))
ethtool_rxnfc_copy_to_user(useraddr, &info, info_size, NULL))
return -EFAULT;
return 0;
@ -859,7 +974,7 @@ static noinline_for_stack int ethtool_get_rxnfc(struct net_device *dev,
info_size = (offsetof(struct ethtool_rxnfc, data) +
sizeof(info.data));
if (copy_from_user(&info, useraddr, info_size))
if (ethtool_rxnfc_copy_from_user(&info, useraddr, info_size))
return -EFAULT;
/* If FLOW_RSS was requested then user-space must be using the
@ -867,7 +982,7 @@ static noinline_for_stack int ethtool_get_rxnfc(struct net_device *dev,
*/
if (cmd == ETHTOOL_GRXFH && info.flow_type & FLOW_RSS) {
info_size = sizeof(info);
if (copy_from_user(&info, useraddr, info_size))
if (ethtool_rxnfc_copy_from_user(&info, useraddr, info_size))
return -EFAULT;
/* Since malicious users may modify the original data,
* we need to check whether FLOW_RSS is still requested.
@ -893,18 +1008,7 @@ static noinline_for_stack int ethtool_get_rxnfc(struct net_device *dev,
if (ret < 0)
goto err_out;
ret = -EFAULT;
if (copy_to_user(useraddr, &info, info_size))
goto err_out;
if (rule_buf) {
useraddr += offsetof(struct ethtool_rxnfc, rule_locs);
if (copy_to_user(useraddr, rule_buf,
info.rule_cnt * sizeof(u32)))
goto err_out;
}
ret = 0;
ret = ethtool_rxnfc_copy_to_user(useraddr, &info, info_size, rule_buf);
err_out:
kfree(rule_buf);

View File

@ -129,7 +129,7 @@ static int ieee802154_dev_ioctl(struct sock *sk, struct ifreq __user *arg,
int ret = -ENOIOCTLCMD;
struct net_device *dev;
if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
if (get_user_ifreq(&ifr, NULL, arg))
return -EFAULT;
ifr.ifr_name[IFNAMSIZ-1] = 0;
@ -143,7 +143,7 @@ static int ieee802154_dev_ioctl(struct sock *sk, struct ifreq __user *arg,
if (dev->type == ARPHRD_IEEE802154 && dev->netdev_ops->ndo_do_ioctl)
ret = dev->netdev_ops->ndo_do_ioctl(dev, &ifr, cmd);
if (!ret && copy_to_user(arg, &ifr, sizeof(struct ifreq)))
if (!ret && put_user_ifreq(&ifr, arg))
ret = -EFAULT;
dev_put(dev);

View File

@ -953,10 +953,10 @@ int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
case SIOCGIFNETMASK:
case SIOCGIFDSTADDR:
case SIOCGIFPFLAGS:
if (copy_from_user(&ifr, p, sizeof(struct ifreq)))
if (get_user_ifreq(&ifr, NULL, p))
return -EFAULT;
err = devinet_ioctl(net, cmd, &ifr);
if (!err && copy_to_user(p, &ifr, sizeof(struct ifreq)))
if (!err && put_user_ifreq(&ifr, p))
err = -EFAULT;
break;
@ -966,7 +966,7 @@ int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
case SIOCSIFDSTADDR:
case SIOCSIFPFLAGS:
case SIOCSIFFLAGS:
if (copy_from_user(&ifr, p, sizeof(struct ifreq)))
if (get_user_ifreq(&ifr, NULL, p))
return -EFAULT;
err = devinet_ioctl(net, cmd, &ifr);
break;

View File

@ -1243,7 +1243,7 @@ out:
return ret;
}
static int inet_gifconf(struct net_device *dev, char __user *buf, int len, int size)
int inet_gifconf(struct net_device *dev, char __user *buf, int len, int size)
{
struct in_device *in_dev = __in_dev_get_rtnl(dev);
const struct in_ifaddr *ifa;
@ -2766,8 +2766,6 @@ void __init devinet_init(void)
INIT_HLIST_HEAD(&inet_addr_lst[i]);
register_pernet_subsys(&devinet_ops);
register_gifconf(PF_INET, inet_gifconf);
register_netdevice_notifier(&ip_netdev_notifier);
queue_delayed_work(system_power_efficient_wq, &check_lifetime_work, 0);

View File

@ -1153,14 +1153,14 @@ static int qrtr_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
rc = put_user(len, (int __user *)argp);
break;
case SIOCGIFADDR:
if (copy_from_user(&ifr, argp, sizeof(ifr))) {
if (get_user_ifreq(&ifr, NULL, argp)) {
rc = -EFAULT;
break;
}
sq = (struct sockaddr_qrtr *)&ifr.ifr_addr;
*sq = ipc->us;
if (copy_to_user(argp, &ifr, sizeof(ifr))) {
if (put_user_ifreq(&ifr, argp)) {
rc = -EFAULT;
break;
}

View File

@ -1088,6 +1088,8 @@ EXPORT_SYMBOL(vlan_ioctl_set);
static long sock_do_ioctl(struct net *net, struct socket *sock,
unsigned int cmd, unsigned long arg)
{
struct ifreq ifr;
bool need_copyout;
int err;
void __user *argp = (void __user *)arg;
@ -1100,25 +1102,13 @@ static long sock_do_ioctl(struct net *net, struct socket *sock,
if (err != -ENOIOCTLCMD)
return err;
if (cmd == SIOCGIFCONF) {
struct ifconf ifc;
if (copy_from_user(&ifc, argp, sizeof(struct ifconf)))
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;
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;
}
@ -1217,6 +1207,11 @@ static long sock_ioctl(struct file *file, unsigned cmd, unsigned long arg)
cmd == SIOCGSTAMP_NEW,
false);
break;
case SIOCGIFCONF:
err = dev_ifconf(net, argp);
break;
default:
err = sock_do_ioctl(net, sock, cmd, arg);
break;
@ -3126,154 +3121,55 @@ void socket_seq_show(struct seq_file *seq)
}
#endif /* CONFIG_PROC_FS */
/* Handle the fact that while struct ifreq has the same *layout* on
* 32/64 for everything but ifreq::ifru_ifmap and ifreq::ifru_data,
* which are handled elsewhere, it still has different *size* due to
* ifreq::ifru_ifmap (which is 16 bytes on 32 bit, 24 bytes on 64-bit,
* resulting in struct ifreq being 32 and 40 bytes respectively).
* As a result, if the struct happens to be at the end of a page and
* the next page isn't readable/writable, we get a fault. To prevent
* that, copy back and forth to the full size.
*/
int get_user_ifreq(struct ifreq *ifr, void __user **ifrdata, void __user *arg)
{
if (in_compat_syscall()) {
struct compat_ifreq *ifr32 = (struct compat_ifreq *)ifr;
memset(ifr, 0, sizeof(*ifr));
if (copy_from_user(ifr32, arg, sizeof(*ifr32)))
return -EFAULT;
if (ifrdata)
*ifrdata = compat_ptr(ifr32->ifr_data);
return 0;
}
if (copy_from_user(ifr, arg, sizeof(*ifr)))
return -EFAULT;
if (ifrdata)
*ifrdata = ifr->ifr_data;
return 0;
}
EXPORT_SYMBOL(get_user_ifreq);
int put_user_ifreq(struct ifreq *ifr, void __user *arg)
{
size_t size = sizeof(*ifr);
if (in_compat_syscall())
size = sizeof(struct compat_ifreq);
if (copy_to_user(arg, ifr, size))
return -EFAULT;
return 0;
}
EXPORT_SYMBOL(put_user_ifreq);
#ifdef CONFIG_COMPAT
static int compat_dev_ifconf(struct net *net, struct compat_ifconf __user *uifc32)
{
struct compat_ifconf ifc32;
struct ifconf ifc;
int err;
if (copy_from_user(&ifc32, uifc32, sizeof(struct compat_ifconf)))
return -EFAULT;
ifc.ifc_len = ifc32.ifc_len;
ifc.ifc_req = compat_ptr(ifc32.ifcbuf);
rtnl_lock();
err = dev_ifconf(net, &ifc, sizeof(struct compat_ifreq));
rtnl_unlock();
if (err)
return err;
ifc32.ifc_len = ifc.ifc_len;
if (copy_to_user(uifc32, &ifc32, sizeof(struct compat_ifconf)))
return -EFAULT;
return 0;
}
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 = 0;
struct ethtool_rxnfc __user *rxnfc = NULL;
struct ifreq ifr;
u32 rule_cnt = 0, actual_rule_cnt;
u32 ethcmd;
u32 data;
int ret;
if (get_user(data, &ifr32->ifr_ifru.ifru_data))
return -EFAULT;
compat_rxnfc = compat_ptr(data);
if (get_user(ethcmd, &compat_rxnfc->cmd))
return -EFAULT;
/* 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);
fallthrough;
case ETHTOOL_GRXRINGS:
case ETHTOOL_GRXCLSRLCNT:
case ETHTOOL_GRXCLSRULE:
case ETHTOOL_SRXCLSRLINS:
convert_out = true;
fallthrough;
case ETHTOOL_SRXCLSRLDEL:
buf_size += sizeof(struct ethtool_rxnfc);
convert_in = true;
rxnfc = compat_alloc_user_space(buf_size);
break;
}
if (copy_from_user(&ifr.ifr_name, &ifr32->ifr_name, IFNAMSIZ))
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
* fs.ring_cookie and at the end of fs, but nowhere else.
*/
BUILD_BUG_ON(offsetof(struct compat_ethtool_rxnfc, fs.m_ext) +
sizeof(compat_rxnfc->fs.m_ext) !=
offsetof(struct ethtool_rxnfc, fs.m_ext) +
sizeof(rxnfc->fs.m_ext));
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 __user *)(&rxnfc->fs.m_ext + 1) -
(void __user *)rxnfc) ||
copy_in_user(&rxnfc->fs.ring_cookie,
&compat_rxnfc->fs.ring_cookie,
(void __user *)(&rxnfc->fs.location + 1) -
(void __user *)&rxnfc->fs.ring_cookie))
return -EFAULT;
if (ethcmd == ETHTOOL_GRXCLSRLALL) {
if (put_user(rule_cnt, &rxnfc->rule_cnt))
return -EFAULT;
} else if (copy_in_user(&rxnfc->rule_cnt,
&compat_rxnfc->rule_cnt,
sizeof(rxnfc->rule_cnt)))
return -EFAULT;
}
ret = dev_ioctl(net, SIOCETHTOOL, &ifr, NULL);
if (ret)
return ret;
if (convert_out) {
if (copy_in_user(compat_rxnfc, rxnfc,
(const void __user *)(&rxnfc->fs.m_ext + 1) -
(const void __user *)rxnfc) ||
copy_in_user(&compat_rxnfc->fs.ring_cookie,
&rxnfc->fs.ring_cookie,
(const void __user *)(&rxnfc->fs.location + 1) -
(const void __user *)&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)
{
compat_uptr_t uptr32;
@ -3281,7 +3177,7 @@ static int compat_siocwandev(struct net *net, struct compat_ifreq __user *uifr32
void __user *saved;
int err;
if (copy_from_user(&ifr, uifr32, sizeof(struct compat_ifreq)))
if (get_user_ifreq(&ifr, NULL, uifr32))
return -EFAULT;
if (get_user(uptr32, &uifr32->ifr_settings.ifs_ifsu))
@ -3293,7 +3189,7 @@ static int compat_siocwandev(struct net *net, struct compat_ifreq __user *uifr32
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)))
if (put_user_ifreq(&ifr, uifr32))
err = -EFAULT;
}
return err;
@ -3317,83 +3213,28 @@ static int compat_ifr_data_ioctl(struct net *net, unsigned int cmd,
static int compat_ifreq_ioctl(struct net *net, struct socket *sock,
unsigned int cmd,
unsigned long arg,
struct compat_ifreq __user *uifr32)
{
struct ifreq __user *uifr;
int err;
/* Handle the fact that while struct ifreq has the same *layout* on
* 32/64 for everything but ifreq::ifru_ifmap and ifreq::ifru_data,
* which are handled elsewhere, it still has different *size* due to
* ifreq::ifru_ifmap (which is 16 bytes on 32 bit, 24 bytes on 64-bit,
* resulting in struct ifreq being 32 and 40 bytes respectively).
* As a result, if the struct happens to be at the end of a page and
* the next page isn't readable/writable, we get a fault. To prevent
* that, copy back and forth to the full size.
*/
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:
case SIOCGIFNAME:
if (copy_in_user(uifr32, uifr, sizeof(*uifr32)))
err = -EFAULT;
break;
}
}
return err;
}
static int compat_sioc_ifmap(struct net *net, unsigned int cmd,
struct compat_ifreq __user *uifr32)
{
struct ifreq ifr;
struct compat_ifmap __user *uifmap32;
bool need_copyout;
int err;
uifmap32 = &uifr32->ifr_ifru.ifru_map;
err = copy_from_user(&ifr, uifr32, sizeof(ifr.ifr_name));
err |= get_user(ifr.ifr_map.mem_start, &uifmap32->mem_start);
err |= get_user(ifr.ifr_map.mem_end, &uifmap32->mem_end);
err |= get_user(ifr.ifr_map.base_addr, &uifmap32->base_addr);
err |= get_user(ifr.ifr_map.irq, &uifmap32->irq);
err |= get_user(ifr.ifr_map.dma, &uifmap32->dma);
err |= get_user(ifr.ifr_map.port, &uifmap32->port);
if (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;
err = dev_ioctl(net, cmd, &ifr, NULL);
if (cmd == SIOCGIFMAP && !err) {
err = copy_to_user(uifr32, &ifr, sizeof(ifr.ifr_name));
err |= put_user(ifr.ifr_map.mem_start, &uifmap32->mem_start);
err |= put_user(ifr.ifr_map.mem_end, &uifmap32->mem_end);
err |= put_user(ifr.ifr_map.base_addr, &uifmap32->base_addr);
err |= put_user(ifr.ifr_map.irq, &uifmap32->irq);
err |= put_user(ifr.ifr_map.dma, &uifmap32->dma);
err |= put_user(ifr.ifr_map.port, &uifmap32->port);
if (err)
err = -EFAULT;
}
return err;
}
@ -3426,15 +3267,8 @@ static int compat_sock_ioctl_trans(struct file *file, struct socket *sock,
case SIOCSIFBR:
case SIOCGIFBR:
return old_bridge_ioctl(argp);
case SIOCGIFCONF:
return compat_dev_ifconf(net, argp);
case SIOCETHTOOL:
return ethtool_ioctl(net, argp);
case SIOCWANDEV:
return compat_siocwandev(net, argp);
case SIOCGIFMAP:
case SIOCSIFMAP:
return compat_sioc_ifmap(net, cmd, argp);
case SIOCGSTAMP_OLD:
case SIOCGSTAMPNS_OLD:
if (!sock->ops->gettstamp)
@ -3442,6 +3276,7 @@ static int compat_sock_ioctl_trans(struct file *file, struct socket *sock,
return sock->ops->gettstamp(sock, argp, cmd == SIOCGSTAMP_OLD,
!COMPAT_USE_64BIT_TIME);
case SIOCETHTOOL:
case SIOCBONDSLAVEINFOQUERY:
case SIOCBONDINFOQUERY:
case SIOCSHWTSTAMP:
@ -3459,10 +3294,13 @@ static int compat_sock_ioctl_trans(struct file *file, struct socket *sock,
case SIOCGSKNS:
case SIOCGSTAMP_NEW:
case SIOCGSTAMPNS_NEW:
case SIOCGIFCONF:
return sock_ioctl(file, cmd, arg);
case SIOCGIFFLAGS:
case SIOCSIFFLAGS:
case SIOCGIFMAP:
case SIOCSIFMAP:
case SIOCGIFMETRIC:
case SIOCSIFMETRIC:
case SIOCGIFMTU:
@ -3499,7 +3337,7 @@ static int compat_sock_ioctl_trans(struct file *file, struct socket *sock,
case SIOCBONDRELEASE:
case SIOCBONDSETHWADDR:
case SIOCBONDCHANGEACTIVE:
return compat_ifreq_ioctl(net, sock, cmd, argp);
return compat_ifreq_ioctl(net, sock, cmd, arg, argp);
case SIOCSARP:
case SIOCGARP: