Skip to content

Commit 0ceb1ac

Browse files
Sergei Trofimovichtorvalds
authored andcommitted
ia64: fix ia64_syscall_get_set_arguments() for break-based syscalls
In https://bugs.gentoo.org/769614 Dmitry noticed that `ptrace(PTRACE_GET_SYSCALL_INFO)` does not work for syscalls called via glibc's syscall() wrapper. ia64 has two ways to call syscalls from userspace: via `break` and via `eps` instructions. The difference is in stack layout: 1. `eps` creates simple stack frame: no locals, in{0..7} == out{0..8} 2. `break` uses userspace stack frame: may be locals (glibc provides one), in{0..7} == out{0..8}. Both work fine in syscall handling cde itself. But `ptrace(PTRACE_GET_SYSCALL_INFO)` uses unwind mechanism to re-extract syscall arguments but it does not account for locals. The change always skips locals registers. It should not change `eps` path as kernel's handler already enforces locals=0 and fixes `break`. Tested on v5.10 on rx3600 machine (ia64 9040 CPU). Link: https://lkml.kernel.org/r/20210221002554.333076-1-slyfox@gentoo.org Link: https://bugs.gentoo.org/769614 Signed-off-by: Sergei Trofimovich <slyfox@gentoo.org> Reported-by: Dmitry V. Levin <ldv@altlinux.org> Cc: Oleg Nesterov <oleg@redhat.com> Cc: John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
1 parent 6ce6442 commit 0ceb1ac

File tree

1 file changed

+18
-6
lines changed

1 file changed

+18
-6
lines changed

arch/ia64/kernel/ptrace.c

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2013,27 +2013,39 @@ static void syscall_get_set_args_cb(struct unw_frame_info *info, void *data)
20132013
{
20142014
struct syscall_get_set_args *args = data;
20152015
struct pt_regs *pt = args->regs;
2016-
unsigned long *krbs, cfm, ndirty;
2016+
unsigned long *krbs, cfm, ndirty, nlocals, nouts;
20172017
int i, count;
20182018

20192019
if (unw_unwind_to_user(info) < 0)
20202020
return;
20212021

2022+
/*
2023+
* We get here via a few paths:
2024+
* - break instruction: cfm is shared with caller.
2025+
* syscall args are in out= regs, locals are non-empty.
2026+
* - epsinstruction: cfm is set by br.call
2027+
* locals don't exist.
2028+
*
2029+
* For both cases argguments are reachable in cfm.sof - cfm.sol.
2030+
* CFM: [ ... | sor: 17..14 | sol : 13..7 | sof : 6..0 ]
2031+
*/
20222032
cfm = pt->cr_ifs;
2033+
nlocals = (cfm >> 7) & 0x7f; /* aka sol */
2034+
nouts = (cfm & 0x7f) - nlocals; /* aka sof - sol */
20232035
krbs = (unsigned long *)info->task + IA64_RBS_OFFSET/8;
20242036
ndirty = ia64_rse_num_regs(krbs, krbs + (pt->loadrs >> 19));
20252037

20262038
count = 0;
20272039
if (in_syscall(pt))
2028-
count = min_t(int, args->n, cfm & 0x7f);
2040+
count = min_t(int, args->n, nouts);
20292041

2042+
/* Iterate over outs. */
20302043
for (i = 0; i < count; i++) {
2044+
int j = ndirty + nlocals + i + args->i;
20312045
if (args->rw)
2032-
*ia64_rse_skip_regs(krbs, ndirty + i + args->i) =
2033-
args->args[i];
2046+
*ia64_rse_skip_regs(krbs, j) = args->args[i];
20342047
else
2035-
args->args[i] = *ia64_rse_skip_regs(krbs,
2036-
ndirty + i + args->i);
2048+
args->args[i] = *ia64_rse_skip_regs(krbs, j);
20372049
}
20382050

20392051
if (!args->rw) {

0 commit comments

Comments
 (0)