From: Greg Kroah-Hartman Date: Tue, 10 Aug 2021 15:03:04 +0000 (+0200) Subject: 5.4-stable patches X-Git-Tag: v4.4.280~3 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=7d45fe4ba0093584883c6421b08a8d30b3b335b9;p=thirdparty%2Fkernel%2Fstable-queue.git 5.4-stable patches added patches: arm64-fix-compat-syscall-return-truncation.patch --- diff --git a/queue-5.4/arm64-fix-compat-syscall-return-truncation.patch b/queue-5.4/arm64-fix-compat-syscall-return-truncation.patch new file mode 100644 index 00000000000..b73497df922 --- /dev/null +++ b/queue-5.4/arm64-fix-compat-syscall-return-truncation.patch @@ -0,0 +1,176 @@ +From e30e8d46cf605d216a799a28c77b8a41c328613a Mon Sep 17 00:00:00 2001 +From: Mark Rutland +Date: Mon, 2 Aug 2021 11:42:00 +0100 +Subject: arm64: fix compat syscall return truncation + +From: Mark Rutland + +commit e30e8d46cf605d216a799a28c77b8a41c328613a upstream. + +Due to inconsistencies in the way we manipulate compat GPRs, we have a +few issues today: + +* For audit and tracing, where error codes are handled as a (native) + long, negative error codes are expected to be sign-extended to the + native 64-bits, or they may fail to be matched correctly. Thus a + syscall which fails with an error may erroneously be identified as + failing. + +* For ptrace, *all* compat return values should be sign-extended for + consistency with 32-bit arm, but we currently only do this for + negative return codes. + +* As we may transiently set the upper 32 bits of some compat GPRs while + in the kernel, these can be sampled by perf, which is somewhat + confusing. This means that where a syscall returns a pointer above 2G, + this will be sign-extended, but will not be mistaken for an error as + error codes are constrained to the inclusive range [-4096, -1] where + no user pointer can exist. + +To fix all of these, we must consistently use helpers to get/set the +compat GPRs, ensuring that we never write the upper 32 bits of the +return code, and always sign-extend when reading the return code. This +patch does so, with the following changes: + +* We re-organise syscall_get_return_value() to always sign-extend for + compat tasks, and reimplement syscall_get_error() atop. We update + syscall_trace_exit() to use syscall_get_return_value(). + +* We consistently use syscall_set_return_value() to set the return + value, ensureing the upper 32 bits are never set unexpectedly. + +* As the core audit code currently uses regs_return_value() rather than + syscall_get_return_value(), we special-case this for + compat_user_mode(regs) such that this will do the right thing. Going + forward, we should try to move the core audit code over to + syscall_get_return_value(). + +Cc: +Reported-by: He Zhe +Reported-by: weiyuchen +Cc: Catalin Marinas +Cc: Will Deacon +Reviewed-by: Catalin Marinas +Link: https://lore.kernel.org/r/20210802104200.21390-1-mark.rutland@arm.com +Signed-off-by: Will Deacon +[Mark: trivial conflict resolution for v5.4.y] +Signed-off-by: Mark Rutland +Signed-off-by: Greg Kroah-Hartman + +--- + arch/arm64/include/asm/ptrace.h | 12 +++++++++++- + arch/arm64/include/asm/syscall.h | 19 ++++++++++--------- + arch/arm64/kernel/ptrace.c | 2 +- + arch/arm64/kernel/signal.c | 3 ++- + arch/arm64/kernel/syscall.c | 7 ++----- + 5 files changed, 26 insertions(+), 17 deletions(-) + +--- a/arch/arm64/include/asm/ptrace.h ++++ b/arch/arm64/include/asm/ptrace.h +@@ -299,7 +299,17 @@ static inline unsigned long kernel_stack + + static inline unsigned long regs_return_value(struct pt_regs *regs) + { +- return regs->regs[0]; ++ unsigned long val = regs->regs[0]; ++ ++ /* ++ * Audit currently uses regs_return_value() instead of ++ * syscall_get_return_value(). Apply the same sign-extension here until ++ * audit is updated to use syscall_get_return_value(). ++ */ ++ if (compat_user_mode(regs)) ++ val = sign_extend64(val, 31); ++ ++ return val; + } + + static inline void regs_set_return_value(struct pt_regs *regs, unsigned long rc) +--- a/arch/arm64/include/asm/syscall.h ++++ b/arch/arm64/include/asm/syscall.h +@@ -29,22 +29,23 @@ static inline void syscall_rollback(stru + regs->regs[0] = regs->orig_x0; + } + +- +-static inline long syscall_get_error(struct task_struct *task, +- struct pt_regs *regs) ++static inline long syscall_get_return_value(struct task_struct *task, ++ struct pt_regs *regs) + { +- unsigned long error = regs->regs[0]; ++ unsigned long val = regs->regs[0]; + + if (is_compat_thread(task_thread_info(task))) +- error = sign_extend64(error, 31); ++ val = sign_extend64(val, 31); + +- return IS_ERR_VALUE(error) ? error : 0; ++ return val; + } + +-static inline long syscall_get_return_value(struct task_struct *task, +- struct pt_regs *regs) ++static inline long syscall_get_error(struct task_struct *task, ++ struct pt_regs *regs) + { +- return regs->regs[0]; ++ unsigned long error = syscall_get_return_value(task, regs); ++ ++ return IS_ERR_VALUE(error) ? error : 0; + } + + static inline void syscall_set_return_value(struct task_struct *task, +--- a/arch/arm64/kernel/ptrace.c ++++ b/arch/arm64/kernel/ptrace.c +@@ -1868,7 +1868,7 @@ void syscall_trace_exit(struct pt_regs * + audit_syscall_exit(regs); + + if (flags & _TIF_SYSCALL_TRACEPOINT) +- trace_sys_exit(regs, regs_return_value(regs)); ++ trace_sys_exit(regs, syscall_get_return_value(current, regs)); + + if (flags & (_TIF_SYSCALL_TRACE | _TIF_SINGLESTEP)) + tracehook_report_syscall(regs, PTRACE_SYSCALL_EXIT); +--- a/arch/arm64/kernel/signal.c ++++ b/arch/arm64/kernel/signal.c +@@ -29,6 +29,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -868,7 +869,7 @@ static void do_signal(struct pt_regs *re + retval == -ERESTART_RESTARTBLOCK || + (retval == -ERESTARTSYS && + !(ksig.ka.sa.sa_flags & SA_RESTART)))) { +- regs->regs[0] = -EINTR; ++ syscall_set_return_value(current, regs, -EINTR, 0); + regs->pc = continue_addr; + } + +--- a/arch/arm64/kernel/syscall.c ++++ b/arch/arm64/kernel/syscall.c +@@ -50,10 +50,7 @@ static void invoke_syscall(struct pt_reg + ret = do_ni_syscall(regs, scno); + } + +- if (is_compat_task()) +- ret = lower_32_bits(ret); +- +- regs->regs[0] = ret; ++ syscall_set_return_value(current, regs, 0, ret); + } + + static inline bool has_syscall_work(unsigned long flags) +@@ -108,7 +105,7 @@ static void el0_svc_common(struct pt_reg + if (has_syscall_work(flags)) { + /* set default errno for user-issued syscall(-1) */ + if (scno == NO_SYSCALL) +- regs->regs[0] = -ENOSYS; ++ syscall_set_return_value(current, regs, -ENOSYS, 0); + scno = syscall_trace_enter(regs); + if (scno == NO_SYSCALL) + goto trace_exit; diff --git a/queue-5.4/series b/queue-5.4/series index e7098cb6b92..1104e3087bb 100644 --- a/queue-5.4/series +++ b/queue-5.4/series @@ -82,3 +82,4 @@ reiserfs-check-directory-items-on-read-from-disk.patch virt_wifi-fix-error-on-connect.patch alpha-send-stop-ipi-to-send-to-online-cpus.patch net-qla3xxx-fix-schedule-while-atomic-in-ql_wait_for.patch +arm64-fix-compat-syscall-return-truncation.patch