From: Yury Norov ynorov@caviumnetworks.com
hulk inclusion category: feature bugzilla: NA CVE: NA ---------------------------
Depending on the personality of the task, syscalls has to be dispatched to either aarch64, aarch32 or aarch64/ilp32 syscall handlers. We add the support of ILP32 mode in this series, therefore introduce corresponding syscall table.
Some system calls are wired to aarch32 syscall handlers, as listed in arch/arm64/kernel/sys_ilp32.c.
For aarch64/ilp32, top halves of syscall arguments are meaningless anthough not zeroed by hardware. Do that in the delouse_pt_regs() routine to avoid passing garbage by userspace.
Signed-off-by: Yury Norov ynorov@caviumnetworks.com Signed-off-by: Xiongfeng Wang wangxiongfeng2@huawei.com Reviewed-by: Hanjun Guo <guohanjun@huawei.com mailto:guohanjun@huawei.com> Signed-off-by: Yang Yingliang yangyingliang@huawei.com --- arch/arm64/include/asm/syscall.h | 4 ++ arch/arm64/include/asm/unistd.h | 8 +++- arch/arm64/include/uapi/asm/unistd.h | 12 +++++ arch/arm64/kernel/Makefile | 2 +- arch/arm64/kernel/sys_ilp32.c | 69 ++++++++++++++++++++++++++++ arch/arm64/kernel/syscall.c | 25 +++++++++- 6 files changed, 116 insertions(+), 4 deletions(-) create mode 100644 arch/arm64/kernel/sys_ilp32.c
diff --git a/arch/arm64/include/asm/syscall.h b/arch/arm64/include/asm/syscall.h index cf7a43521c67d..7ffed9d85f0ae 100644 --- a/arch/arm64/include/asm/syscall.h +++ b/arch/arm64/include/asm/syscall.h @@ -28,6 +28,10 @@ extern const syscall_fn_t sys_call_table[]; extern const syscall_fn_t a32_sys_call_table[]; #endif
+#ifdef CONFIG_ARM64_ILP32 +extern const syscall_fn_t ilp32_sys_call_table[]; +#endif + static inline int syscall_get_nr(struct task_struct *task, struct pt_regs *regs) { diff --git a/arch/arm64/include/asm/unistd.h b/arch/arm64/include/asm/unistd.h index c17ae448c3574..f3401de12dc9d 100644 --- a/arch/arm64/include/asm/unistd.h +++ b/arch/arm64/include/asm/unistd.h @@ -13,12 +13,16 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see http://www.gnu.org/licenses/. */ -#ifdef CONFIG_AARCH32_EL0 + +#ifdef CONFIG_COMPAT #define __ARCH_WANT_COMPAT_STAT64 +#define __ARCH_WANT_SYS_LLSEEK +#endif + +#ifdef CONFIG_AARCH32_EL0 #define __ARCH_WANT_SYS_GETHOSTNAME #define __ARCH_WANT_SYS_PAUSE #define __ARCH_WANT_SYS_GETPGRP -#define __ARCH_WANT_SYS_LLSEEK #define __ARCH_WANT_SYS_NICE #define __ARCH_WANT_SYS_SIGPENDING #define __ARCH_WANT_SYS_SIGPROCMASK diff --git a/arch/arm64/include/uapi/asm/unistd.h b/arch/arm64/include/uapi/asm/unistd.h index 439b1c55c827b..80f1cb4ae2e13 100644 --- a/arch/arm64/include/uapi/asm/unistd.h +++ b/arch/arm64/include/uapi/asm/unistd.h @@ -15,7 +15,19 @@ * along with this program. If not, see http://www.gnu.org/licenses/. */
+/* + * Use AARCH32 interface for sys_sync_file_range() as it passes 64-bit arguments. + */ +#if defined(__ILP32__) || defined(__SYSCALL_COMPAT) +#define __ARCH_WANT_SYNC_FILE_RANGE2 +#endif + +/* + * AARCH64/ILP32 is introduced after next syscalls were deprecated. + */ +#if !(defined(__ILP32__) || defined(__SYSCALL_COMPAT)) #define __ARCH_WANT_RENAMEAT #define __ARCH_WANT_SET_GET_RLIMIT +#endif
#include <asm-generic/unistd.h> diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile index f6157900a395c..47093d4cb2078 100644 --- a/arch/arm64/kernel/Makefile +++ b/arch/arm64/kernel/Makefile @@ -29,7 +29,7 @@ $(obj)/%.stub.o: $(obj)/%.o FORCE
arm64-obj-$(CONFIG_AARCH32_EL0) += sys32.o kuser32.o signal32.o \ sys_compat.o binfmt_elf32.o -arm64-obj-$(CONFIG_ARM64_ILP32) += binfmt_ilp32.o +arm64-obj-$(CONFIG_ARM64_ILP32) += binfmt_ilp32.o sys_ilp32.o arm64-obj-$(CONFIG_COMPAT) += sys32_common.o arm64-obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o entry-ftrace.o arm64-obj-$(CONFIG_MODULES) += arm64ksyms.o module.o diff --git a/arch/arm64/kernel/sys_ilp32.c b/arch/arm64/kernel/sys_ilp32.c new file mode 100644 index 0000000000000..169a381363110 --- /dev/null +++ b/arch/arm64/kernel/sys_ilp32.c @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/* + * AArch64- ILP32 specific system calls implementation + * Copyright (C) 2018 Marvell. + */ + +#define __SYSCALL_COMPAT + +#include <linux/compat.h> +#include <linux/compiler.h> +#include <linux/syscalls.h> + +#include <asm/syscall.h> + +/* + * AARCH32 requires 4-page alignment for shared memory, + * but AARCH64 - only 1 page. This is the only difference + * between compat and native sys_shmat(). So ILP32 just pick + * AARCH64 version. + */ +#define __arm64_compat_sys_shmat __arm64_sys_shmat + +/* + * ILP32 needs special handling for some ptrace requests. + */ +#define __arm64_sys_ptrace __arm64_compat_sys_ptrace + +/* + * Using AARCH32 interface for syscalls that take 64-bit + * parameters in registers. + */ +#define __arm64_compat_sys_fadvise64_64 __arm64_compat_sys_aarch32_fadvise64_64 +#define __arm64_compat_sys_fallocate __arm64_compat_sys_aarch32_fallocate +#define __arm64_compat_sys_ftruncate64 __arm64_compat_sys_aarch32_ftruncate64 +#define __arm64_compat_sys_pread64 __arm64_compat_sys_aarch32_pread64 +#define __arm64_compat_sys_pwrite64 __arm64_compat_sys_aarch32_pwrite64 +#define __arm64_compat_sys_readahead __arm64_compat_sys_aarch32_readahead +#define __arm64_compat_sys_sync_file_range2 __arm64_compat_sys_aarch32_sync_file_range2 +#define __arm64_compat_sys_truncate64 __arm64_compat_sys_aarch32_truncate64 +#define __arm64_sys_mmap2 __arm64_compat_sys_aarch32_mmap2 + +/* + * Using AARCH32 interface for syscalls that take the size of + * struct statfs as an argument, as it's calculated differently + * in kernel and user spaces. + */ +#define __arm64_compat_sys_fstatfs64 __arm64_compat_sys_aarch32_fstatfs64 +#define __arm64_compat_sys_statfs64 __arm64_compat_sys_aarch32_statfs64 + +/* + * Wrappers to pass the pt_regs argument. + */ +#define sys_personality sys_arm64_personality + +asmlinkage long sys_ni_syscall(const struct pt_regs *); +#define __arm64_sys_ni_syscall sys_ni_syscall + +#undef __SYSCALL +#define __SYSCALL(nr, sym) asmlinkage long __arm64_##sym(const struct pt_regs *); +#include <asm/unistd.h> + +#undef __SYSCALL +#define __SYSCALL(nr, sym) [nr] = (syscall_fn_t)__arm64_##sym, + +const syscall_fn_t ilp32_sys_call_table[__NR_syscalls] = { + [0 ... __NR_syscalls - 1] = (syscall_fn_t)sys_ni_syscall, +#include <asm/unistd.h> +}; diff --git a/arch/arm64/kernel/syscall.c b/arch/arm64/kernel/syscall.c index 67021e7d7986b..20b6ebbd79d67 100644 --- a/arch/arm64/kernel/syscall.c +++ b/arch/arm64/kernel/syscall.c @@ -157,10 +157,33 @@ static inline void sve_user_discard(void) sve_user_disable(); }
+#ifdef CONFIG_ARM64_ILP32 +static inline void delouse_pt_regs(struct pt_regs *regs) +{ + regs->regs[0] &= UINT_MAX; + regs->regs[1] &= UINT_MAX; + regs->regs[2] &= UINT_MAX; + regs->regs[3] &= UINT_MAX; + regs->regs[4] &= UINT_MAX; + regs->regs[5] &= UINT_MAX; + regs->regs[6] &= UINT_MAX; + regs->regs[7] &= UINT_MAX; +} +#endif + asmlinkage void el0_svc_handler(struct pt_regs *regs) { + const syscall_fn_t *t = sys_call_table; + +#ifdef CONFIG_ARM64_ILP32 + if (is_ilp32_compat_task()) { + t = ilp32_sys_call_table; + delouse_pt_regs(regs); + } +#endif + sve_user_discard(); - el0_svc_common(regs, regs->regs[8], __NR_syscalls, sys_call_table); + el0_svc_common(regs, regs->regs[8], __NR_syscalls, t); }
#ifdef CONFIG_AARCH32_EL0