162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci#include <linux/ptrace.h> 362306a36Sopenharmony_ci#include <linux/sched.h> 462306a36Sopenharmony_ci#include <linux/sched/task_stack.h> 562306a36Sopenharmony_ci#include <linux/export.h> 662306a36Sopenharmony_ci#include <asm/syscall.h> 762306a36Sopenharmony_ci 862306a36Sopenharmony_cistatic int collect_syscall(struct task_struct *target, struct syscall_info *info) 962306a36Sopenharmony_ci{ 1062306a36Sopenharmony_ci unsigned long args[6] = { }; 1162306a36Sopenharmony_ci struct pt_regs *regs; 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci if (!try_get_task_stack(target)) { 1462306a36Sopenharmony_ci /* Task has no stack, so the task isn't in a syscall. */ 1562306a36Sopenharmony_ci memset(info, 0, sizeof(*info)); 1662306a36Sopenharmony_ci info->data.nr = -1; 1762306a36Sopenharmony_ci return 0; 1862306a36Sopenharmony_ci } 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci regs = task_pt_regs(target); 2162306a36Sopenharmony_ci if (unlikely(!regs)) { 2262306a36Sopenharmony_ci put_task_stack(target); 2362306a36Sopenharmony_ci return -EAGAIN; 2462306a36Sopenharmony_ci } 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci info->sp = user_stack_pointer(regs); 2762306a36Sopenharmony_ci info->data.instruction_pointer = instruction_pointer(regs); 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci info->data.nr = syscall_get_nr(target, regs); 3062306a36Sopenharmony_ci if (info->data.nr != -1L) 3162306a36Sopenharmony_ci syscall_get_arguments(target, regs, args); 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci info->data.args[0] = args[0]; 3462306a36Sopenharmony_ci info->data.args[1] = args[1]; 3562306a36Sopenharmony_ci info->data.args[2] = args[2]; 3662306a36Sopenharmony_ci info->data.args[3] = args[3]; 3762306a36Sopenharmony_ci info->data.args[4] = args[4]; 3862306a36Sopenharmony_ci info->data.args[5] = args[5]; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci put_task_stack(target); 4162306a36Sopenharmony_ci return 0; 4262306a36Sopenharmony_ci} 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci/** 4562306a36Sopenharmony_ci * task_current_syscall - Discover what a blocked task is doing. 4662306a36Sopenharmony_ci * @target: thread to examine 4762306a36Sopenharmony_ci * @info: structure with the following fields: 4862306a36Sopenharmony_ci * .sp - filled with user stack pointer 4962306a36Sopenharmony_ci * .data.nr - filled with system call number or -1 5062306a36Sopenharmony_ci * .data.args - filled with @maxargs system call arguments 5162306a36Sopenharmony_ci * .data.instruction_pointer - filled with user PC 5262306a36Sopenharmony_ci * 5362306a36Sopenharmony_ci * If @target is blocked in a system call, returns zero with @info.data.nr 5462306a36Sopenharmony_ci * set to the call's number and @info.data.args filled in with its 5562306a36Sopenharmony_ci * arguments. Registers not used for system call arguments may not be available 5662306a36Sopenharmony_ci * and it is not kosher to use &struct user_regset calls while the system 5762306a36Sopenharmony_ci * call is still in progress. Note we may get this result if @target 5862306a36Sopenharmony_ci * has finished its system call but not yet returned to user mode, such 5962306a36Sopenharmony_ci * as when it's stopped for signal handling or syscall exit tracing. 6062306a36Sopenharmony_ci * 6162306a36Sopenharmony_ci * If @target is blocked in the kernel during a fault or exception, 6262306a36Sopenharmony_ci * returns zero with *@info.data.nr set to -1 and does not fill in 6362306a36Sopenharmony_ci * @info.data.args. If so, it's now safe to examine @target using 6462306a36Sopenharmony_ci * &struct user_regset get() calls as long as we're sure @target won't return 6562306a36Sopenharmony_ci * to user mode. 6662306a36Sopenharmony_ci * 6762306a36Sopenharmony_ci * Returns -%EAGAIN if @target does not remain blocked. 6862306a36Sopenharmony_ci */ 6962306a36Sopenharmony_ciint task_current_syscall(struct task_struct *target, struct syscall_info *info) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci unsigned long ncsw; 7262306a36Sopenharmony_ci unsigned int state; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci if (target == current) 7562306a36Sopenharmony_ci return collect_syscall(target, info); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci state = READ_ONCE(target->__state); 7862306a36Sopenharmony_ci if (unlikely(!state)) 7962306a36Sopenharmony_ci return -EAGAIN; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci ncsw = wait_task_inactive(target, state); 8262306a36Sopenharmony_ci if (unlikely(!ncsw) || 8362306a36Sopenharmony_ci unlikely(collect_syscall(target, info)) || 8462306a36Sopenharmony_ci unlikely(wait_task_inactive(target, state) != ncsw)) 8562306a36Sopenharmony_ci return -EAGAIN; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci return 0; 8862306a36Sopenharmony_ci} 89