Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

powerpc/32: fix syscall wrappers with 64-bit arguments of unaligned register-pairs

powerpc 32-bit system call (and function) calling convention for 64-bit
arguments requires the next available odd-pair (two sequential registers
with the first being odd-numbered) from the standard register argument
allocation.

The first argument register is r3, so a 64-bit argument that appears at
an even position in the argument list must skip a register (unless there
were preceding 64-bit arguments, which might throw things off). This
requires non-standard compat definitions to deal with the holes in the
argument register allocation.

With pt_regs syscall wrappers which use a standard mapper to map pt_regs
GPRs to function arguments, 32-bit kernels hit the same basic problem,
the standard definitions don't cope with the unused argument registers.

Fix this by having 32-bit kernels share those syscall definitions with
compat.

Thanks to Jason for spending a lot of time finding and bisecting this
and developing a trivial reproducer. The perfect bug report.

Reported-by: Jason A. Donenfeld <Jason@zx2c4.com>
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
Fixes: 7e92e01b72452 ("powerpc: Provide syscall wrapper")
Reviewed-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20221012035335.866440-1-npiggin@gmail.com

authored by

Nicholas Piggin and committed by
Michael Ellerman
e2375062 ae5b6779

+56 -15
+16
arch/powerpc/include/asm/syscalls.h
··· 89 89 * responsible for combining parameter pairs. 90 90 */ 91 91 92 + #ifdef CONFIG_PPC32 93 + long sys_ppc_pread64(unsigned int fd, 94 + char __user *ubuf, compat_size_t count, 95 + u32 reg6, u32 pos1, u32 pos2); 96 + long sys_ppc_pwrite64(unsigned int fd, 97 + const char __user *ubuf, compat_size_t count, 98 + u32 reg6, u32 pos1, u32 pos2); 99 + long sys_ppc_readahead(int fd, u32 r4, 100 + u32 offset1, u32 offset2, u32 count); 101 + long sys_ppc_truncate64(const char __user *path, u32 reg4, 102 + unsigned long len1, unsigned long len2); 103 + long sys_ppc_ftruncate64(unsigned int fd, u32 reg4, 104 + unsigned long len1, unsigned long len2); 105 + long sys_ppc32_fadvise64(int fd, u32 unused, u32 offset1, u32 offset2, 106 + size_t len, int advice); 107 + #endif 92 108 #ifdef CONFIG_COMPAT 93 109 long compat_sys_mmap2(unsigned long addr, size_t len, 94 110 unsigned long prot, unsigned long flags,
+1
arch/powerpc/kernel/Makefile
··· 73 73 obj-y += ptrace/ 74 74 obj-$(CONFIG_PPC64) += setup_64.o irq_64.o\ 75 75 paca.o nvram_64.o note.o 76 + obj-$(CONFIG_PPC32) += sys_ppc32.o 76 77 obj-$(CONFIG_COMPAT) += sys_ppc32.o signal_32.o 77 78 obj-$(CONFIG_VDSO32) += vdso32_wrapper.o 78 79 obj-$(CONFIG_PPC_WATCHDOG) += watchdog.o
+29 -9
arch/powerpc/kernel/sys_ppc32.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0-or-later 2 2 /* 3 - * sys_ppc32.c: Conversion between 32bit and 64bit native syscalls. 3 + * sys_ppc32.c: 32-bit system calls with complex calling conventions. 4 4 * 5 5 * Copyright (C) 2001 IBM 6 6 * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) 7 7 * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) 8 8 * 9 - * These routines maintain argument size conversion between 32bit and 64bit 10 - * environment. 9 + * 32-bit system calls with 64-bit arguments pass those in register pairs. 10 + * This must be specially dealt with on 64-bit kernels. The compat_arg_u64_dual 11 + * in generic compat syscalls is not always usable because the register 12 + * pairing is constrained depending on preceding arguments. 13 + * 14 + * An analogous problem exists on 32-bit kernels with ARCH_HAS_SYSCALL_WRAPPER, 15 + * the defined system call functions take the pt_regs as an argument, and there 16 + * is a mapping macro which maps registers to arguments 17 + * (SC_POWERPC_REGS_TO_ARGS) which also does not deal with these 64-bit 18 + * arguments. 19 + * 20 + * This file contains these system calls. 11 21 */ 12 22 13 23 #include <linux/kernel.h> ··· 57 47 #include <asm/syscalls.h> 58 48 #include <asm/switch_to.h> 59 49 60 - COMPAT_SYSCALL_DEFINE6(ppc_pread64, 50 + #ifdef CONFIG_PPC32 51 + #define PPC32_SYSCALL_DEFINE4 SYSCALL_DEFINE4 52 + #define PPC32_SYSCALL_DEFINE5 SYSCALL_DEFINE5 53 + #define PPC32_SYSCALL_DEFINE6 SYSCALL_DEFINE6 54 + #else 55 + #define PPC32_SYSCALL_DEFINE4 COMPAT_SYSCALL_DEFINE4 56 + #define PPC32_SYSCALL_DEFINE5 COMPAT_SYSCALL_DEFINE5 57 + #define PPC32_SYSCALL_DEFINE6 COMPAT_SYSCALL_DEFINE6 58 + #endif 59 + 60 + PPC32_SYSCALL_DEFINE6(ppc_pread64, 61 61 unsigned int, fd, 62 62 char __user *, ubuf, compat_size_t, count, 63 63 u32, reg6, u32, pos1, u32, pos2) ··· 75 55 return ksys_pread64(fd, ubuf, count, merge_64(pos1, pos2)); 76 56 } 77 57 78 - COMPAT_SYSCALL_DEFINE6(ppc_pwrite64, 58 + PPC32_SYSCALL_DEFINE6(ppc_pwrite64, 79 59 unsigned int, fd, 80 60 const char __user *, ubuf, compat_size_t, count, 81 61 u32, reg6, u32, pos1, u32, pos2) ··· 83 63 return ksys_pwrite64(fd, ubuf, count, merge_64(pos1, pos2)); 84 64 } 85 65 86 - COMPAT_SYSCALL_DEFINE5(ppc_readahead, 66 + PPC32_SYSCALL_DEFINE5(ppc_readahead, 87 67 int, fd, u32, r4, 88 68 u32, offset1, u32, offset2, u32, count) 89 69 { 90 70 return ksys_readahead(fd, merge_64(offset1, offset2), count); 91 71 } 92 72 93 - COMPAT_SYSCALL_DEFINE4(ppc_truncate64, 73 + PPC32_SYSCALL_DEFINE4(ppc_truncate64, 94 74 const char __user *, path, u32, reg4, 95 75 unsigned long, len1, unsigned long, len2) 96 76 { 97 77 return ksys_truncate(path, merge_64(len1, len2)); 98 78 } 99 79 100 - COMPAT_SYSCALL_DEFINE4(ppc_ftruncate64, 80 + PPC32_SYSCALL_DEFINE4(ppc_ftruncate64, 101 81 unsigned int, fd, u32, reg4, 102 82 unsigned long, len1, unsigned long, len2) 103 83 { 104 84 return ksys_ftruncate(fd, merge_64(len1, len2)); 105 85 } 106 86 107 - COMPAT_SYSCALL_DEFINE6(ppc32_fadvise64, 87 + PPC32_SYSCALL_DEFINE6(ppc32_fadvise64, 108 88 int, fd, u32, unused, u32, offset1, u32, offset2, 109 89 size_t, len, int, advice) 110 90 {
+10 -6
arch/powerpc/kernel/syscalls/syscall.tbl
··· 228 228 176 64 rt_sigtimedwait sys_rt_sigtimedwait 229 229 177 nospu rt_sigqueueinfo sys_rt_sigqueueinfo compat_sys_rt_sigqueueinfo 230 230 178 nospu rt_sigsuspend sys_rt_sigsuspend compat_sys_rt_sigsuspend 231 - 179 common pread64 sys_pread64 compat_sys_ppc_pread64 232 - 180 common pwrite64 sys_pwrite64 compat_sys_ppc_pwrite64 231 + 179 32 pread64 sys_ppc_pread64 compat_sys_ppc_pread64 232 + 179 64 pread64 sys_pread64 233 + 180 32 pwrite64 sys_ppc_pwrite64 compat_sys_ppc_pwrite64 234 + 180 64 pwrite64 sys_pwrite64 233 235 181 common chown sys_chown 234 236 182 common getcwd sys_getcwd 235 237 183 common capget sys_capget ··· 244 242 188 common putpmsg sys_ni_syscall 245 243 189 nospu vfork sys_vfork 246 244 190 common ugetrlimit sys_getrlimit compat_sys_getrlimit 247 - 191 common readahead sys_readahead compat_sys_ppc_readahead 245 + 191 32 readahead sys_ppc_readahead compat_sys_ppc_readahead 246 + 191 64 readahead sys_readahead 248 247 192 32 mmap2 sys_mmap2 compat_sys_mmap2 249 - 193 32 truncate64 sys_truncate64 compat_sys_ppc_truncate64 250 - 194 32 ftruncate64 sys_ftruncate64 compat_sys_ppc_ftruncate64 248 + 193 32 truncate64 sys_ppc_truncate64 compat_sys_ppc_truncate64 249 + 194 32 ftruncate64 sys_ppc_ftruncate64 compat_sys_ppc_ftruncate64 251 250 195 32 stat64 sys_stat64 252 251 196 32 lstat64 sys_lstat64 253 252 197 32 fstat64 sys_fstat64 ··· 291 288 230 common io_submit sys_io_submit compat_sys_io_submit 292 289 231 common io_cancel sys_io_cancel 293 290 232 nospu set_tid_address sys_set_tid_address 294 - 233 common fadvise64 sys_fadvise64 compat_sys_ppc32_fadvise64 291 + 233 32 fadvise64 sys_ppc32_fadvise64 compat_sys_ppc32_fadvise64 292 + 233 64 fadvise64 sys_fadvise64 295 293 234 nospu exit_group sys_exit_group 296 294 235 nospu lookup_dcookie sys_lookup_dcookie compat_sys_lookup_dcookie 297 295 236 common epoll_create sys_epoll_create