mirror of
https://github.com/torvalds/linux.git
synced 2024-11-11 14:42:24 +00:00
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:
commit
090597b4a9
@ -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)
|
||||
|
@ -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 ||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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 @@ 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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
328
net/socket.c
328
net/socket.c
@ -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:
|
||||
|
Loading…
Reference in New Issue
Block a user