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 @@ ...@@ -5,6 +5,9 @@
#ifndef __ASM_COMPAT_H #ifndef __ASM_COMPAT_H
#define __ASM_COMPAT_H #define __ASM_COMPAT_H
#define compat_mode_t compat_mode_t
typedef u16 compat_mode_t;
#include <asm-generic/compat.h> #include <asm-generic/compat.h>
#ifdef CONFIG_COMPAT #ifdef CONFIG_COMPAT
...@@ -27,13 +30,9 @@ typedef u16 __compat_uid_t; ...@@ -27,13 +30,9 @@ typedef u16 __compat_uid_t;
typedef u16 __compat_gid_t; typedef u16 __compat_gid_t;
typedef u16 __compat_uid16_t; typedef u16 __compat_uid16_t;
typedef u16 __compat_gid16_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 u32 compat_dev_t;
typedef s32 compat_nlink_t; typedef s32 compat_nlink_t;
typedef u16 compat_ipc_pid_t; typedef u16 compat_ipc_pid_t;
typedef u32 compat_caddr_t;
typedef __kernel_fsid_t compat_fsid_t; typedef __kernel_fsid_t compat_fsid_t;
struct compat_stat { struct compat_stat {
...@@ -103,13 +102,6 @@ struct compat_statfs { ...@@ -103,13 +102,6 @@ struct compat_statfs {
#define COMPAT_RLIM_INFINITY 0xffffffff #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_OFF_T_MAX 0x7fffffff
#define compat_user_stack_pointer() (user_stack_pointer(task_pt_regs(current))) #define compat_user_stack_pointer() (user_stack_pointer(task_pt_regs(current)))
......
...@@ -9,20 +9,25 @@ ...@@ -9,20 +9,25 @@
#include <asm/page.h> #include <asm/page.h>
#include <asm/ptrace.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> #include <asm-generic/compat.h>
#define COMPAT_USER_HZ 100 #define COMPAT_USER_HZ 100
#define COMPAT_UTS_MACHINE "mips\0\0\0" #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_dev_t;
typedef u32 compat_nlink_t; typedef u32 compat_nlink_t;
typedef s32 compat_ipc_pid_t; typedef s32 compat_ipc_pid_t;
typedef s32 compat_caddr_t;
typedef struct { typedef struct {
s32 val[2]; s32 val[2];
} compat_fsid_t; } compat_fsid_t;
...@@ -89,13 +94,6 @@ struct compat_statfs { ...@@ -89,13 +94,6 @@ struct compat_statfs {
#define COMPAT_RLIM_INFINITY 0x7fffffffUL #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 #define COMPAT_OFF_T_MAX 0x7fffffff
static inline void __user *arch_compat_alloc_user_space(long len) static inline void __user *arch_compat_alloc_user_space(long len)
......
...@@ -8,6 +8,9 @@ ...@@ -8,6 +8,9 @@
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/thread_info.h> #include <linux/thread_info.h>
#define compat_mode_t compat_mode_t
typedef u16 compat_mode_t;
#include <asm-generic/compat.h> #include <asm-generic/compat.h>
#define COMPAT_USER_HZ 100 #define COMPAT_USER_HZ 100
...@@ -15,13 +18,9 @@ ...@@ -15,13 +18,9 @@
typedef u32 __compat_uid_t; typedef u32 __compat_uid_t;
typedef u32 __compat_gid_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 u32 compat_dev_t;
typedef u16 compat_nlink_t; typedef u16 compat_nlink_t;
typedef u16 compat_ipc_pid_t; typedef u16 compat_ipc_pid_t;
typedef u32 compat_caddr_t;
struct compat_stat { struct compat_stat {
compat_dev_t st_dev; /* dev_t is 32 bits on parisc */ compat_dev_t st_dev; /* dev_t is 32 bits on parisc */
...@@ -96,13 +95,6 @@ struct compat_sigcontext { ...@@ -96,13 +95,6 @@ struct compat_sigcontext {
#define COMPAT_RLIM_INFINITY 0xffffffff #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 #define COMPAT_OFF_T_MAX 0x7fffffff
struct compat_ipc64_perm { struct compat_ipc64_perm {
......
...@@ -19,13 +19,9 @@ ...@@ -19,13 +19,9 @@
typedef u32 __compat_uid_t; typedef u32 __compat_uid_t;
typedef u32 __compat_gid_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 u32 compat_dev_t;
typedef s16 compat_nlink_t; typedef s16 compat_nlink_t;
typedef u16 compat_ipc_pid_t; typedef u16 compat_ipc_pid_t;
typedef u32 compat_caddr_t;
typedef __kernel_fsid_t compat_fsid_t; typedef __kernel_fsid_t compat_fsid_t;
struct compat_stat { struct compat_stat {
...@@ -85,13 +81,6 @@ struct compat_statfs { ...@@ -85,13 +81,6 @@ struct compat_statfs {
#define COMPAT_RLIM_INFINITY 0xffffffff #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_OFF_T_MAX 0x7fffffff
static inline void __user *arch_compat_alloc_user_space(long len) static inline void __user *arch_compat_alloc_user_space(long len)
......
...@@ -9,6 +9,9 @@ ...@@ -9,6 +9,9 @@
#include <linux/sched/task_stack.h> #include <linux/sched/task_stack.h>
#include <linux/thread_info.h> #include <linux/thread_info.h>
#define compat_mode_t compat_mode_t
typedef u16 compat_mode_t;
#include <asm-generic/compat.h> #include <asm-generic/compat.h>
#define __TYPE_IS_PTR(t) (!__builtin_types_compatible_p( \ #define __TYPE_IS_PTR(t) (!__builtin_types_compatible_p( \
...@@ -55,13 +58,9 @@ ...@@ -55,13 +58,9 @@
typedef u16 __compat_uid_t; typedef u16 __compat_uid_t;
typedef u16 __compat_gid_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_dev_t;
typedef u16 compat_nlink_t; typedef u16 compat_nlink_t;
typedef u16 compat_ipc_pid_t; typedef u16 compat_ipc_pid_t;
typedef u32 compat_caddr_t;
typedef __kernel_fsid_t compat_fsid_t; typedef __kernel_fsid_t compat_fsid_t;
typedef struct { typedef struct {
...@@ -155,13 +154,6 @@ struct compat_statfs64 { ...@@ -155,13 +154,6 @@ struct compat_statfs64 {
#define COMPAT_RLIM_INFINITY 0xffffffff #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 #define COMPAT_OFF_T_MAX 0x7fffffff
/* /*
......
...@@ -6,6 +6,9 @@ ...@@ -6,6 +6,9 @@
*/ */
#include <linux/types.h> #include <linux/types.h>
#define compat_mode_t compat_mode_t
typedef u16 compat_mode_t;
#include <asm-generic/compat.h> #include <asm-generic/compat.h>
#define COMPAT_USER_HZ 100 #define COMPAT_USER_HZ 100
...@@ -13,13 +16,9 @@ ...@@ -13,13 +16,9 @@
typedef u16 __compat_uid_t; typedef u16 __compat_uid_t;
typedef u16 __compat_gid_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_dev_t;
typedef s16 compat_nlink_t; typedef s16 compat_nlink_t;
typedef u16 compat_ipc_pid_t; typedef u16 compat_ipc_pid_t;
typedef u32 compat_caddr_t;
typedef __kernel_fsid_t compat_fsid_t; typedef __kernel_fsid_t compat_fsid_t;
struct compat_stat { struct compat_stat {
...@@ -115,13 +114,6 @@ struct compat_statfs { ...@@ -115,13 +114,6 @@ struct compat_statfs {
#define COMPAT_RLIM_INFINITY 0x7fffffff #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 #define COMPAT_OFF_T_MAX 0x7fffffff
#ifdef CONFIG_COMPAT #ifdef CONFIG_COMPAT
......
...@@ -12,6 +12,9 @@ ...@@ -12,6 +12,9 @@
#include <asm/user32.h> #include <asm/user32.h>
#include <asm/unistd.h> #include <asm/unistd.h>
#define compat_mode_t compat_mode_t
typedef u16 compat_mode_t;
#include <asm-generic/compat.h> #include <asm-generic/compat.h>
#define COMPAT_USER_HZ 100 #define COMPAT_USER_HZ 100
...@@ -19,13 +22,9 @@ ...@@ -19,13 +22,9 @@
typedef u16 __compat_uid_t; typedef u16 __compat_uid_t;
typedef u16 __compat_gid_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_dev_t;
typedef u16 compat_nlink_t; typedef u16 compat_nlink_t;
typedef u16 compat_ipc_pid_t; typedef u16 compat_ipc_pid_t;
typedef u32 compat_caddr_t;
typedef __kernel_fsid_t compat_fsid_t; typedef __kernel_fsid_t compat_fsid_t;
struct compat_stat { struct compat_stat {
...@@ -92,13 +91,6 @@ struct compat_statfs { ...@@ -92,13 +91,6 @@ struct compat_statfs {
#define COMPAT_RLIM_INFINITY 0xffffffff #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 #define COMPAT_OFF_T_MAX 0x7fffffff
struct compat_ipc64_perm { struct compat_ipc64_perm {
......
...@@ -29,6 +29,7 @@ typedef struct { ...@@ -29,6 +29,7 @@ typedef struct {
#define SA_X32_ABI 0x01000000u #define SA_X32_ABI 0x01000000u
#ifndef CONFIG_COMPAT #ifndef CONFIG_COMPAT
#define compat_sigset_t compat_sigset_t
typedef sigset_t compat_sigset_t; typedef sigset_t compat_sigset_t;
#endif #endif
......
...@@ -20,7 +20,18 @@ typedef u16 compat_ushort_t; ...@@ -20,7 +20,18 @@ typedef u16 compat_ushort_t;
typedef u32 compat_uint_t; typedef u32 compat_uint_t;
typedef u32 compat_ulong_t; typedef u32 compat_ulong_t;
typedef u32 compat_uptr_t; typedef u32 compat_uptr_t;
typedef u32 compat_caddr_t;
typedef u32 compat_aio_context_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 #ifdef CONFIG_COMPAT_FOR_U64_ALIGNMENT
typedef s64 __attribute__((aligned(4))) compat_s64; typedef s64 __attribute__((aligned(4))) compat_s64;
...@@ -30,4 +41,10 @@ typedef s64 compat_s64; ...@@ -30,4 +41,10 @@ typedef s64 compat_s64;
typedef u64 compat_u64; typedef u64 compat_u64;
#endif #endif
#ifndef _COMPAT_NSIG
typedef u32 compat_sigset_word;
#define _COMPAT_NSIG _NSIG
#define _COMPAT_NSIG_BPW 32
#endif
#endif #endif
...@@ -20,11 +20,8 @@ ...@@ -20,11 +20,8 @@
#include <linux/unistd.h> #include <linux/unistd.h>
#include <asm/compat.h> #include <asm/compat.h>
#ifdef CONFIG_COMPAT
#include <asm/siginfo.h> #include <asm/siginfo.h>
#include <asm/signal.h> #include <asm/signal.h>
#endif
#ifdef CONFIG_ARCH_HAS_SYSCALL_WRAPPER #ifdef CONFIG_ARCH_HAS_SYSCALL_WRAPPER
/* /*
...@@ -95,8 +92,6 @@ struct compat_iovec { ...@@ -95,8 +92,6 @@ struct compat_iovec {
compat_size_t iov_len; compat_size_t iov_len;
}; };
#ifdef CONFIG_COMPAT
#ifndef compat_user_stack_pointer #ifndef compat_user_stack_pointer
#define compat_user_stack_pointer() current_user_stack_pointer() #define compat_user_stack_pointer() current_user_stack_pointer()
#endif #endif
...@@ -131,9 +126,11 @@ struct compat_tms { ...@@ -131,9 +126,11 @@ struct compat_tms {
#define _COMPAT_NSIG_WORDS (_COMPAT_NSIG / _COMPAT_NSIG_BPW) #define _COMPAT_NSIG_WORDS (_COMPAT_NSIG / _COMPAT_NSIG_BPW)
#ifndef compat_sigset_t
typedef struct { typedef struct {
compat_sigset_word sig[_COMPAT_NSIG_WORDS]; compat_sigset_word sig[_COMPAT_NSIG_WORDS];
} compat_sigset_t; } compat_sigset_t;
#endif
int set_compat_user_sigmask(const compat_sigset_t __user *umask, int set_compat_user_sigmask(const compat_sigset_t __user *umask,
size_t sigsetsize); size_t sigsetsize);
...@@ -384,6 +381,7 @@ struct compat_keyctl_kdf_params { ...@@ -384,6 +381,7 @@ struct compat_keyctl_kdf_params {
__u32 __spare[8]; __u32 __spare[8];
}; };
struct compat_stat;
struct compat_statfs; struct compat_statfs;
struct compat_statfs64; struct compat_statfs64;
struct compat_old_linux_dirent; struct compat_old_linux_dirent;
...@@ -428,7 +426,7 @@ put_compat_sigset(compat_sigset_t __user *compat, const sigset_t *set, ...@@ -428,7 +426,7 @@ put_compat_sigset(compat_sigset_t __user *compat, const sigset_t *set,
unsigned int size) unsigned int size)
{ {
/* size <= sizeof(compat_sigset_t) <= sizeof(sigset_t) */ /* size <= sizeof(compat_sigset_t) <= sizeof(sigset_t) */
#ifdef __BIG_ENDIAN #if defined(__BIG_ENDIAN) && defined(CONFIG_64BIT)
compat_sigset_t v; compat_sigset_t v;
switch (_NSIG_WORDS) { switch (_NSIG_WORDS) {
case 4: v.sig[7] = (set->sig[3] >> 32); v.sig[6] = set->sig[3]; 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); ...@@ -929,17 +927,6 @@ asmlinkage long compat_sys_socketcall(int call, u32 __user *args);
#endif /* CONFIG_ARCH_HAS_SYSCALL_WRAPPER */ #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 * ns_to_old_timeval32 - Compat version of ns_to_timeval
* @nsec: the nanoseconds value to be converted * @nsec: the nanoseconds value to be converted
...@@ -969,6 +956,17 @@ int kcompat_sys_statfs64(const char __user * pathname, compat_size_t sz, ...@@ -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, int kcompat_sys_fstatfs64(unsigned int fd, compat_size_t sz,
struct compat_statfs64 __user * buf); 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 */ #else /* !CONFIG_COMPAT */
#define is_compat_task() (0) #define is_compat_task() (0)
......
...@@ -17,8 +17,6 @@ ...@@ -17,8 +17,6 @@
#include <linux/compat.h> #include <linux/compat.h>
#include <uapi/linux/ethtool.h> #include <uapi/linux/ethtool.h>
#ifdef CONFIG_COMPAT
struct compat_ethtool_rx_flow_spec { struct compat_ethtool_rx_flow_spec {
u32 flow_type; u32 flow_type;
union ethtool_flow_union h_u; union ethtool_flow_union h_u;
...@@ -38,8 +36,6 @@ struct compat_ethtool_rxnfc { ...@@ -38,8 +36,6 @@ struct compat_ethtool_rxnfc {
u32 rule_locs[]; u32 rule_locs[];
}; };
#endif /* CONFIG_COMPAT */
#include <linux/rculist.h> #include <linux/rculist.h>
/** /**
......
...@@ -178,6 +178,15 @@ static inline struct net_device *ip_dev_find(struct net *net, __be32 addr) ...@@ -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 inet_addr_onlink(struct in_device *in_dev, __be32 a, __be32 b);
int devinet_ioctl(struct net *net, unsigned int cmd, struct ifreq *); 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); void devinet_init(void);
struct in_device *inetdev_by_index(struct net *, int); struct in_device *inetdev_by_index(struct net *, int);
__be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope); __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) ...@@ -3289,14 +3289,6 @@ static inline bool dev_has_header(const struct net_device *dev)
return dev->header_ops && dev->header_ops->create; 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 #ifdef CONFIG_NET_FLOW_LIMIT
#define FLOW_LIMIT_HISTORY (1 << 7) /* must be ^2 and !overflow buckets */ #define FLOW_LIMIT_HISTORY (1 << 7) /* must be ^2 and !overflow buckets */
struct sd_flow_limit { struct sd_flow_limit {
...@@ -4014,9 +4006,11 @@ int netdev_rx_handler_register(struct net_device *dev, ...@@ -4014,9 +4006,11 @@ int netdev_rx_handler_register(struct net_device *dev,
void netdev_rx_handler_unregister(struct net_device *dev); void netdev_rx_handler_unregister(struct net_device *dev);
bool dev_valid_name(const char *name); 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, int dev_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr,
bool *need_copyout); 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 *); int dev_ethtool(struct net *net, struct ifreq *);
unsigned int dev_get_flags(const struct net_device *); unsigned int dev_get_flags(const struct net_device *);
int __dev_change_flags(struct net_device *dev, unsigned int flags, int __dev_change_flags(struct net_device *dev, unsigned int flags,
......
...@@ -666,7 +666,7 @@ static int atif_ioctl(int cmd, void __user *arg) ...@@ -666,7 +666,7 @@ static int atif_ioctl(int cmd, void __user *arg)
struct rtentry rtdef; struct rtentry rtdef;
int add_route; int add_route;
if (copy_from_user(&atreq, arg, sizeof(atreq))) if (get_user_ifreq(&atreq, NULL, arg))
return -EFAULT; return -EFAULT;
dev = __dev_get_by_name(&init_net, atreq.ifr_name); dev = __dev_get_by_name(&init_net, atreq.ifr_name);
...@@ -865,7 +865,7 @@ static int atif_ioctl(int cmd, void __user *arg) ...@@ -865,7 +865,7 @@ static int atif_ioctl(int cmd, void __user *arg)
return 0; 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) static int atrtr_ioctl_addrt(struct rtentry *rt)
......
// SPDX-License-Identifier: GPL-2.0 // SPDX-License-Identifier: GPL-2.0
#include <linux/kmod.h> #include <linux/kmod.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/inetdevice.h>
#include <linux/etherdevice.h> #include <linux/etherdevice.h>
#include <linux/rtnetlink.h> #include <linux/rtnetlink.h>
#include <linux/net_tstamp.h> #include <linux/net_tstamp.h>
...@@ -25,79 +26,108 @@ static int dev_ifname(struct net *net, struct ifreq *ifr) ...@@ -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); 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 * Perform a SIOCGIFCONF call. This structure will change
* size eventually, and there is nothing I can do about it. * size eventually, and there is nothing I can do about it.
* Thus we will need a 'compatibility mode'. * Thus we will need a 'compatibility mode'.
*/ */
int dev_ifconf(struct net *net, struct ifconf __user *uifc)
int dev_ifconf(struct net *net, struct ifconf *ifc, int size)
{ {
struct net_device *dev; struct net_device *dev;
char __user *pos; void __user *pos;
int len; size_t size;
int total; int len, total = 0, done;
int i;
/* /* both the ifconf and the ifreq structures are slightly different */
* Fetch the caller's info block. if (in_compat_syscall()) {
*/ struct compat_ifconf ifc32;
pos = ifc->ifc_buf; if (copy_from_user(&ifc32, uifc, sizeof(struct compat_ifconf)))
len = ifc->ifc_len; return -EFAULT;
/* pos = compat_ptr(ifc32.ifcbuf);
* Loop over the interfaces, and write an info block for each. 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_each_netdev(net, dev) {
for (i = 0; i < NPROTO; i++) { if (!pos)
if (gifconf_list[i]) { done = inet_gifconf(dev, NULL, 0, size);
int done; else
if (!pos) done = inet_gifconf(dev, pos + total,
done = gifconf_list[i](dev, NULL, 0, size); len - total, size);
else if (done < 0) {
done = gifconf_list[i](dev, pos + total, rtnl_unlock();
len - total, size); return -EFAULT;
if (done < 0)
return -EFAULT;
total += done;
}
} }
total += done;
} }
rtnl_unlock();
/* return put_user(total, &uifc->ifc_len);
* All done. Write the updated control block back to the caller. }
*/
ifc->ifc_len = total; 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; 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() * 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 ...@@ -128,13 +158,7 @@ static int dev_ifsioc_locked(struct net *net, struct ifreq *ifr, unsigned int cm
break; break;
case SIOCGIFMAP: case SIOCGIFMAP:
ifr->ifr_map.mem_start = dev->mem_start; return dev_getifmap(dev, ifr);
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;
case SIOCGIFINDEX: case SIOCGIFINDEX:
ifr->ifr_ifindex = dev->ifindex; ifr->ifr_ifindex = dev->ifindex;
...@@ -275,12 +299,7 @@ static int dev_ifsioc(struct net *net, struct ifreq *ifr, unsigned int cmd) ...@@ -275,12 +299,7 @@ static int dev_ifsioc(struct net *net, struct ifreq *ifr, unsigned int cmd)
return 0; return 0;
case SIOCSIFMAP: case SIOCSIFMAP:
if (ops->ndo_set_config) { return dev_setifmap(dev, ifr);
if (!netif_device_present(dev))
return -ENODEV;
return ops->ndo_set_config(dev, &ifr->ifr_map);
}
return -EOPNOTSUPP;
case SIOCADDMULTI: case SIOCADDMULTI:
if (!ops->ndo_set_rx_mode || if (!ops->ndo_set_rx_mode ||
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
* the information ethtool needs. * the information ethtool needs.
*/ */
#include <linux/compat.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/capability.h> #include <linux/capability.h>
...@@ -807,6 +808,120 @@ static noinline_for_stack int ethtool_get_sset_info(struct net_device *dev, ...@@ -807,6 +808,120 @@ static noinline_for_stack int ethtool_get_sset_info(struct net_device *dev,
return ret; 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, static noinline_for_stack int ethtool_set_rxnfc(struct net_device *dev,
u32 cmd, void __user *useraddr) u32 cmd, void __user *useraddr)
{ {
...@@ -825,7 +940,7 @@ static noinline_for_stack int ethtool_set_rxnfc(struct net_device *dev, ...@@ -825,7 +940,7 @@ static noinline_for_stack int ethtool_set_rxnfc(struct net_device *dev,
info_size = (offsetof(struct ethtool_rxnfc, data) + info_size = (offsetof(struct ethtool_rxnfc, data) +
sizeof(info.data)); sizeof(info.data));
if (copy_from_user(&info, useraddr, info_size)) if (ethtool_rxnfc_copy_from_user(&info, useraddr, info_size))
return -EFAULT; return -EFAULT;
rc = dev->ethtool_ops->set_rxnfc(dev, &info); rc = dev->ethtool_ops->set_rxnfc(dev, &info);
...@@ -833,7 +948,7 @@ static noinline_for_stack int ethtool_set_rxnfc(struct net_device *dev, ...@@ -833,7 +948,7 @@ static noinline_for_stack int ethtool_set_rxnfc(struct net_device *dev,
return rc; return rc;
if (cmd == ETHTOOL_SRXCLSRLINS && if (cmd == ETHTOOL_SRXCLSRLINS &&
copy_to_user(useraddr, &info, info_size)) ethtool_rxnfc_copy_to_user(useraddr, &info, info_size, NULL))
return -EFAULT; return -EFAULT;
return 0; return 0;
...@@ -859,7 +974,7 @@ static noinline_for_stack int ethtool_get_rxnfc(struct net_device *dev, ...@@ -859,7 +974,7 @@ static noinline_for_stack int ethtool_get_rxnfc(struct net_device *dev,
info_size = (offsetof(struct ethtool_rxnfc, data) + info_size = (offsetof(struct ethtool_rxnfc, data) +
sizeof(info.data)); sizeof(info.data));
if (copy_from_user(&info, useraddr, info_size)) if (ethtool_rxnfc_copy_from_user(&info, useraddr, info_size))
return -EFAULT; return -EFAULT;
/* If FLOW_RSS was requested then user-space must be using the /* 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, ...@@ -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) { if (cmd == ETHTOOL_GRXFH && info.flow_type & FLOW_RSS) {
info_size = sizeof(info); info_size = sizeof(info);
if (copy_from_user(&info, useraddr, info_size)) if (ethtool_rxnfc_copy_from_user(&info, useraddr, info_size))
return -EFAULT; return -EFAULT;
/* Since malicious users may modify the original data, /* Since malicious users may modify the original data,
* we need to check whether FLOW_RSS is still requested. * 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, ...@@ -893,18 +1008,7 @@ static noinline_for_stack int ethtool_get_rxnfc(struct net_device *dev,
if (ret < 0) if (ret < 0)
goto err_out; goto err_out;
ret = -EFAULT; ret = ethtool_rxnfc_copy_to_user(useraddr, &info, info_size, rule_buf);
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;
err_out: err_out:
kfree(rule_buf); kfree(rule_buf);
......
...@@ -129,7 +129,7 @@ static int ieee802154_dev_ioctl(struct sock *sk, struct ifreq __user *arg, ...@@ -129,7 +129,7 @@ static int ieee802154_dev_ioctl(struct sock *sk, struct ifreq __user *arg,
int ret = -ENOIOCTLCMD; int ret = -ENOIOCTLCMD;
struct net_device *dev; struct net_device *dev;
if (copy_from_user(&ifr, arg, sizeof(struct ifreq))) if (get_user_ifreq(&ifr, NULL, arg))
return -EFAULT; return -EFAULT;
ifr.ifr_name[IFNAMSIZ-1] = 0; ifr.ifr_name[IFNAMSIZ-1] = 0;
...@@ -143,7 +143,7 @@ static int ieee802154_dev_ioctl(struct sock *sk, struct ifreq __user *arg, ...@@ -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) if (dev->type == ARPHRD_IEEE802154 && dev->netdev_ops->ndo_do_ioctl)
ret = dev->netdev_ops->ndo_do_ioctl(dev, &ifr, cmd); 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; ret = -EFAULT;
dev_put(dev); dev_put(dev);
......
...@@ -953,10 +953,10 @@ int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) ...@@ -953,10 +953,10 @@ int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
case SIOCGIFNETMASK: case SIOCGIFNETMASK:
case SIOCGIFDSTADDR: case SIOCGIFDSTADDR:
case SIOCGIFPFLAGS: case SIOCGIFPFLAGS:
if (copy_from_user(&ifr, p, sizeof(struct ifreq))) if (get_user_ifreq(&ifr, NULL, p))
return -EFAULT; return -EFAULT;
err = devinet_ioctl(net, cmd, &ifr); 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; err = -EFAULT;
break; break;
...@@ -966,7 +966,7 @@ int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) ...@@ -966,7 +966,7 @@ int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
case SIOCSIFDSTADDR: case SIOCSIFDSTADDR:
case SIOCSIFPFLAGS: case SIOCSIFPFLAGS:
case SIOCSIFFLAGS: case SIOCSIFFLAGS:
if (copy_from_user(&ifr, p, sizeof(struct ifreq))) if (get_user_ifreq(&ifr, NULL, p))
return -EFAULT; return -EFAULT;
err = devinet_ioctl(net, cmd, &ifr); err = devinet_ioctl(net, cmd, &ifr);
break; break;
......
...@@ -1243,7 +1243,7 @@ int devinet_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr) ...@@ -1243,7 +1243,7 @@ int devinet_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr)
return ret; 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); struct in_device *in_dev = __in_dev_get_rtnl(dev);
const struct in_ifaddr *ifa; const struct in_ifaddr *ifa;
...@@ -2766,8 +2766,6 @@ void __init devinet_init(void) ...@@ -2766,8 +2766,6 @@ void __init devinet_init(void)
INIT_HLIST_HEAD(&inet_addr_lst[i]); INIT_HLIST_HEAD(&inet_addr_lst[i]);
register_pernet_subsys(&devinet_ops); register_pernet_subsys(&devinet_ops);
register_gifconf(PF_INET, inet_gifconf);
register_netdevice_notifier(&ip_netdev_notifier); register_netdevice_notifier(&ip_netdev_notifier);
queue_delayed_work(system_power_efficient_wq, &check_lifetime_work, 0); 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) ...@@ -1153,14 +1153,14 @@ static int qrtr_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
rc = put_user(len, (int __user *)argp); rc = put_user(len, (int __user *)argp);
break; break;
case SIOCGIFADDR: case SIOCGIFADDR:
if (copy_from_user(&ifr, argp, sizeof(ifr))) { if (get_user_ifreq(&ifr, NULL, argp)) {
rc = -EFAULT; rc = -EFAULT;
break; break;
} }
sq = (struct sockaddr_qrtr *)&ifr.ifr_addr; sq = (struct sockaddr_qrtr *)&ifr.ifr_addr;
*sq = ipc->us; *sq = ipc->us;
if (copy_to_user(argp, &ifr, sizeof(ifr))) { if (put_user_ifreq(&ifr, argp)) {
rc = -EFAULT; rc = -EFAULT;
break; 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