Commit 090597b4 authored by David S. Miller's avatar David S. Miller

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: default avatarDavid S. Miller <davem@davemloft.net>
parents 4431531c 29c49648
......@@ -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)))
......
......@@ -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)
......
......@@ -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 {
......
......@@ -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)
......
......@@ -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
/*
......
......@@ -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
......
......@@ -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 {
......
......@@ -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
......
......@@ -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
......@@ -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)
......
......@@ -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>
/**
......
......@@ -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);
......
......@@ -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,
......
......@@ -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)
......
// 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;
if (copy_from_user(&ifc, uifc, sizeof(struct ifconf)))
return -EFAULT;
pos = ifc.ifc_buf;
len = ifc.ifc_len;
size = sizeof(struct ifreq);
}
total = 0;
/* Loop over the interfaces, and write an info block for each. */
rtnl_lock();
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 (!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();
/*
* All done. Write the updated control block back to the caller.
*/
ifc->ifc_len = total;
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 ||
......
......@@ -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 @@ static noinline_for_stack int ethtool_get_sset_info(struct net_device *dev,
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);
......
......@@ -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);
......
......@@ -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;
......
......@@ -1243,7 +1243,7 @@ int devinet_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr)
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);
......
......@@ -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;
}
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment