summaryrefslogtreecommitdiffstats
path: root/kernel/ptrace.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/ptrace.c')
-rw-r--r--kernel/ptrace.c101
1 files changed, 100 insertions, 1 deletions
diff --git a/kernel/ptrace.c b/kernel/ptrace.c
index 83a531cea2f3..cb9ddcc08119 100644
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -32,6 +32,8 @@
32#include <linux/compat.h> 32#include <linux/compat.h>
33#include <linux/sched/signal.h> 33#include <linux/sched/signal.h>
34 34
35#include <asm/syscall.h> /* for syscall_get_* */
36
35/* 37/*
36 * Access another process' address space via ptrace. 38 * Access another process' address space via ptrace.
37 * Source/target buffer must be kernel space, 39 * Source/target buffer must be kernel space,
@@ -897,7 +899,100 @@ static int ptrace_regset(struct task_struct *task, int req, unsigned int type,
897 * to ensure no machine forgets it. 899 * to ensure no machine forgets it.
898 */ 900 */
899EXPORT_SYMBOL_GPL(task_user_regset_view); 901EXPORT_SYMBOL_GPL(task_user_regset_view);
900#endif 902
903static unsigned long
904ptrace_get_syscall_info_entry(struct task_struct *child, struct pt_regs *regs,
905 struct ptrace_syscall_info *info)
906{
907 unsigned long args[ARRAY_SIZE(info->entry.args)];
908 int i;
909
910 info->op = PTRACE_SYSCALL_INFO_ENTRY;
911 info->entry.nr = syscall_get_nr(child, regs);
912 syscall_get_arguments(child, regs, args);
913 for (i = 0; i < ARRAY_SIZE(args); i++)
914 info->entry.args[i] = args[i];
915
916 /* args is the last field in struct ptrace_syscall_info.entry */
917 return offsetofend(struct ptrace_syscall_info, entry.args);
918}
919
920static unsigned long
921ptrace_get_syscall_info_seccomp(struct task_struct *child, struct pt_regs *regs,
922 struct ptrace_syscall_info *info)
923{
924 /*
925 * As struct ptrace_syscall_info.entry is currently a subset
926 * of struct ptrace_syscall_info.seccomp, it makes sense to
927 * initialize that subset using ptrace_get_syscall_info_entry().
928 * This can be reconsidered in the future if these structures
929 * diverge significantly enough.
930 */
931 ptrace_get_syscall_info_entry(child, regs, info);
932 info->op = PTRACE_SYSCALL_INFO_SECCOMP;
933 info->seccomp.ret_data = child->ptrace_message;
934
935 /* ret_data is the last field in struct ptrace_syscall_info.seccomp */
936 return offsetofend(struct ptrace_syscall_info, seccomp.ret_data);
937}
938
939static unsigned long
940ptrace_get_syscall_info_exit(struct task_struct *child, struct pt_regs *regs,
941 struct ptrace_syscall_info *info)
942{
943 info->op = PTRACE_SYSCALL_INFO_EXIT;
944 info->exit.rval = syscall_get_error(child, regs);
945 info->exit.is_error = !!info->exit.rval;
946 if (!info->exit.is_error)
947 info->exit.rval = syscall_get_return_value(child, regs);
948
949 /* is_error is the last field in struct ptrace_syscall_info.exit */
950 return offsetofend(struct ptrace_syscall_info, exit.is_error);
951}
952
953static int
954ptrace_get_syscall_info(struct task_struct *child, unsigned long user_size,
955 void __user *datavp)
956{
957 struct pt_regs *regs = task_pt_regs(child);
958 struct ptrace_syscall_info info = {
959 .op = PTRACE_SYSCALL_INFO_NONE,
960 .arch = syscall_get_arch(child),
961 .instruction_pointer = instruction_pointer(regs),
962 .stack_pointer = user_stack_pointer(regs),
963 };
964 unsigned long actual_size = offsetof(struct ptrace_syscall_info, entry);
965 unsigned long write_size;
966
967 /*
968 * This does not need lock_task_sighand() to access
969 * child->last_siginfo because ptrace_freeze_traced()
970 * called earlier by ptrace_check_attach() ensures that
971 * the tracee cannot go away and clear its last_siginfo.
972 */
973 switch (child->last_siginfo ? child->last_siginfo->si_code : 0) {
974 case SIGTRAP | 0x80:
975 switch (child->ptrace_message) {
976 case PTRACE_EVENTMSG_SYSCALL_ENTRY:
977 actual_size = ptrace_get_syscall_info_entry(child, regs,
978 &info);
979 break;
980 case PTRACE_EVENTMSG_SYSCALL_EXIT:
981 actual_size = ptrace_get_syscall_info_exit(child, regs,
982 &info);
983 break;
984 }
985 break;
986 case SIGTRAP | (PTRACE_EVENT_SECCOMP << 8):
987 actual_size = ptrace_get_syscall_info_seccomp(child, regs,
988 &info);
989 break;
990 }
991
992 write_size = min(actual_size, user_size);
993 return copy_to_user(datavp, &info, write_size) ? -EFAULT : actual_size;
994}
995#endif /* CONFIG_HAVE_ARCH_TRACEHOOK */
901 996
902int ptrace_request(struct task_struct *child, long request, 997int ptrace_request(struct task_struct *child, long request,
903 unsigned long addr, unsigned long data) 998 unsigned long addr, unsigned long data)
@@ -1114,6 +1209,10 @@ int ptrace_request(struct task_struct *child, long request,
1114 ret = __put_user(kiov.iov_len, &uiov->iov_len); 1209 ret = __put_user(kiov.iov_len, &uiov->iov_len);
1115 break; 1210 break;
1116 } 1211 }
1212
1213 case PTRACE_GET_SYSCALL_INFO:
1214 ret = ptrace_get_syscall_info(child, addr, datavp);
1215 break;
1117#endif 1216#endif
1118 1217
1119 case PTRACE_SECCOMP_GET_FILTER: 1218 case PTRACE_SECCOMP_GET_FILTER: