From patchwork Thu Jul 22 14:28:58 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Arnd Bergmann X-Patchwork-Id: 12394223 X-Patchwork-Delegate: kuba@kernel.org Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-20.5 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 1B8C3C63793 for ; Thu, 22 Jul 2021 14:29:25 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 047946100C for ; Thu, 22 Jul 2021 14:29:24 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232413AbhGVNss (ORCPT ); Thu, 22 Jul 2021 09:48:48 -0400 Received: from mail.kernel.org ([198.145.29.99]:44822 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232385AbhGVNsp (ORCPT ); Thu, 22 Jul 2021 09:48:45 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 52B2A61278; Thu, 22 Jul 2021 14:29:18 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1626964160; bh=O54DZAW58lGHxHXPsPQBp8/QBUnzIS5LKcItSp7Excs=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=AqkNdsRXpOqG/nN4/8xyLZsKr4Uw+8fOJqKl6OULqYh8tt8yWoh5vjdda93hCN1ta 8SdPDfMAieTBdm6MpaoEJh+OmyeKzL2ACw+MjHQz9CwuDEfxmoYhl2fNBt7ytwqMNP AHj+KUQrJP8fcalkk25WaesComooQK5wtT5KN8gd7xthrDOWSEYb4syYeRj6EZRSdc QKAFn1lhPn/bM2uJBbS9gzyuhbWMlOLNey1eVXwK38UX9uv2fUO4wOQVxZKIdNPNZw Aa5WVRWtkD9n4uBmSU/npRqTTPQC0+lms3p/Lx61BGOcu8Ppep+vJyNgc06LG1jat5 whRCzroswV8/A== From: Arnd Bergmann To: netdev@vger.kernel.org Cc: Arnd Bergmann , Al Viro , Andrew Lunn , Christoph Hellwig , David Ahern , "David S. Miller" , Eric Dumazet , Hideaki YOSHIFUJI , Jakub Kicinski , Kees Cook , Marco Elver , linux-kernel@vger.kernel.org, linux-arch@vger.kernel.org Subject: [PATCH net-next v6 1/6] compat: make linux/compat.h available everywhere Date: Thu, 22 Jul 2021 16:28:58 +0200 Message-Id: <20210722142903.213084-2-arnd@kernel.org> X-Mailer: git-send-email 2.29.2 In-Reply-To: <20210722142903.213084-1-arnd@kernel.org> References: <20210722142903.213084-1-arnd@kernel.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: kuba@kernel.org From: Arnd Bergmann Parts of linux/compat.h are under an #ifdef, but we end up using more of those over time, moving things around bit by bit. To get it over with once and for all, make all of this file uncondititonal now so it can be accessed everywhere. There are only a few types left that are in asm/compat.h but not yet in the asm-generic version, so add those in the process. This requires providing a few more types in asm-generic/compat.h that were not already there. The only tricky one is compat_sigset_t, which needs a little help on 32-bit architectures and for x86. Signed-off-by: Arnd Bergmann Reviewed-by: Christoph Hellwig --- Changed in v6: - split out linux/compat.h changes from multiple patches into a separate change - Do it all at once --- arch/arm64/include/asm/compat.h | 14 +++----------- arch/mips/include/asm/compat.h | 24 +++++++++++------------ arch/parisc/include/asm/compat.h | 14 +++----------- arch/powerpc/include/asm/compat.h | 11 ----------- arch/s390/include/asm/compat.h | 14 +++----------- arch/sparc/include/asm/compat.h | 14 +++----------- arch/x86/include/asm/compat.h | 14 +++----------- arch/x86/include/asm/signal.h | 1 + include/asm-generic/compat.h | 17 ++++++++++++++++ include/linux/compat.h | 32 +++++++++++++++---------------- 10 files changed, 59 insertions(+), 96 deletions(-) diff --git a/arch/arm64/include/asm/compat.h b/arch/arm64/include/asm/compat.h index 23a9fb73c04f..79c1a750e357 100644 --- a/arch/arm64/include/asm/compat.h +++ b/arch/arm64/include/asm/compat.h @@ -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 #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))) diff --git a/arch/mips/include/asm/compat.h b/arch/mips/include/asm/compat.h index 65975712a22d..53f015a1b0a7 100644 --- a/arch/mips/include/asm/compat.h +++ b/arch/mips/include/asm/compat.h @@ -9,20 +9,25 @@ #include #include +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 #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) diff --git a/arch/parisc/include/asm/compat.h b/arch/parisc/include/asm/compat.h index 1a609d38f667..b5d90e82b65d 100644 --- a/arch/parisc/include/asm/compat.h +++ b/arch/parisc/include/asm/compat.h @@ -8,6 +8,9 @@ #include #include +#define compat_mode_t compat_mode_t +typedef u16 compat_mode_t; + #include #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 { diff --git a/arch/powerpc/include/asm/compat.h b/arch/powerpc/include/asm/compat.h index 9191fc29e6ed..e33dcf134cdd 100644 --- a/arch/powerpc/include/asm/compat.h +++ b/arch/powerpc/include/asm/compat.h @@ -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) diff --git a/arch/s390/include/asm/compat.h b/arch/s390/include/asm/compat.h index ea5b9c34b7be..8d49505b4a43 100644 --- a/arch/s390/include/asm/compat.h +++ b/arch/s390/include/asm/compat.h @@ -9,6 +9,9 @@ #include #include +#define compat_mode_t compat_mode_t +typedef u16 compat_mode_t; + #include #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 /* diff --git a/arch/sparc/include/asm/compat.h b/arch/sparc/include/asm/compat.h index b85842cda99f..8b63410e830f 100644 --- a/arch/sparc/include/asm/compat.h +++ b/arch/sparc/include/asm/compat.h @@ -6,6 +6,9 @@ */ #include +#define compat_mode_t compat_mode_t +typedef u16 compat_mode_t; + #include #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 diff --git a/arch/x86/include/asm/compat.h b/arch/x86/include/asm/compat.h index be09c7eac89f..4ae01cdb99de 100644 --- a/arch/x86/include/asm/compat.h +++ b/arch/x86/include/asm/compat.h @@ -12,6 +12,9 @@ #include #include +#define compat_mode_t compat_mode_t +typedef u16 compat_mode_t; + #include #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 { diff --git a/arch/x86/include/asm/signal.h b/arch/x86/include/asm/signal.h index 6fd8410a3910..2dfb5fea13af 100644 --- a/arch/x86/include/asm/signal.h +++ b/arch/x86/include/asm/signal.h @@ -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 diff --git a/include/asm-generic/compat.h b/include/asm-generic/compat.h index 30f7b18a36f9..d46c0201cc34 100644 --- a/include/asm-generic/compat.h +++ b/include/asm-generic/compat.h @@ -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 diff --git a/include/linux/compat.h b/include/linux/compat.h index c270124e4402..8e0598c7d1d1 100644 --- a/include/linux/compat.h +++ b/include/linux/compat.h @@ -20,11 +20,8 @@ #include #include - -#ifdef CONFIG_COMPAT #include #include -#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) From patchwork Thu Jul 22 14:28:59 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Arnd Bergmann X-Patchwork-Id: 12394225 X-Patchwork-Delegate: kuba@kernel.org Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-20.5 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 3F13DC63793 for ; Thu, 22 Jul 2021 14:29:28 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 249EE6120C for ; Thu, 22 Jul 2021 14:29:28 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232440AbhGVNsv (ORCPT ); Thu, 22 Jul 2021 09:48:51 -0400 Received: from mail.kernel.org ([198.145.29.99]:44914 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232414AbhGVNss (ORCPT ); Thu, 22 Jul 2021 09:48:48 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 35F006127C; Thu, 22 Jul 2021 14:29:21 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1626964163; bh=xajuxrCvKB1Y1mrk9SEuzfkwt+MpNx7NzXHkP0RMqko=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Msnlp1siR87z/SPkyxiFMazLN61nQL+jofb7cDHBw2+gCNlAZWqXor4Fnpn/QJPtj DEx3flbg3MD7aj/sW/qph/TcWXeqjDLV1v7ZhT0CqtSPdgk81/DQd+2NV+V2QXThTZ +XkACe/353KDkQnsvh6/M0HSjf1xPfBNFdAYUvje+I9zQHSpfrfAYt0hhYp10SPSoH CETjgrxRdIuJPyLMPKTOD5zjIeaEQUoPTSuiC1NQlSj2CGqyXpQlFPEublAf7nXmHK /utj9Loj3nfeFJPhjURDEG4HWjLDX8QpEkspRtVM8E9NZXBkhi/00p1+osH+E4F9gD ZWcnfJ+08Dmvg== From: Arnd Bergmann To: netdev@vger.kernel.org Cc: Arnd Bergmann , Al Viro , Andrew Lunn , Christoph Hellwig , David Ahern , "David S. Miller" , Eric Dumazet , Hideaki YOSHIFUJI , Jakub Kicinski , Kees Cook , Marco Elver , linux-kernel@vger.kernel.org, linux-arch@vger.kernel.org Subject: [PATCH net-next v6 2/6] ethtool: improve compat ioctl handling Date: Thu, 22 Jul 2021 16:28:59 +0200 Message-Id: <20210722142903.213084-3-arnd@kernel.org> X-Mailer: git-send-email 2.29.2 In-Reply-To: <20210722142903.213084-1-arnd@kernel.org> References: <20210722142903.213084-1-arnd@kernel.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: kuba@kernel.org From: Arnd Bergmann The ethtool compat ioctl handling is hidden away in net/socket.c, which introduces a couple of minor oddities: - The implementation may end up diverging, as seen in the RXNFC extension in commit 84a1d9c48200 ("net: ethtool: extend RXNFC API to support RSS spreading of filter matches") that does not work in compat mode. - Most architectures do not need the compat handling at all because u64 and compat_u64 have the same alignment. - On x86, the conversion is done for both x32 and i386 user space, but it's actually wrong to do it for x32 and cannot work there. - On 32-bit Arm, it never worked for compat oabi user space, since that needs to do the same conversion but does not. - It would be nice to get rid of both compat_alloc_user_space() and copy_in_user() throughout the kernel. None of these actually seems to be a serious problem that real users are likely to encounter, but fixing all of them actually leads to code that is both shorter and more readable. Signed-off-by: Arnd Bergmann Reviewed-by: Christoph Hellwig --- Changes in v2: - remove extraneous 'inline' keyword (davem) - split helper functions into smaller units (hch) - remove arm oabi check with missing dependency (0day bot) --- include/linux/ethtool.h | 4 -- net/ethtool/ioctl.c | 136 +++++++++++++++++++++++++++++++++++----- net/socket.c | 125 +----------------------------------- 3 files changed, 121 insertions(+), 144 deletions(-) diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index 232daaec56e4..4711b96dae0c 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -17,8 +17,6 @@ #include #include -#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 /** diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c index baa5d10043cb..6134b180f59f 100644 --- a/net/ethtool/ioctl.c +++ b/net/ethtool/ioctl.c @@ -7,6 +7,7 @@ * the information ethtool needs. */ +#include #include #include #include @@ -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); diff --git a/net/socket.c b/net/socket.c index 0b2dad3bdf7f..ec63cf6de33e 100644 --- a/net/socket.c +++ b/net/socket.c @@ -3152,128 +3152,6 @@ static int compat_dev_ifconf(struct net *net, struct compat_ifconf __user *uifc3 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; @@ -3428,8 +3306,6 @@ static int compat_sock_ioctl_trans(struct file *file, struct socket *sock, 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: @@ -3442,6 +3318,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: From patchwork Thu Jul 22 14:29:00 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Arnd Bergmann X-Patchwork-Id: 12394227 X-Patchwork-Delegate: kuba@kernel.org Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-20.5 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 276F8C63797 for ; Thu, 22 Jul 2021 14:29:36 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 0D10D61278 for ; Thu, 22 Jul 2021 14:29:36 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232474AbhGVNs6 (ORCPT ); Thu, 22 Jul 2021 09:48:58 -0400 Received: from mail.kernel.org ([198.145.29.99]:45000 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232435AbhGVNsv (ORCPT ); Thu, 22 Jul 2021 09:48:51 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 18C9D61278; Thu, 22 Jul 2021 14:29:23 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1626964166; bh=au6vao3VHfIPdPAVPU8U92s0dP9EsEQ5hVMVd/IrSwM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=B7NQ01a+lU5DAh8cRA1qNTRwVTX4+vPfpTIgzOUFwnyievMyIhSI37TmuXyDX1Ice hqF+kCd2XOMFZByp3Lc2UQybnkVqiHpuPaYnET45aO6hwPiYSVU/2CsdC5Ov4i5aSO fMQpouuRXsDGN/rrV6NkDiDdFxgEbklp/1XLKLOoig0FAaCKjr3CfUJt0+2ypYWHDK UzmIscp2WLxQ9SnpXT0wGx4KTiyuR61Pnd5ekZxPC7dwM62yIEecRqiqr0Uf7U8HtT a2JAJUIcVDyDLQEBzjXUQ9LvQXXesqmEW34hZ1SsFl0668hJcTShu83kv0vPQMuxC8 Ih+Yu6cs+0XeQ== From: Arnd Bergmann To: netdev@vger.kernel.org Cc: Arnd Bergmann , Al Viro , Andrew Lunn , Christoph Hellwig , David Ahern , "David S. Miller" , Eric Dumazet , Hideaki YOSHIFUJI , Jakub Kicinski , Kees Cook , Marco Elver , linux-kernel@vger.kernel.org, linux-arch@vger.kernel.org Subject: [PATCH net-next v6 3/6] net: socket: rework SIOC?IFMAP ioctls Date: Thu, 22 Jul 2021 16:29:00 +0200 Message-Id: <20210722142903.213084-4-arnd@kernel.org> X-Mailer: git-send-email 2.29.2 In-Reply-To: <20210722142903.213084-1-arnd@kernel.org> References: <20210722142903.213084-1-arnd@kernel.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: kuba@kernel.org From: Arnd Bergmann SIOCGIFMAP and SIOCSIFMAP currently require compat_alloc_user_space() and copy_in_user() for compat mode. Move the compat handling into the location where the structures are actually used, to avoid using those interfaces and get a clearer implementation. Reviewed-by: Christoph Hellwig Signed-off-by: Arnd Bergmann --- changes in v3: - complete rewrite changes in v2: - fix building with CONFIG_COMPAT disabled (0day bot) - split up dev_ifmap() into more readable helpers (hch) - move rcu_read_unlock() for readability (hch) --- net/core/dev_ioctl.c | 65 +++++++++++++++++++++++++++++++++++--------- net/socket.c | 39 ++------------------------ 2 files changed, 54 insertions(+), 50 deletions(-) diff --git a/net/core/dev_ioctl.c b/net/core/dev_ioctl.c index 478d032f34ac..62f45da7ecfe 100644 --- a/net/core/dev_ioctl.c +++ b/net/core/dev_ioctl.c @@ -98,6 +98,56 @@ int dev_ifconf(struct net *net, struct ifconf *ifc, int size) return 0; } +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; + + 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 +178,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 +319,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 || diff --git a/net/socket.c b/net/socket.c index ec63cf6de33e..62005a12ec70 100644 --- a/net/socket.c +++ b/net/socket.c @@ -3241,40 +3241,6 @@ static int compat_ifreq_ioctl(struct net *net, struct socket *sock, 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; - 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) - 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; -} - /* Since old style bridge ioctl's endup using SIOCDEVPRIVATE * for some operations; this forces use of the newer bridge-utils that * use compatible ioctls @@ -3308,9 +3274,6 @@ static int compat_sock_ioctl_trans(struct file *file, struct socket *sock, return compat_dev_ifconf(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) @@ -3340,6 +3303,8 @@ static int compat_sock_ioctl_trans(struct file *file, struct socket *sock, case SIOCGIFFLAGS: case SIOCSIFFLAGS: + case SIOCGIFMAP: + case SIOCSIFMAP: case SIOCGIFMETRIC: case SIOCSIFMETRIC: case SIOCGIFMTU: From patchwork Thu Jul 22 14:29:01 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Arnd Bergmann X-Patchwork-Id: 12394233 X-Patchwork-Delegate: kuba@kernel.org Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-20.5 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 391A5C6377D for ; Thu, 22 Jul 2021 14:29:55 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 21A6261278 for ; Thu, 22 Jul 2021 14:29:55 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232530AbhGVNtI (ORCPT ); Thu, 22 Jul 2021 09:49:08 -0400 Received: from mail.kernel.org ([198.145.29.99]:45082 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232416AbhGVNsy (ORCPT ); Thu, 22 Jul 2021 09:48:54 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id F14936127C; Thu, 22 Jul 2021 14:29:26 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1626964169; bh=EOoIV2dalOPoEOZSfB56YVD829AJM/wltGQv+39YEKM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=eJPc+NbmXXXaKlj20nVhfscCVtIKkyAxPO7AioVBZYsLewdsbmpQIaYydtmRbVCuH rd9PFjnJ/59OdaKmCuXeE0a/CM7uK6dJbxFhqCjvejP+RyaEAM26TCNCs5kNzkSleJ z/z9I9kmy+XJ9YpsNVpzr58vQTXBcjzv0vdYR1Z5sRFgZyqMuGi4kpCBGG5iFMA5Ti EeMjmsZwypvBDlya1Ck8MJ9MedeQR4CgiH9PLKEDqIjR/2mPGFcYGV//E/t8QkGrzT HHnr14zixu2TEZr5MAZ6tcVgBaGyAGYZ8C2AGYPPKMSE6CCB5/rxFDreW1yfcqCItu k5Su1ydchP2zA== From: Arnd Bergmann To: netdev@vger.kernel.org Cc: Arnd Bergmann , Al Viro , Andrew Lunn , Christoph Hellwig , David Ahern , "David S. Miller" , Eric Dumazet , Hideaki YOSHIFUJI , Jakub Kicinski , Kees Cook , Marco Elver , linux-kernel@vger.kernel.org, linux-arch@vger.kernel.org Subject: [PATCH net-next v6 4/6] net: socket: remove register_gifconf Date: Thu, 22 Jul 2021 16:29:01 +0200 Message-Id: <20210722142903.213084-5-arnd@kernel.org> X-Mailer: git-send-email 2.29.2 In-Reply-To: <20210722142903.213084-1-arnd@kernel.org> References: <20210722142903.213084-1-arnd@kernel.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: kuba@kernel.org From: Arnd Bergmann Since dynamic registration of the gifconf() helper is only used for IPv4, and this can not be in a loadable module, this can be simplified noticeably by turning it into a direct function call as a preparation for cleaning up the compat handling. Signed-off-by: Arnd Bergmann Reviewed-by: Christoph Hellwig --- changes in v6: - split register_gifconf from compat changes --- include/linux/inetdevice.h | 9 ++++++++ include/linux/netdevice.h | 8 ------- net/core/dev_ioctl.c | 43 +++++++++----------------------------- net/ipv4/devinet.c | 4 +--- 4 files changed, 20 insertions(+), 44 deletions(-) diff --git a/include/linux/inetdevice.h b/include/linux/inetdevice.h index 53aa0343bf69..67e042932681 100644 --- a/include/linux/inetdevice.h +++ b/include/linux/inetdevice.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); diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 42f6f866d5f3..6630a9f0b0f0 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -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 { diff --git a/net/core/dev_ioctl.c b/net/core/dev_ioctl.c index 62f45da7ecfe..c22c3dc15ce9 100644 --- a/net/core/dev_ioctl.c +++ b/net/core/dev_ioctl.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 #include #include +#include #include #include #include @@ -25,26 +26,6 @@ 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. @@ -72,19 +53,15 @@ int dev_ifconf(struct net *net, struct ifconf *ifc, int size) 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; - } - } + int done; + if (!pos) + done = inet_gifconf(dev, NULL, 0, size); + else + done = inet_gifconf(dev, pos + total, + len - total, size); + if (done < 0) + return -EFAULT; + total += done; } /* diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index 94b648d9eaff..c82aded8da7d 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -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); From patchwork Thu Jul 22 14:29:02 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Arnd Bergmann X-Patchwork-Id: 12394231 X-Patchwork-Delegate: kuba@kernel.org Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-20.5 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id DB852C63798 for ; Thu, 22 Jul 2021 14:29:41 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id C0BE36136D for ; Thu, 22 Jul 2021 14:29:41 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232501AbhGVNtF (ORCPT ); Thu, 22 Jul 2021 09:49:05 -0400 Received: from mail.kernel.org ([198.145.29.99]:45112 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232462AbhGVNs5 (ORCPT ); Thu, 22 Jul 2021 09:48:57 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id D31166120C; Thu, 22 Jul 2021 14:29:29 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1626964172; bh=W9+e8IrFAfPvah86bUNMrcJYs6r3bs9IuXWRBwh35Zs=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=V3FMmzoRoQ5drSNc2LsO9o70OC6zOy7OWlVeIlSPiXdvtQFiTJVmooudOcNPe2kNe HRDEOQn/pxoVjqF+nCoT7ON5GmcEhANNqQlHSvEJQSB6OnIaKbuf9MsGBPv527VeY9 kjjAiDnsCh3meFgqZGsKhEVGLroPeqG+GAYSN32dVlZVtSJD+DVJuFINPpmX1eisoR ltFrs7ttGgok84jmIC/eY2DRsCevwuhFnxdoxC58NrshEhSmzYljghdOBVluGRvcNP Pwf8YnVJe4kDXjmXjdqYQIY0aF2H78jhvQYvGsKOCUvVK7O4NG86qspNG0MWmsi+5N qEW8I8Z9i6YIw== From: Arnd Bergmann To: netdev@vger.kernel.org Cc: Arnd Bergmann , Al Viro , Andrew Lunn , Christoph Hellwig , David Ahern , "David S. Miller" , Eric Dumazet , Hideaki YOSHIFUJI , Jakub Kicinski , Kees Cook , Marco Elver , linux-kernel@vger.kernel.org, linux-arch@vger.kernel.org Subject: [PATCH net-next v6 5/6] net: socket: simplify dev_ifconf handling Date: Thu, 22 Jul 2021 16:29:02 +0200 Message-Id: <20210722142903.213084-6-arnd@kernel.org> X-Mailer: git-send-email 2.29.2 In-Reply-To: <20210722142903.213084-1-arnd@kernel.org> References: <20210722142903.213084-1-arnd@kernel.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: kuba@kernel.org From: Arnd Bergmann The dev_ifconf() calling conventions make compat handling more complicated than necessary, simplify this by moving the in_compat_syscall() check into the function. Signed-off-by: Arnd Bergmann Reviewed-by: Christoph Hellwig --- changes in v6: - split register_gifconf from compat changes --- include/linux/netdevice.h | 2 +- net/core/dev_ioctl.c | 55 +++++++++++++++++++----------------- net/socket.c | 59 ++++++++++----------------------------- 3 files changed, 44 insertions(+), 72 deletions(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 6630a9f0b0f0..da2c273c7e0a 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -4008,7 +4008,7 @@ void netdev_rx_handler_unregister(struct net_device *dev); bool dev_valid_name(const char *name); 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, diff --git a/net/core/dev_ioctl.c b/net/core/dev_ioctl.c index c22c3dc15ce9..950e2fe5d56a 100644 --- a/net/core/dev_ioctl.c +++ b/net/core/dev_ioctl.c @@ -31,48 +31,51 @@ static int dev_ifname(struct net *net, struct ifreq *ifr) * 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; + + if (copy_from_user(&ifc32, uifc, sizeof(struct compat_ifconf))) + return -EFAULT; - pos = ifc->ifc_buf; - len = ifc->ifc_len; + pos = compat_ptr(ifc32.ifcbuf); + len = ifc32.ifc_len; + size = sizeof(struct compat_ifreq); + } else { + struct ifconf ifc; - /* - * Loop over the interfaces, and write an info block for each. - */ + if (copy_from_user(&ifc, uifc, sizeof(struct ifconf))) + return -EFAULT; - total = 0; + pos = ifc.ifc_buf; + len = ifc.ifc_len; + size = sizeof(struct ifreq); + } + + /* Loop over the interfaces, and write an info block for each. */ + rtnl_lock(); for_each_netdev(net, dev) { - int done; if (!pos) done = inet_gifconf(dev, NULL, 0, size); else done = inet_gifconf(dev, pos + total, len - total, size); - if (done < 0) + 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; - - /* - * Both BSD and Solaris return 0 here, so we do too. - */ - return 0; + return put_user(total, &uifc->ifc_len); } static int dev_getifmap(struct net_device *dev, struct ifreq *ifr) diff --git a/net/socket.c b/net/socket.c index 62005a12ec70..ecdb7913a3bd 100644 --- a/net/socket.c +++ b/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))) - 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))) + 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; - 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; @@ -3127,31 +3122,6 @@ void socket_seq_show(struct seq_file *seq) #endif /* CONFIG_PROC_FS */ #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 compat_siocwandev(struct net *net, struct compat_ifreq __user *uifr32) { compat_uptr_t uptr32; @@ -3270,8 +3240,6 @@ 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 SIOCWANDEV: return compat_siocwandev(net, argp); case SIOCGSTAMP_OLD: @@ -3299,6 +3267,7 @@ 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: From patchwork Thu Jul 22 14:29:03 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Arnd Bergmann X-Patchwork-Id: 12394229 X-Patchwork-Delegate: kuba@kernel.org Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-20.5 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id BD81CC63793 for ; Thu, 22 Jul 2021 14:29:40 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id A63D26100C for ; Thu, 22 Jul 2021 14:29:40 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232487AbhGVNtE (ORCPT ); Thu, 22 Jul 2021 09:49:04 -0400 Received: from mail.kernel.org ([198.145.29.99]:45158 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232479AbhGVNtA (ORCPT ); Thu, 22 Jul 2021 09:49:00 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id BFCF261264; Thu, 22 Jul 2021 14:29:32 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1626964175; bh=KjBue2rqqdcIaMQLxNsPwViX+cBWxqB88BIRymALCT4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=LbA+tMghE8Rl2XzXqEYdMox+k6RRecwqy/xIWzVsR8vdYqsc8wQ3KDOKuToNShDpf +SPX4cN4R2iAcDI3rQb5exHyQQuqU+f3gYaEP1x/PhcgSkRJuvuUyqf14G2oKWCUHC AlN7vVA9+rdbIKdDRidvQsA4ifebAnbFGYHydubhXH7Ns5XUsZZ/S09vx+7J8CPVLz W96B6KPjNDfh7vgO0kNp0vdFWJjnRUz/zpIsGRXPVqbtYjVi9yTFj3rYm62SMzh+Fp nDa5jMJwngTIbIq0Z0JGl8qeu3WsCHML03nJM7/EZ4CqFdK+7dC5MVnM+fUdLMV/lb TdF5Xze3vx9Vw== From: Arnd Bergmann To: netdev@vger.kernel.org Cc: Arnd Bergmann , Al Viro , Andrew Lunn , Christoph Hellwig , David Ahern , "David S. Miller" , Eric Dumazet , Hideaki YOSHIFUJI , Jakub Kicinski , Kees Cook , Marco Elver , linux-kernel@vger.kernel.org, linux-arch@vger.kernel.org Subject: [PATCH net-next v6 6/6] net: socket: rework compat_ifreq_ioctl() Date: Thu, 22 Jul 2021 16:29:03 +0200 Message-Id: <20210722142903.213084-7-arnd@kernel.org> X-Mailer: git-send-email 2.29.2 In-Reply-To: <20210722142903.213084-1-arnd@kernel.org> References: <20210722142903.213084-1-arnd@kernel.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: kuba@kernel.org From: Arnd Bergmann compat_ifreq_ioctl() is one of the last users of copy_in_user() and compat_alloc_user_space(), as it attempts to convert the 'struct ifreq' arguments from 32-bit to 64-bit format as used by dev_ioctl() and a couple of socket family specific interpretations. The current implementation works correctly when calling dev_ioctl(), inet_ioctl(), ieee802154_sock_ioctl(), atalk_ioctl(), qrtr_ioctl() and packet_ioctl(). The ioctl handlers for x25, netrom, rose and x25 do not interpret the arguments and only block the corresponding commands, so they do not care. For af_inet6 and af_decnet however, the compat conversion is slightly incorrect, as it will copy more data than the native handler accesses, both of them use a structure that is shorter than ifreq. Replace the copy_in_user() conversion with a pair of accessor functions to read and write the ifreq data in place with the correct length where needed, while leaving the other ones to copy the (already compatible) structures directly. Signed-off-by: Arnd Bergmann Reviewed-by: Christoph Hellwig --- include/linux/netdevice.h | 2 + net/appletalk/ddp.c | 4 +- net/ieee802154/socket.c | 4 +- net/ipv4/af_inet.c | 6 +-- net/qrtr/qrtr.c | 4 +- net/socket.c | 103 ++++++++++++++++++++++++-------------- 6 files changed, 76 insertions(+), 47 deletions(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index da2c273c7e0a..c871dc223dfa 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -4006,6 +4006,8 @@ 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 __user *ifc); diff --git a/net/appletalk/ddp.c b/net/appletalk/ddp.c index 8ade5a4ceaf5..bf5736c1d458 100644 --- a/net/appletalk/ddp.c +++ b/net/appletalk/ddp.c @@ -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) diff --git a/net/ieee802154/socket.c b/net/ieee802154/socket.c index a45a0401adc5..f5077de3619e 100644 --- a/net/ieee802154/socket.c +++ b/net/ieee802154/socket.c @@ -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); diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 54648181dd56..0e4d758c2585 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -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; diff --git a/net/qrtr/qrtr.c b/net/qrtr/qrtr.c index e6f4a6202f82..e71847877248 100644 --- a/net/qrtr/qrtr.c +++ b/net/qrtr/qrtr.c @@ -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; } diff --git a/net/socket.c b/net/socket.c index ecdb7913a3bd..84de89c1ee9d 100644 --- a/net/socket.c +++ b/net/socket.c @@ -3121,6 +3121,54 @@ 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_siocwandev(struct net *net, struct compat_ifreq __user *uifr32) { @@ -3129,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)) @@ -3141,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; @@ -3165,49 +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; + struct ifreq ifr; + bool need_copyout; 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. + 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; - uifr = compat_alloc_user_space(sizeof(*uifr)); - if (copy_in_user(uifr, uifr32, sizeof(*uifr32))) + 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 = 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; } @@ -3310,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: