ptrace_get_syscall_info_entry(child, regs, info);
        info->seccomp.ret_data = child->ptrace_message;
 
-       /* ret_data is the last field in struct ptrace_syscall_info.seccomp */
+       /*
+        * ret_data is the last non-reserved field
+        * in struct ptrace_syscall_info.seccomp
+        */
        return offsetofend(struct ptrace_syscall_info, seccomp.ret_data);
 }
 
        write_size = min(actual_size, user_size);
        return copy_to_user(datavp, &info, write_size) ? -EFAULT : actual_size;
 }
+
+static int
+ptrace_set_syscall_info_entry(struct task_struct *child, struct pt_regs *regs,
+                             struct ptrace_syscall_info *info)
+{
+       unsigned long args[ARRAY_SIZE(info->entry.args)];
+       int nr = info->entry.nr;
+       int i;
+
+       /*
+        * Check that the syscall number specified in info->entry.nr
+        * is either a value of type "int" or a sign-extended value
+        * of type "int".
+        */
+       if (nr != info->entry.nr)
+               return -ERANGE;
+
+       for (i = 0; i < ARRAY_SIZE(args); i++) {
+               args[i] = info->entry.args[i];
+               /*
+                * Check that the syscall argument specified in
+                * info->entry.args[i] is either a value of type
+                * "unsigned long" or a sign-extended value of type "long".
+                */
+               if (args[i] != info->entry.args[i])
+                       return -ERANGE;
+       }
+
+       syscall_set_nr(child, regs, nr);
+       /*
+        * If the syscall number is set to -1, setting syscall arguments is not
+        * just pointless, it would also clobber the syscall return value on
+        * those architectures that share the same register both for the first
+        * argument of syscall and its return value.
+        */
+       if (nr != -1)
+               syscall_set_arguments(child, regs, args);
+
+       return 0;
+}
+
+static int
+ptrace_set_syscall_info_seccomp(struct task_struct *child, struct pt_regs *regs,
+                               struct ptrace_syscall_info *info)
+{
+       /*
+        * info->entry is currently a subset of info->seccomp,
+        * info->seccomp.ret_data is currently ignored.
+        */
+       return ptrace_set_syscall_info_entry(child, regs, info);
+}
+
+static int
+ptrace_set_syscall_info_exit(struct task_struct *child, struct pt_regs *regs,
+                            struct ptrace_syscall_info *info)
+{
+       long rval = info->exit.rval;
+
+       /*
+        * Check that the return value specified in info->exit.rval
+        * is either a value of type "long" or a sign-extended value
+        * of type "long".
+        */
+       if (rval != info->exit.rval)
+               return -ERANGE;
+
+       if (info->exit.is_error)
+               syscall_set_return_value(child, regs, rval, 0);
+       else
+               syscall_set_return_value(child, regs, 0, rval);
+
+       return 0;
+}
+
+static int
+ptrace_set_syscall_info(struct task_struct *child, unsigned long user_size,
+                       const void __user *datavp)
+{
+       struct pt_regs *regs = task_pt_regs(child);
+       struct ptrace_syscall_info info;
+
+       if (user_size < sizeof(info))
+               return -EINVAL;
+
+       /*
+        * The compatibility is tracked by info.op and info.flags: if user-space
+        * does not instruct us to use unknown extra bits from future versions
+        * of ptrace_syscall_info, we are not going to read them either.
+        */
+       if (copy_from_user(&info, datavp, sizeof(info)))
+               return -EFAULT;
+
+       /* Reserved for future use. */
+       if (info.flags || info.reserved)
+               return -EINVAL;
+
+       /* Changing the type of the system call stop is not supported yet. */
+       if (ptrace_get_syscall_info_op(child) != info.op)
+               return -EINVAL;
+
+       switch (info.op) {
+       case PTRACE_SYSCALL_INFO_ENTRY:
+               return ptrace_set_syscall_info_entry(child, regs, &info);
+       case PTRACE_SYSCALL_INFO_EXIT:
+               return ptrace_set_syscall_info_exit(child, regs, &info);
+       case PTRACE_SYSCALL_INFO_SECCOMP:
+               return ptrace_set_syscall_info_seccomp(child, regs, &info);
+       default:
+               /* Other types of system call stops are not supported yet. */
+               return -EINVAL;
+       }
+}
 #endif /* CONFIG_HAVE_ARCH_TRACEHOOK */
 
 int ptrace_request(struct task_struct *child, long request,
        case PTRACE_GET_SYSCALL_INFO:
                ret = ptrace_get_syscall_info(child, addr, datavp);
                break;
+
+       case PTRACE_SET_SYSCALL_INFO:
+               ret = ptrace_set_syscall_info(child, addr, datavp);
+               break;
 #endif
 
        case PTRACE_SECCOMP_GET_FILTER: