|
@@ -62,6 +62,7 @@
|
|
#include <linux/kcov.h>
|
|
#include <linux/kcov.h>
|
|
#include <linux/random.h>
|
|
#include <linux/random.h>
|
|
#include <linux/rcuwait.h>
|
|
#include <linux/rcuwait.h>
|
|
|
|
+#include <linux/compat.h>
|
|
|
|
|
|
#include <linux/uaccess.h>
|
|
#include <linux/uaccess.h>
|
|
#include <asm/unistd.h>
|
|
#include <asm/unistd.h>
|
|
@@ -995,14 +996,21 @@ SYSCALL_DEFINE1(exit_group, int, error_code)
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+struct waitid_info {
|
|
|
|
+ pid_t pid;
|
|
|
|
+ uid_t uid;
|
|
|
|
+ int status;
|
|
|
|
+ int cause;
|
|
|
|
+};
|
|
|
|
+
|
|
struct wait_opts {
|
|
struct wait_opts {
|
|
enum pid_type wo_type;
|
|
enum pid_type wo_type;
|
|
int wo_flags;
|
|
int wo_flags;
|
|
struct pid *wo_pid;
|
|
struct pid *wo_pid;
|
|
|
|
|
|
- struct siginfo __user *wo_info;
|
|
|
|
- int __user *wo_stat;
|
|
|
|
- struct rusage __user *wo_rusage;
|
|
|
|
|
|
+ struct waitid_info *wo_info;
|
|
|
|
+ int wo_stat;
|
|
|
|
+ struct rusage *wo_rusage;
|
|
|
|
|
|
wait_queue_t child_wait;
|
|
wait_queue_t child_wait;
|
|
int notask_error;
|
|
int notask_error;
|
|
@@ -1049,34 +1057,6 @@ eligible_child(struct wait_opts *wo, bool ptrace, struct task_struct *p)
|
|
return 1;
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
|
|
-static int wait_noreap_copyout(struct wait_opts *wo, struct task_struct *p,
|
|
|
|
- pid_t pid, uid_t uid, int why, int status)
|
|
|
|
-{
|
|
|
|
- struct siginfo __user *infop;
|
|
|
|
- int retval = wo->wo_rusage
|
|
|
|
- ? getrusage(p, RUSAGE_BOTH, wo->wo_rusage) : 0;
|
|
|
|
-
|
|
|
|
- put_task_struct(p);
|
|
|
|
- infop = wo->wo_info;
|
|
|
|
- if (infop) {
|
|
|
|
- if (!retval)
|
|
|
|
- retval = put_user(SIGCHLD, &infop->si_signo);
|
|
|
|
- if (!retval)
|
|
|
|
- retval = put_user(0, &infop->si_errno);
|
|
|
|
- if (!retval)
|
|
|
|
- retval = put_user((short)why, &infop->si_code);
|
|
|
|
- if (!retval)
|
|
|
|
- retval = put_user(pid, &infop->si_pid);
|
|
|
|
- if (!retval)
|
|
|
|
- retval = put_user(uid, &infop->si_uid);
|
|
|
|
- if (!retval)
|
|
|
|
- retval = put_user(status, &infop->si_status);
|
|
|
|
- }
|
|
|
|
- if (!retval)
|
|
|
|
- retval = pid;
|
|
|
|
- return retval;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
/*
|
|
/*
|
|
* Handle sys_wait4 work for one task in state EXIT_ZOMBIE. We hold
|
|
* Handle sys_wait4 work for one task in state EXIT_ZOMBIE. We hold
|
|
* read_lock(&tasklist_lock) on entry. If we return zero, we still hold
|
|
* read_lock(&tasklist_lock) on entry. If we return zero, we still hold
|
|
@@ -1085,30 +1065,23 @@ static int wait_noreap_copyout(struct wait_opts *wo, struct task_struct *p,
|
|
*/
|
|
*/
|
|
static int wait_task_zombie(struct wait_opts *wo, struct task_struct *p)
|
|
static int wait_task_zombie(struct wait_opts *wo, struct task_struct *p)
|
|
{
|
|
{
|
|
- int state, retval, status;
|
|
|
|
|
|
+ int state, status;
|
|
pid_t pid = task_pid_vnr(p);
|
|
pid_t pid = task_pid_vnr(p);
|
|
uid_t uid = from_kuid_munged(current_user_ns(), task_uid(p));
|
|
uid_t uid = from_kuid_munged(current_user_ns(), task_uid(p));
|
|
- struct siginfo __user *infop;
|
|
|
|
|
|
+ struct waitid_info *infop;
|
|
|
|
|
|
if (!likely(wo->wo_flags & WEXITED))
|
|
if (!likely(wo->wo_flags & WEXITED))
|
|
return 0;
|
|
return 0;
|
|
|
|
|
|
if (unlikely(wo->wo_flags & WNOWAIT)) {
|
|
if (unlikely(wo->wo_flags & WNOWAIT)) {
|
|
- int exit_code = p->exit_code;
|
|
|
|
- int why;
|
|
|
|
-
|
|
|
|
|
|
+ status = p->exit_code;
|
|
get_task_struct(p);
|
|
get_task_struct(p);
|
|
read_unlock(&tasklist_lock);
|
|
read_unlock(&tasklist_lock);
|
|
sched_annotate_sleep();
|
|
sched_annotate_sleep();
|
|
-
|
|
|
|
- if ((exit_code & 0x7f) == 0) {
|
|
|
|
- why = CLD_EXITED;
|
|
|
|
- status = exit_code >> 8;
|
|
|
|
- } else {
|
|
|
|
- why = (exit_code & 0x80) ? CLD_DUMPED : CLD_KILLED;
|
|
|
|
- status = exit_code & 0x7f;
|
|
|
|
- }
|
|
|
|
- return wait_noreap_copyout(wo, p, pid, uid, why, status);
|
|
|
|
|
|
+ if (wo->wo_rusage)
|
|
|
|
+ getrusage(p, RUSAGE_BOTH, wo->wo_rusage);
|
|
|
|
+ put_task_struct(p);
|
|
|
|
+ goto out_info;
|
|
}
|
|
}
|
|
/*
|
|
/*
|
|
* Move the task's state to DEAD/TRACE, only one thread can do this.
|
|
* Move the task's state to DEAD/TRACE, only one thread can do this.
|
|
@@ -1181,38 +1154,11 @@ static int wait_task_zombie(struct wait_opts *wo, struct task_struct *p)
|
|
spin_unlock_irq(¤t->sighand->siglock);
|
|
spin_unlock_irq(¤t->sighand->siglock);
|
|
}
|
|
}
|
|
|
|
|
|
- retval = wo->wo_rusage
|
|
|
|
- ? getrusage(p, RUSAGE_BOTH, wo->wo_rusage) : 0;
|
|
|
|
|
|
+ if (wo->wo_rusage)
|
|
|
|
+ getrusage(p, RUSAGE_BOTH, wo->wo_rusage);
|
|
status = (p->signal->flags & SIGNAL_GROUP_EXIT)
|
|
status = (p->signal->flags & SIGNAL_GROUP_EXIT)
|
|
? p->signal->group_exit_code : p->exit_code;
|
|
? p->signal->group_exit_code : p->exit_code;
|
|
- if (!retval && wo->wo_stat)
|
|
|
|
- retval = put_user(status, wo->wo_stat);
|
|
|
|
-
|
|
|
|
- infop = wo->wo_info;
|
|
|
|
- if (!retval && infop)
|
|
|
|
- retval = put_user(SIGCHLD, &infop->si_signo);
|
|
|
|
- if (!retval && infop)
|
|
|
|
- retval = put_user(0, &infop->si_errno);
|
|
|
|
- if (!retval && infop) {
|
|
|
|
- int why;
|
|
|
|
-
|
|
|
|
- if ((status & 0x7f) == 0) {
|
|
|
|
- why = CLD_EXITED;
|
|
|
|
- status >>= 8;
|
|
|
|
- } else {
|
|
|
|
- why = (status & 0x80) ? CLD_DUMPED : CLD_KILLED;
|
|
|
|
- status &= 0x7f;
|
|
|
|
- }
|
|
|
|
- retval = put_user((short)why, &infop->si_code);
|
|
|
|
- if (!retval)
|
|
|
|
- retval = put_user(status, &infop->si_status);
|
|
|
|
- }
|
|
|
|
- if (!retval && infop)
|
|
|
|
- retval = put_user(pid, &infop->si_pid);
|
|
|
|
- if (!retval && infop)
|
|
|
|
- retval = put_user(uid, &infop->si_uid);
|
|
|
|
- if (!retval)
|
|
|
|
- retval = pid;
|
|
|
|
|
|
+ wo->wo_stat = status;
|
|
|
|
|
|
if (state == EXIT_TRACE) {
|
|
if (state == EXIT_TRACE) {
|
|
write_lock_irq(&tasklist_lock);
|
|
write_lock_irq(&tasklist_lock);
|
|
@@ -1229,7 +1175,21 @@ static int wait_task_zombie(struct wait_opts *wo, struct task_struct *p)
|
|
if (state == EXIT_DEAD)
|
|
if (state == EXIT_DEAD)
|
|
release_task(p);
|
|
release_task(p);
|
|
|
|
|
|
- return retval;
|
|
|
|
|
|
+out_info:
|
|
|
|
+ infop = wo->wo_info;
|
|
|
|
+ if (infop) {
|
|
|
|
+ if ((status & 0x7f) == 0) {
|
|
|
|
+ infop->cause = CLD_EXITED;
|
|
|
|
+ infop->status = status >> 8;
|
|
|
|
+ } else {
|
|
|
|
+ infop->cause = (status & 0x80) ? CLD_DUMPED : CLD_KILLED;
|
|
|
|
+ infop->status = status & 0x7f;
|
|
|
|
+ }
|
|
|
|
+ infop->pid = pid;
|
|
|
|
+ infop->uid = uid;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return pid;
|
|
}
|
|
}
|
|
|
|
|
|
static int *task_stopped_code(struct task_struct *p, bool ptrace)
|
|
static int *task_stopped_code(struct task_struct *p, bool ptrace)
|
|
@@ -1265,8 +1225,8 @@ static int *task_stopped_code(struct task_struct *p, bool ptrace)
|
|
static int wait_task_stopped(struct wait_opts *wo,
|
|
static int wait_task_stopped(struct wait_opts *wo,
|
|
int ptrace, struct task_struct *p)
|
|
int ptrace, struct task_struct *p)
|
|
{
|
|
{
|
|
- struct siginfo __user *infop;
|
|
|
|
- int retval, exit_code, *p_code, why;
|
|
|
|
|
|
+ struct waitid_info *infop;
|
|
|
|
+ int exit_code, *p_code, why;
|
|
uid_t uid = 0; /* unneeded, required by compiler */
|
|
uid_t uid = 0; /* unneeded, required by compiler */
|
|
pid_t pid;
|
|
pid_t pid;
|
|
|
|
|
|
@@ -1311,34 +1271,21 @@ unlock_sig:
|
|
why = ptrace ? CLD_TRAPPED : CLD_STOPPED;
|
|
why = ptrace ? CLD_TRAPPED : CLD_STOPPED;
|
|
read_unlock(&tasklist_lock);
|
|
read_unlock(&tasklist_lock);
|
|
sched_annotate_sleep();
|
|
sched_annotate_sleep();
|
|
|
|
+ if (wo->wo_rusage)
|
|
|
|
+ getrusage(p, RUSAGE_BOTH, wo->wo_rusage);
|
|
|
|
+ put_task_struct(p);
|
|
|
|
|
|
- if (unlikely(wo->wo_flags & WNOWAIT))
|
|
|
|
- return wait_noreap_copyout(wo, p, pid, uid, why, exit_code);
|
|
|
|
-
|
|
|
|
- retval = wo->wo_rusage
|
|
|
|
- ? getrusage(p, RUSAGE_BOTH, wo->wo_rusage) : 0;
|
|
|
|
- if (!retval && wo->wo_stat)
|
|
|
|
- retval = put_user((exit_code << 8) | 0x7f, wo->wo_stat);
|
|
|
|
|
|
+ if (likely(!(wo->wo_flags & WNOWAIT)))
|
|
|
|
+ wo->wo_stat = (exit_code << 8) | 0x7f;
|
|
|
|
|
|
infop = wo->wo_info;
|
|
infop = wo->wo_info;
|
|
- if (!retval && infop)
|
|
|
|
- retval = put_user(SIGCHLD, &infop->si_signo);
|
|
|
|
- if (!retval && infop)
|
|
|
|
- retval = put_user(0, &infop->si_errno);
|
|
|
|
- if (!retval && infop)
|
|
|
|
- retval = put_user((short)why, &infop->si_code);
|
|
|
|
- if (!retval && infop)
|
|
|
|
- retval = put_user(exit_code, &infop->si_status);
|
|
|
|
- if (!retval && infop)
|
|
|
|
- retval = put_user(pid, &infop->si_pid);
|
|
|
|
- if (!retval && infop)
|
|
|
|
- retval = put_user(uid, &infop->si_uid);
|
|
|
|
- if (!retval)
|
|
|
|
- retval = pid;
|
|
|
|
- put_task_struct(p);
|
|
|
|
-
|
|
|
|
- BUG_ON(!retval);
|
|
|
|
- return retval;
|
|
|
|
|
|
+ if (infop) {
|
|
|
|
+ infop->cause = why;
|
|
|
|
+ infop->status = exit_code;
|
|
|
|
+ infop->pid = pid;
|
|
|
|
+ infop->uid = uid;
|
|
|
|
+ }
|
|
|
|
+ return pid;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
/*
|
|
@@ -1349,7 +1296,7 @@ unlock_sig:
|
|
*/
|
|
*/
|
|
static int wait_task_continued(struct wait_opts *wo, struct task_struct *p)
|
|
static int wait_task_continued(struct wait_opts *wo, struct task_struct *p)
|
|
{
|
|
{
|
|
- int retval;
|
|
|
|
|
|
+ struct waitid_info *infop;
|
|
pid_t pid;
|
|
pid_t pid;
|
|
uid_t uid;
|
|
uid_t uid;
|
|
|
|
|
|
@@ -1374,22 +1321,20 @@ static int wait_task_continued(struct wait_opts *wo, struct task_struct *p)
|
|
get_task_struct(p);
|
|
get_task_struct(p);
|
|
read_unlock(&tasklist_lock);
|
|
read_unlock(&tasklist_lock);
|
|
sched_annotate_sleep();
|
|
sched_annotate_sleep();
|
|
|
|
+ if (wo->wo_rusage)
|
|
|
|
+ getrusage(p, RUSAGE_BOTH, wo->wo_rusage);
|
|
|
|
+ put_task_struct(p);
|
|
|
|
|
|
- if (!wo->wo_info) {
|
|
|
|
- retval = wo->wo_rusage
|
|
|
|
- ? getrusage(p, RUSAGE_BOTH, wo->wo_rusage) : 0;
|
|
|
|
- put_task_struct(p);
|
|
|
|
- if (!retval && wo->wo_stat)
|
|
|
|
- retval = put_user(0xffff, wo->wo_stat);
|
|
|
|
- if (!retval)
|
|
|
|
- retval = pid;
|
|
|
|
|
|
+ infop = wo->wo_info;
|
|
|
|
+ if (!infop) {
|
|
|
|
+ wo->wo_stat = 0xffff;
|
|
} else {
|
|
} else {
|
|
- retval = wait_noreap_copyout(wo, p, pid, uid,
|
|
|
|
- CLD_CONTINUED, SIGCONT);
|
|
|
|
- BUG_ON(retval == 0);
|
|
|
|
|
|
+ infop->cause = CLD_CONTINUED;
|
|
|
|
+ infop->pid = pid;
|
|
|
|
+ infop->uid = uid;
|
|
|
|
+ infop->status = SIGCONT;
|
|
}
|
|
}
|
|
-
|
|
|
|
- return retval;
|
|
|
|
|
|
+ return pid;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
/*
|
|
@@ -1617,8 +1562,8 @@ end:
|
|
return retval;
|
|
return retval;
|
|
}
|
|
}
|
|
|
|
|
|
-SYSCALL_DEFINE5(waitid, int, which, pid_t, upid, struct siginfo __user *,
|
|
|
|
- infop, int, options, struct rusage __user *, ru)
|
|
|
|
|
|
+static long kernel_waitid(int which, pid_t upid, struct waitid_info *infop,
|
|
|
|
+ int options, struct rusage *ru)
|
|
{
|
|
{
|
|
struct wait_opts wo;
|
|
struct wait_opts wo;
|
|
struct pid *pid = NULL;
|
|
struct pid *pid = NULL;
|
|
@@ -1656,38 +1601,46 @@ SYSCALL_DEFINE5(waitid, int, which, pid_t, upid, struct siginfo __user *,
|
|
wo.wo_pid = pid;
|
|
wo.wo_pid = pid;
|
|
wo.wo_flags = options;
|
|
wo.wo_flags = options;
|
|
wo.wo_info = infop;
|
|
wo.wo_info = infop;
|
|
- wo.wo_stat = NULL;
|
|
|
|
wo.wo_rusage = ru;
|
|
wo.wo_rusage = ru;
|
|
ret = do_wait(&wo);
|
|
ret = do_wait(&wo);
|
|
|
|
|
|
- if (ret > 0) {
|
|
|
|
|
|
+ if (ret > 0)
|
|
ret = 0;
|
|
ret = 0;
|
|
- } else if (infop) {
|
|
|
|
- /*
|
|
|
|
- * For a WNOHANG return, clear out all the fields
|
|
|
|
- * we would set so the user can easily tell the
|
|
|
|
- * difference.
|
|
|
|
- */
|
|
|
|
- if (!ret)
|
|
|
|
- ret = put_user(0, &infop->si_signo);
|
|
|
|
- if (!ret)
|
|
|
|
- ret = put_user(0, &infop->si_errno);
|
|
|
|
- if (!ret)
|
|
|
|
- ret = put_user(0, &infop->si_code);
|
|
|
|
- if (!ret)
|
|
|
|
- ret = put_user(0, &infop->si_pid);
|
|
|
|
- if (!ret)
|
|
|
|
- ret = put_user(0, &infop->si_uid);
|
|
|
|
- if (!ret)
|
|
|
|
- ret = put_user(0, &infop->si_status);
|
|
|
|
- }
|
|
|
|
|
|
|
|
put_pid(pid);
|
|
put_pid(pid);
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
-SYSCALL_DEFINE4(wait4, pid_t, upid, int __user *, stat_addr,
|
|
|
|
- int, options, struct rusage __user *, ru)
|
|
|
|
|
|
+SYSCALL_DEFINE5(waitid, int, which, pid_t, upid, struct siginfo __user *,
|
|
|
|
+ infop, int, options, struct rusage __user *, ru)
|
|
|
|
+{
|
|
|
|
+ struct rusage r;
|
|
|
|
+ struct waitid_info info = {.status = 0};
|
|
|
|
+ long err = kernel_waitid(which, upid, &info, options, ru ? &r : NULL);
|
|
|
|
+
|
|
|
|
+ if (!err) {
|
|
|
|
+ if (ru && copy_to_user(ru, &r, sizeof(struct rusage)))
|
|
|
|
+ return -EFAULT;
|
|
|
|
+ }
|
|
|
|
+ if (!infop)
|
|
|
|
+ return err;
|
|
|
|
+
|
|
|
|
+ user_access_begin();
|
|
|
|
+ unsafe_put_user(err ? 0 : SIGCHLD, &infop->si_signo, Efault);
|
|
|
|
+ unsafe_put_user(0, &infop->si_errno, Efault);
|
|
|
|
+ unsafe_put_user((short)info.cause, &infop->si_code, Efault);
|
|
|
|
+ unsafe_put_user(info.pid, &infop->si_pid, Efault);
|
|
|
|
+ unsafe_put_user(info.uid, &infop->si_uid, Efault);
|
|
|
|
+ unsafe_put_user(info.status, &infop->si_status, Efault);
|
|
|
|
+ user_access_end();
|
|
|
|
+ return err;
|
|
|
|
+Efault:
|
|
|
|
+ user_access_end();
|
|
|
|
+ return -EFAULT;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+long kernel_wait4(pid_t upid, int __user *stat_addr, int options,
|
|
|
|
+ struct rusage *ru)
|
|
{
|
|
{
|
|
struct wait_opts wo;
|
|
struct wait_opts wo;
|
|
struct pid *pid = NULL;
|
|
struct pid *pid = NULL;
|
|
@@ -1715,14 +1668,29 @@ SYSCALL_DEFINE4(wait4, pid_t, upid, int __user *, stat_addr,
|
|
wo.wo_pid = pid;
|
|
wo.wo_pid = pid;
|
|
wo.wo_flags = options | WEXITED;
|
|
wo.wo_flags = options | WEXITED;
|
|
wo.wo_info = NULL;
|
|
wo.wo_info = NULL;
|
|
- wo.wo_stat = stat_addr;
|
|
|
|
|
|
+ wo.wo_stat = 0;
|
|
wo.wo_rusage = ru;
|
|
wo.wo_rusage = ru;
|
|
ret = do_wait(&wo);
|
|
ret = do_wait(&wo);
|
|
put_pid(pid);
|
|
put_pid(pid);
|
|
|
|
+ if (ret > 0 && stat_addr && put_user(wo.wo_stat, stat_addr))
|
|
|
|
+ ret = -EFAULT;
|
|
|
|
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+SYSCALL_DEFINE4(wait4, pid_t, upid, int __user *, stat_addr,
|
|
|
|
+ int, options, struct rusage __user *, ru)
|
|
|
|
+{
|
|
|
|
+ struct rusage r;
|
|
|
|
+ long err = kernel_wait4(upid, stat_addr, options, ru ? &r : NULL);
|
|
|
|
+
|
|
|
|
+ if (err > 0) {
|
|
|
|
+ if (ru && copy_to_user(ru, &r, sizeof(struct rusage)))
|
|
|
|
+ return -EFAULT;
|
|
|
|
+ }
|
|
|
|
+ return err;
|
|
|
|
+}
|
|
|
|
+
|
|
#ifdef __ARCH_WANT_SYS_WAITPID
|
|
#ifdef __ARCH_WANT_SYS_WAITPID
|
|
|
|
|
|
/*
|
|
/*
|
|
@@ -1735,3 +1703,56 @@ SYSCALL_DEFINE3(waitpid, pid_t, pid, int __user *, stat_addr, int, options)
|
|
}
|
|
}
|
|
|
|
|
|
#endif
|
|
#endif
|
|
|
|
+
|
|
|
|
+#ifdef CONFIG_COMPAT
|
|
|
|
+COMPAT_SYSCALL_DEFINE4(wait4,
|
|
|
|
+ compat_pid_t, pid,
|
|
|
|
+ compat_uint_t __user *, stat_addr,
|
|
|
|
+ int, options,
|
|
|
|
+ struct compat_rusage __user *, ru)
|
|
|
|
+{
|
|
|
|
+ struct rusage r;
|
|
|
|
+ long err = kernel_wait4(pid, stat_addr, options, ru ? &r : NULL);
|
|
|
|
+ if (err > 0) {
|
|
|
|
+ if (ru && put_compat_rusage(&r, ru))
|
|
|
|
+ return -EFAULT;
|
|
|
|
+ }
|
|
|
|
+ return err;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+COMPAT_SYSCALL_DEFINE5(waitid,
|
|
|
|
+ int, which, compat_pid_t, pid,
|
|
|
|
+ struct compat_siginfo __user *, infop, int, options,
|
|
|
|
+ struct compat_rusage __user *, uru)
|
|
|
|
+{
|
|
|
|
+ struct rusage ru;
|
|
|
|
+ struct waitid_info info = {.status = 0};
|
|
|
|
+ long err = kernel_waitid(which, pid, &info, options, uru ? &ru : NULL);
|
|
|
|
+
|
|
|
|
+ if (!err && uru) {
|
|
|
|
+ /* kernel_waitid() overwrites everything in ru */
|
|
|
|
+ if (COMPAT_USE_64BIT_TIME)
|
|
|
|
+ err = copy_to_user(uru, &ru, sizeof(ru));
|
|
|
|
+ else
|
|
|
|
+ err = put_compat_rusage(&ru, uru);
|
|
|
|
+ if (err)
|
|
|
|
+ return -EFAULT;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!infop)
|
|
|
|
+ return err;
|
|
|
|
+
|
|
|
|
+ user_access_begin();
|
|
|
|
+ unsafe_put_user(err ? 0 : SIGCHLD, &infop->si_signo, Efault);
|
|
|
|
+ unsafe_put_user(0, &infop->si_errno, Efault);
|
|
|
|
+ unsafe_put_user((short)info.cause, &infop->si_code, Efault);
|
|
|
|
+ unsafe_put_user(info.pid, &infop->si_pid, Efault);
|
|
|
|
+ unsafe_put_user(info.uid, &infop->si_uid, Efault);
|
|
|
|
+ unsafe_put_user(info.status, &infop->si_status, Efault);
|
|
|
|
+ user_access_end();
|
|
|
|
+ return err;
|
|
|
|
+Efault:
|
|
|
|
+ user_access_end();
|
|
|
|
+ return -EFAULT;
|
|
|
|
+}
|
|
|
|
+#endif
|