diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2017-07-05 17:10:19 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2017-07-05 17:10:19 -0400 |
commit | 4be95131bf3bca97b6a7db9c6fb63db2cb94da06 (patch) | |
tree | c623b98ba6aa37299f5109cb8bd0e53be547c72d | |
parent | 3bad2f1c676581d01e7645eb03e9b27e28b0a92e (diff) | |
parent | 92ebce5ac55dba258c608248dddf59eca3f7f514 (diff) |
Merge branch 'work.sys_wait' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
Pull wait syscall updates from Al Viro:
"Consolidating sys_wait* and compat counterparts.
Gets rid of set_fs()/double-copy mess, simplifies the whole thing
(lifting the copyouts to the syscalls means less headache in the part
that does actual work - fewer failure exits, to start with), gets rid
of the overhead of field-by-field __put_user()"
* 'work.sys_wait' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs:
osf_wait4: switch to kernel_wait4()
waitid(): switch copyout of siginfo to unsafe_put_user()
wait_task_zombie: consolidate info logics
kill wait_noreap_copyout()
lift getrusage() from wait_noreap_copyout()
waitid(2): leave copyout of siginfo to syscall itself
kernel_wait4()/kernel_waitid(): delay copying status to userland
wait4(2)/waitid(2): separate copying rusage to userland
move compat wait4 and waitid next to native variants
-rw-r--r-- | arch/alpha/kernel/osf_sys.c | 53 | ||||
-rw-r--r-- | include/linux/resource.h | 2 | ||||
-rw-r--r-- | include/linux/sched/task.h | 2 | ||||
-rw-r--r-- | kernel/compat.c | 66 | ||||
-rw-r--r-- | kernel/exit.c | 307 | ||||
-rw-r--r-- | kernel/sys.c | 16 |
6 files changed, 187 insertions, 259 deletions
diff --git a/arch/alpha/kernel/osf_sys.c b/arch/alpha/kernel/osf_sys.c index ce93124a850b..b23d6fbbb225 100644 --- a/arch/alpha/kernel/osf_sys.c +++ b/arch/alpha/kernel/osf_sys.c | |||
@@ -1183,48 +1183,23 @@ SYSCALL_DEFINE2(osf_getrusage, int, who, struct rusage32 __user *, ru) | |||
1183 | SYSCALL_DEFINE4(osf_wait4, pid_t, pid, int __user *, ustatus, int, options, | 1183 | SYSCALL_DEFINE4(osf_wait4, pid_t, pid, int __user *, ustatus, int, options, |
1184 | struct rusage32 __user *, ur) | 1184 | struct rusage32 __user *, ur) |
1185 | { | 1185 | { |
1186 | struct rusage r; | ||
1187 | long ret, err; | ||
1188 | unsigned int status = 0; | 1186 | unsigned int status = 0; |
1189 | mm_segment_t old_fs; | 1187 | struct rusage r; |
1190 | 1188 | long err = kernel_wait4(pid, &status, options, &r); | |
1189 | if (err <= 0) | ||
1190 | return err; | ||
1191 | if (put_user(status, ustatus)) | ||
1192 | return -EFAULT; | ||
1191 | if (!ur) | 1193 | if (!ur) |
1192 | return sys_wait4(pid, ustatus, options, NULL); | 1194 | return err; |
1193 | 1195 | if (put_tv32(&ur->ru_utime, &r.ru_utime)) | |
1194 | old_fs = get_fs(); | ||
1195 | |||
1196 | set_fs (KERNEL_DS); | ||
1197 | ret = sys_wait4(pid, (unsigned int __user *) &status, options, | ||
1198 | (struct rusage __user *) &r); | ||
1199 | set_fs (old_fs); | ||
1200 | |||
1201 | if (!access_ok(VERIFY_WRITE, ur, sizeof(*ur))) | ||
1202 | return -EFAULT; | 1196 | return -EFAULT; |
1203 | 1197 | if (put_tv32(&ur->ru_stime, &r.ru_stime)) | |
1204 | err = put_user(status, ustatus); | 1198 | return -EFAULT; |
1205 | if (ret < 0) | 1199 | if (copy_to_user(&ur->ru_maxrss, &r.ru_maxrss, |
1206 | return err ? err : ret; | 1200 | sizeof(struct rusage32) - offsetof(struct rusage32, ru_maxrss))) |
1207 | 1201 | return -EFAULT; | |
1208 | err |= __put_user(r.ru_utime.tv_sec, &ur->ru_utime.tv_sec); | 1202 | return err; |
1209 | err |= __put_user(r.ru_utime.tv_usec, &ur->ru_utime.tv_usec); | ||
1210 | err |= __put_user(r.ru_stime.tv_sec, &ur->ru_stime.tv_sec); | ||
1211 | err |= __put_user(r.ru_stime.tv_usec, &ur->ru_stime.tv_usec); | ||
1212 | err |= __put_user(r.ru_maxrss, &ur->ru_maxrss); | ||
1213 | err |= __put_user(r.ru_ixrss, &ur->ru_ixrss); | ||
1214 | err |= __put_user(r.ru_idrss, &ur->ru_idrss); | ||
1215 | err |= __put_user(r.ru_isrss, &ur->ru_isrss); | ||
1216 | err |= __put_user(r.ru_minflt, &ur->ru_minflt); | ||
1217 | err |= __put_user(r.ru_majflt, &ur->ru_majflt); | ||
1218 | err |= __put_user(r.ru_nswap, &ur->ru_nswap); | ||
1219 | err |= __put_user(r.ru_inblock, &ur->ru_inblock); | ||
1220 | err |= __put_user(r.ru_oublock, &ur->ru_oublock); | ||
1221 | err |= __put_user(r.ru_msgsnd, &ur->ru_msgsnd); | ||
1222 | err |= __put_user(r.ru_msgrcv, &ur->ru_msgrcv); | ||
1223 | err |= __put_user(r.ru_nsignals, &ur->ru_nsignals); | ||
1224 | err |= __put_user(r.ru_nvcsw, &ur->ru_nvcsw); | ||
1225 | err |= __put_user(r.ru_nivcsw, &ur->ru_nivcsw); | ||
1226 | |||
1227 | return err ? err : ret; | ||
1228 | } | 1203 | } |
1229 | 1204 | ||
1230 | /* | 1205 | /* |
diff --git a/include/linux/resource.h b/include/linux/resource.h index 5bc3116e649c..277afdad6589 100644 --- a/include/linux/resource.h +++ b/include/linux/resource.h | |||
@@ -6,7 +6,7 @@ | |||
6 | 6 | ||
7 | struct task_struct; | 7 | struct task_struct; |
8 | 8 | ||
9 | int getrusage(struct task_struct *p, int who, struct rusage __user *ru); | 9 | void getrusage(struct task_struct *p, int who, struct rusage *ru); |
10 | int do_prlimit(struct task_struct *tsk, unsigned int resource, | 10 | int do_prlimit(struct task_struct *tsk, unsigned int resource, |
11 | struct rlimit *new_rlim, struct rlimit *old_rlim); | 11 | struct rlimit *new_rlim, struct rlimit *old_rlim); |
12 | 12 | ||
diff --git a/include/linux/sched/task.h b/include/linux/sched/task.h index f0f065c5afcf..c97e5f096927 100644 --- a/include/linux/sched/task.h +++ b/include/linux/sched/task.h | |||
@@ -9,6 +9,7 @@ | |||
9 | #include <linux/sched.h> | 9 | #include <linux/sched.h> |
10 | 10 | ||
11 | struct task_struct; | 11 | struct task_struct; |
12 | struct rusage; | ||
12 | union thread_union; | 13 | union thread_union; |
13 | 14 | ||
14 | /* | 15 | /* |
@@ -74,6 +75,7 @@ extern long _do_fork(unsigned long, unsigned long, unsigned long, int __user *, | |||
74 | extern long do_fork(unsigned long, unsigned long, unsigned long, int __user *, int __user *); | 75 | extern long do_fork(unsigned long, unsigned long, unsigned long, int __user *, int __user *); |
75 | struct task_struct *fork_idle(int); | 76 | struct task_struct *fork_idle(int); |
76 | extern pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags); | 77 | extern pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags); |
78 | extern long kernel_wait4(pid_t, int *, int, struct rusage *); | ||
77 | 79 | ||
78 | extern void free_task(struct task_struct *tsk); | 80 | extern void free_task(struct task_struct *tsk); |
79 | 81 | ||
diff --git a/kernel/compat.c b/kernel/compat.c index ebd8bdc3fd68..9ce1d4876e60 100644 --- a/kernel/compat.c +++ b/kernel/compat.c | |||
@@ -396,72 +396,6 @@ int put_compat_rusage(const struct rusage *r, struct compat_rusage __user *ru) | |||
396 | return 0; | 396 | return 0; |
397 | } | 397 | } |
398 | 398 | ||
399 | COMPAT_SYSCALL_DEFINE4(wait4, | ||
400 | compat_pid_t, pid, | ||
401 | compat_uint_t __user *, stat_addr, | ||
402 | int, options, | ||
403 | struct compat_rusage __user *, ru) | ||
404 | { | ||
405 | if (!ru) { | ||
406 | return sys_wait4(pid, stat_addr, options, NULL); | ||
407 | } else { | ||
408 | struct rusage r; | ||
409 | int ret; | ||
410 | unsigned int status; | ||
411 | mm_segment_t old_fs = get_fs(); | ||
412 | |||
413 | set_fs (KERNEL_DS); | ||
414 | ret = sys_wait4(pid, | ||
415 | (stat_addr ? | ||
416 | (unsigned int __user *) &status : NULL), | ||
417 | options, (struct rusage __user *) &r); | ||
418 | set_fs (old_fs); | ||
419 | |||
420 | if (ret > 0) { | ||
421 | if (put_compat_rusage(&r, ru)) | ||
422 | return -EFAULT; | ||
423 | if (stat_addr && put_user(status, stat_addr)) | ||
424 | return -EFAULT; | ||
425 | } | ||
426 | return ret; | ||
427 | } | ||
428 | } | ||
429 | |||
430 | COMPAT_SYSCALL_DEFINE5(waitid, | ||
431 | int, which, compat_pid_t, pid, | ||
432 | struct compat_siginfo __user *, uinfo, int, options, | ||
433 | struct compat_rusage __user *, uru) | ||
434 | { | ||
435 | siginfo_t info; | ||
436 | struct rusage ru; | ||
437 | long ret; | ||
438 | mm_segment_t old_fs = get_fs(); | ||
439 | |||
440 | memset(&info, 0, sizeof(info)); | ||
441 | |||
442 | set_fs(KERNEL_DS); | ||
443 | ret = sys_waitid(which, pid, (siginfo_t __user *)&info, options, | ||
444 | uru ? (struct rusage __user *)&ru : NULL); | ||
445 | set_fs(old_fs); | ||
446 | |||
447 | if ((ret < 0) || (info.si_signo == 0)) | ||
448 | return ret; | ||
449 | |||
450 | if (uru) { | ||
451 | /* sys_waitid() overwrites everything in ru */ | ||
452 | if (COMPAT_USE_64BIT_TIME) | ||
453 | ret = copy_to_user(uru, &ru, sizeof(ru)); | ||
454 | else | ||
455 | ret = put_compat_rusage(&ru, uru); | ||
456 | if (ret) | ||
457 | return -EFAULT; | ||
458 | } | ||
459 | |||
460 | BUG_ON(info.si_code & __SI_MASK); | ||
461 | info.si_code |= __SI_CHLD; | ||
462 | return copy_siginfo_to_user32(uinfo, &info); | ||
463 | } | ||
464 | |||
465 | static int compat_get_user_cpu_mask(compat_ulong_t __user *user_mask_ptr, | 399 | static int compat_get_user_cpu_mask(compat_ulong_t __user *user_mask_ptr, |
466 | unsigned len, struct cpumask *new_mask) | 400 | unsigned len, struct cpumask *new_mask) |
467 | { | 401 | { |
diff --git a/kernel/exit.c b/kernel/exit.c index c63226283aef..b0cc86a2d00b 100644 --- a/kernel/exit.c +++ b/kernel/exit.c | |||
@@ -62,6 +62,7 @@ | |||
62 | #include <linux/kcov.h> | 62 | #include <linux/kcov.h> |
63 | #include <linux/random.h> | 63 | #include <linux/random.h> |
64 | #include <linux/rcuwait.h> | 64 | #include <linux/rcuwait.h> |
65 | #include <linux/compat.h> | ||
65 | 66 | ||
66 | #include <linux/uaccess.h> | 67 | #include <linux/uaccess.h> |
67 | #include <asm/unistd.h> | 68 | #include <asm/unistd.h> |
@@ -982,14 +983,21 @@ SYSCALL_DEFINE1(exit_group, int, error_code) | |||
982 | return 0; | 983 | return 0; |
983 | } | 984 | } |
984 | 985 | ||
986 | struct waitid_info { | ||
987 | pid_t pid; | ||
988 | uid_t uid; | ||
989 | int status; | ||
990 | int cause; | ||
991 | }; | ||
992 | |||
985 | struct wait_opts { | 993 | struct wait_opts { |
986 | enum pid_type wo_type; | 994 | enum pid_type wo_type; |
987 | int wo_flags; | 995 | int wo_flags; |
988 | struct pid *wo_pid; | 996 | struct pid *wo_pid; |
989 | 997 | ||
990 | struct siginfo __user *wo_info; | 998 | struct waitid_info *wo_info; |
991 | int __user *wo_stat; | 999 | int wo_stat; |
992 | struct rusage __user *wo_rusage; | 1000 | struct rusage *wo_rusage; |
993 | 1001 | ||
994 | wait_queue_entry_t child_wait; | 1002 | wait_queue_entry_t child_wait; |
995 | int notask_error; | 1003 | int notask_error; |
@@ -1036,34 +1044,6 @@ eligible_child(struct wait_opts *wo, bool ptrace, struct task_struct *p) | |||
1036 | return 1; | 1044 | return 1; |
1037 | } | 1045 | } |
1038 | 1046 | ||
1039 | static int wait_noreap_copyout(struct wait_opts *wo, struct task_struct *p, | ||
1040 | pid_t pid, uid_t uid, int why, int status) | ||
1041 | { | ||
1042 | struct siginfo __user *infop; | ||
1043 | int retval = wo->wo_rusage | ||
1044 | ? getrusage(p, RUSAGE_BOTH, wo->wo_rusage) : 0; | ||
1045 | |||
1046 | put_task_struct(p); | ||
1047 | infop = wo->wo_info; | ||
1048 | if (infop) { | ||
1049 | if (!retval) | ||
1050 | retval = put_user(SIGCHLD, &infop->si_signo); | ||
1051 | if (!retval) | ||
1052 | retval = put_user(0, &infop->si_errno); | ||
1053 | if (!retval) | ||
1054 | retval = put_user((short)why, &infop->si_code); | ||
1055 | if (!retval) | ||
1056 | retval = put_user(pid, &infop->si_pid); | ||
1057 | if (!retval) | ||
1058 | retval = put_user(uid, &infop->si_uid); | ||
1059 | if (!retval) | ||
1060 | retval = put_user(status, &infop->si_status); | ||
1061 | } | ||
1062 | if (!retval) | ||
1063 | retval = pid; | ||
1064 | return retval; | ||
1065 | } | ||
1066 | |||
1067 | /* | 1047 | /* |
1068 | * Handle sys_wait4 work for one task in state EXIT_ZOMBIE. We hold | 1048 | * Handle sys_wait4 work for one task in state EXIT_ZOMBIE. We hold |
1069 | * read_lock(&tasklist_lock) on entry. If we return zero, we still hold | 1049 | * read_lock(&tasklist_lock) on entry. If we return zero, we still hold |
@@ -1072,30 +1052,23 @@ static int wait_noreap_copyout(struct wait_opts *wo, struct task_struct *p, | |||
1072 | */ | 1052 | */ |
1073 | static int wait_task_zombie(struct wait_opts *wo, struct task_struct *p) | 1053 | static int wait_task_zombie(struct wait_opts *wo, struct task_struct *p) |
1074 | { | 1054 | { |
1075 | int state, retval, status; | 1055 | int state, status; |
1076 | pid_t pid = task_pid_vnr(p); | 1056 | pid_t pid = task_pid_vnr(p); |
1077 | uid_t uid = from_kuid_munged(current_user_ns(), task_uid(p)); | 1057 | uid_t uid = from_kuid_munged(current_user_ns(), task_uid(p)); |
1078 | struct siginfo __user *infop; | 1058 | struct waitid_info *infop; |
1079 | 1059 | ||
1080 | if (!likely(wo->wo_flags & WEXITED)) | 1060 | if (!likely(wo->wo_flags & WEXITED)) |
1081 | return 0; | 1061 | return 0; |
1082 | 1062 | ||
1083 | if (unlikely(wo->wo_flags & WNOWAIT)) { | 1063 | if (unlikely(wo->wo_flags & WNOWAIT)) { |
1084 | int exit_code = p->exit_code; | 1064 | status = p->exit_code; |
1085 | int why; | ||
1086 | |||
1087 | get_task_struct(p); | 1065 | get_task_struct(p); |
1088 | read_unlock(&tasklist_lock); | 1066 | read_unlock(&tasklist_lock); |
1089 | sched_annotate_sleep(); | 1067 | sched_annotate_sleep(); |
1090 | 1068 | if (wo->wo_rusage) | |
1091 | if ((exit_code & 0x7f) == 0) { | 1069 | getrusage(p, RUSAGE_BOTH, wo->wo_rusage); |
1092 | why = CLD_EXITED; | 1070 | put_task_struct(p); |
1093 | status = exit_code >> 8; | 1071 | goto out_info; |
1094 | } else { | ||
1095 | why = (exit_code & 0x80) ? CLD_DUMPED : CLD_KILLED; | ||
1096 | status = exit_code & 0x7f; | ||
1097 | } | ||
1098 | return wait_noreap_copyout(wo, p, pid, uid, why, status); | ||
1099 | } | 1072 | } |
1100 | /* | 1073 | /* |
1101 | * Move the task's state to DEAD/TRACE, only one thread can do this. | 1074 | * Move the task's state to DEAD/TRACE, only one thread can do this. |
@@ -1168,38 +1141,11 @@ static int wait_task_zombie(struct wait_opts *wo, struct task_struct *p) | |||
1168 | spin_unlock_irq(¤t->sighand->siglock); | 1141 | spin_unlock_irq(¤t->sighand->siglock); |
1169 | } | 1142 | } |
1170 | 1143 | ||
1171 | retval = wo->wo_rusage | 1144 | if (wo->wo_rusage) |
1172 | ? getrusage(p, RUSAGE_BOTH, wo->wo_rusage) : 0; | 1145 | getrusage(p, RUSAGE_BOTH, wo->wo_rusage); |
1173 | status = (p->signal->flags & SIGNAL_GROUP_EXIT) | 1146 | status = (p->signal->flags & SIGNAL_GROUP_EXIT) |
1174 | ? p->signal->group_exit_code : p->exit_code; | 1147 | ? p->signal->group_exit_code : p->exit_code; |
1175 | if (!retval && wo->wo_stat) | 1148 | wo->wo_stat = status; |
1176 | retval = put_user(status, wo->wo_stat); | ||
1177 | |||
1178 | infop = wo->wo_info; | ||
1179 | if (!retval && infop) | ||
1180 | retval = put_user(SIGCHLD, &infop->si_signo); | ||
1181 | if (!retval && infop) | ||
1182 | retval = put_user(0, &infop->si_errno); | ||
1183 | if (!retval && infop) { | ||
1184 | int why; | ||
1185 | |||
1186 | if ((status & 0x7f) == 0) { | ||
1187 | why = CLD_EXITED; | ||
1188 | status >>= 8; | ||
1189 | } else { | ||
1190 | why = (status & 0x80) ? CLD_DUMPED : CLD_KILLED; | ||
1191 | status &= 0x7f; | ||
1192 | } | ||
1193 | retval = put_user((short)why, &infop->si_code); | ||
1194 | if (!retval) | ||
1195 | retval = put_user(status, &infop->si_status); | ||
1196 | } | ||
1197 | if (!retval && infop) | ||
1198 | retval = put_user(pid, &infop->si_pid); | ||
1199 | if (!retval && infop) | ||
1200 | retval = put_user(uid, &infop->si_uid); | ||
1201 | if (!retval) | ||
1202 | retval = pid; | ||
1203 | 1149 | ||
1204 | if (state == EXIT_TRACE) { | 1150 | if (state == EXIT_TRACE) { |
1205 | write_lock_irq(&tasklist_lock); | 1151 | write_lock_irq(&tasklist_lock); |
@@ -1216,7 +1162,21 @@ static int wait_task_zombie(struct wait_opts *wo, struct task_struct *p) | |||
1216 | if (state == EXIT_DEAD) | 1162 | if (state == EXIT_DEAD) |
1217 | release_task(p); | 1163 | release_task(p); |
1218 | 1164 | ||
1219 | return retval; | 1165 | out_info: |
1166 | infop = wo->wo_info; | ||
1167 | if (infop) { | ||
1168 | if ((status & 0x7f) == 0) { | ||
1169 | infop->cause = CLD_EXITED; | ||
1170 | infop->status = status >> 8; | ||
1171 | } else { | ||
1172 | infop->cause = (status & 0x80) ? CLD_DUMPED : CLD_KILLED; | ||
1173 | infop->status = status & 0x7f; | ||
1174 | } | ||
1175 | infop->pid = pid; | ||
1176 | infop->uid = uid; | ||
1177 | } | ||
1178 | |||
1179 | return pid; | ||
1220 | } | 1180 | } |
1221 | 1181 | ||
1222 | static int *task_stopped_code(struct task_struct *p, bool ptrace) | 1182 | static int *task_stopped_code(struct task_struct *p, bool ptrace) |
@@ -1252,8 +1212,8 @@ static int *task_stopped_code(struct task_struct *p, bool ptrace) | |||
1252 | static int wait_task_stopped(struct wait_opts *wo, | 1212 | static int wait_task_stopped(struct wait_opts *wo, |
1253 | int ptrace, struct task_struct *p) | 1213 | int ptrace, struct task_struct *p) |
1254 | { | 1214 | { |
1255 | struct siginfo __user *infop; | 1215 | struct waitid_info *infop; |
1256 | int retval, exit_code, *p_code, why; | 1216 | int exit_code, *p_code, why; |
1257 | uid_t uid = 0; /* unneeded, required by compiler */ | 1217 | uid_t uid = 0; /* unneeded, required by compiler */ |
1258 | pid_t pid; | 1218 | pid_t pid; |
1259 | 1219 | ||
@@ -1298,34 +1258,21 @@ unlock_sig: | |||
1298 | why = ptrace ? CLD_TRAPPED : CLD_STOPPED; | 1258 | why = ptrace ? CLD_TRAPPED : CLD_STOPPED; |
1299 | read_unlock(&tasklist_lock); | 1259 | read_unlock(&tasklist_lock); |
1300 | sched_annotate_sleep(); | 1260 | sched_annotate_sleep(); |
1261 | if (wo->wo_rusage) | ||
1262 | getrusage(p, RUSAGE_BOTH, wo->wo_rusage); | ||
1263 | put_task_struct(p); | ||
1301 | 1264 | ||
1302 | if (unlikely(wo->wo_flags & WNOWAIT)) | 1265 | if (likely(!(wo->wo_flags & WNOWAIT))) |
1303 | return wait_noreap_copyout(wo, p, pid, uid, why, exit_code); | 1266 | wo->wo_stat = (exit_code << 8) | 0x7f; |
1304 | |||
1305 | retval = wo->wo_rusage | ||
1306 | ? getrusage(p, RUSAGE_BOTH, wo->wo_rusage) : 0; | ||
1307 | if (!retval && wo->wo_stat) | ||
1308 | retval = put_user((exit_code << 8) | 0x7f, wo->wo_stat); | ||
1309 | 1267 | ||
1310 | infop = wo->wo_info; | 1268 | infop = wo->wo_info; |
1311 | if (!retval && infop) | 1269 | if (infop) { |
1312 | retval = put_user(SIGCHLD, &infop->si_signo); | 1270 | infop->cause = why; |
1313 | if (!retval && infop) | 1271 | infop->status = exit_code; |
1314 | retval = put_user(0, &infop->si_errno); | 1272 | infop->pid = pid; |
1315 | if (!retval && infop) | 1273 | infop->uid = uid; |
1316 | retval = put_user((short)why, &infop->si_code); | 1274 | } |
1317 | if (!retval && infop) | 1275 | return pid; |
1318 | retval = put_user(exit_code, &infop->si_status); | ||
1319 | if (!retval && infop) | ||
1320 | retval = put_user(pid, &infop->si_pid); | ||
1321 | if (!retval && infop) | ||
1322 | retval = put_user(uid, &infop->si_uid); | ||
1323 | if (!retval) | ||
1324 | retval = pid; | ||
1325 | put_task_struct(p); | ||
1326 | |||
1327 | BUG_ON(!retval); | ||
1328 | return retval; | ||
1329 | } | 1276 | } |
1330 | 1277 | ||
1331 | /* | 1278 | /* |
@@ -1336,7 +1283,7 @@ unlock_sig: | |||
1336 | */ | 1283 | */ |
1337 | static int wait_task_continued(struct wait_opts *wo, struct task_struct *p) | 1284 | static int wait_task_continued(struct wait_opts *wo, struct task_struct *p) |
1338 | { | 1285 | { |
1339 | int retval; | 1286 | struct waitid_info *infop; |
1340 | pid_t pid; | 1287 | pid_t pid; |
1341 | uid_t uid; | 1288 | uid_t uid; |
1342 | 1289 | ||
@@ -1361,22 +1308,20 @@ static int wait_task_continued(struct wait_opts *wo, struct task_struct *p) | |||
1361 | get_task_struct(p); | 1308 | get_task_struct(p); |
1362 | read_unlock(&tasklist_lock); | 1309 | read_unlock(&tasklist_lock); |
1363 | sched_annotate_sleep(); | 1310 | sched_annotate_sleep(); |
1311 | if (wo->wo_rusage) | ||
1312 | getrusage(p, RUSAGE_BOTH, wo->wo_rusage); | ||
1313 | put_task_struct(p); | ||
1364 | 1314 | ||
1365 | if (!wo->wo_info) { | 1315 | infop = wo->wo_info; |
1366 | retval = wo->wo_rusage | 1316 | if (!infop) { |
1367 | ? getrusage(p, RUSAGE_BOTH, wo->wo_rusage) : 0; | 1317 | wo->wo_stat = 0xffff; |
1368 | put_task_struct(p); | ||
1369 | if (!retval && wo->wo_stat) | ||
1370 | retval = put_user(0xffff, wo->wo_stat); | ||
1371 | if (!retval) | ||
1372 | retval = pid; | ||
1373 | } else { | 1318 | } else { |
1374 | retval = wait_noreap_copyout(wo, p, pid, uid, | 1319 | infop->cause = CLD_CONTINUED; |
1375 | CLD_CONTINUED, SIGCONT); | 1320 | infop->pid = pid; |
1376 | BUG_ON(retval == 0); | 1321 | infop->uid = uid; |
1322 | infop->status = SIGCONT; | ||
1377 | } | 1323 | } |
1378 | 1324 | return pid; | |
1379 | return retval; | ||
1380 | } | 1325 | } |
1381 | 1326 | ||
1382 | /* | 1327 | /* |
@@ -1604,8 +1549,8 @@ end: | |||
1604 | return retval; | 1549 | return retval; |
1605 | } | 1550 | } |
1606 | 1551 | ||
1607 | SYSCALL_DEFINE5(waitid, int, which, pid_t, upid, struct siginfo __user *, | 1552 | static long kernel_waitid(int which, pid_t upid, struct waitid_info *infop, |
1608 | infop, int, options, struct rusage __user *, ru) | 1553 | int options, struct rusage *ru) |
1609 | { | 1554 | { |
1610 | struct wait_opts wo; | 1555 | struct wait_opts wo; |
1611 | struct pid *pid = NULL; | 1556 | struct pid *pid = NULL; |
@@ -1643,38 +1588,46 @@ SYSCALL_DEFINE5(waitid, int, which, pid_t, upid, struct siginfo __user *, | |||
1643 | wo.wo_pid = pid; | 1588 | wo.wo_pid = pid; |
1644 | wo.wo_flags = options; | 1589 | wo.wo_flags = options; |
1645 | wo.wo_info = infop; | 1590 | wo.wo_info = infop; |
1646 | wo.wo_stat = NULL; | ||
1647 | wo.wo_rusage = ru; | 1591 | wo.wo_rusage = ru; |
1648 | ret = do_wait(&wo); | 1592 | ret = do_wait(&wo); |
1649 | 1593 | ||
1650 | if (ret > 0) { | 1594 | if (ret > 0) |
1651 | ret = 0; | 1595 | ret = 0; |
1652 | } else if (infop) { | ||
1653 | /* | ||
1654 | * For a WNOHANG return, clear out all the fields | ||
1655 | * we would set so the user can easily tell the | ||
1656 | * difference. | ||
1657 | */ | ||
1658 | if (!ret) | ||
1659 | ret = put_user(0, &infop->si_signo); | ||
1660 | if (!ret) | ||
1661 | ret = put_user(0, &infop->si_errno); | ||
1662 | if (!ret) | ||
1663 | ret = put_user(0, &infop->si_code); | ||
1664 | if (!ret) | ||
1665 | ret = put_user(0, &infop->si_pid); | ||
1666 | if (!ret) | ||
1667 | ret = put_user(0, &infop->si_uid); | ||
1668 | if (!ret) | ||
1669 | ret = put_user(0, &infop->si_status); | ||
1670 | } | ||
1671 | 1596 | ||
1672 | put_pid(pid); | 1597 | put_pid(pid); |
1673 | return ret; | 1598 | return ret; |
1674 | } | 1599 | } |
1675 | 1600 | ||
1676 | SYSCALL_DEFINE4(wait4, pid_t, upid, int __user *, stat_addr, | 1601 | SYSCALL_DEFINE5(waitid, int, which, pid_t, upid, struct siginfo __user *, |
1677 | int, options, struct rusage __user *, ru) | 1602 | infop, int, options, struct rusage __user *, ru) |
1603 | { | ||
1604 | struct rusage r; | ||
1605 | struct waitid_info info = {.status = 0}; | ||
1606 | long err = kernel_waitid(which, upid, &info, options, ru ? &r : NULL); | ||
1607 | |||
1608 | if (!err) { | ||
1609 | if (ru && copy_to_user(ru, &r, sizeof(struct rusage))) | ||
1610 | return -EFAULT; | ||
1611 | } | ||
1612 | if (!infop) | ||
1613 | return err; | ||
1614 | |||
1615 | user_access_begin(); | ||
1616 | unsafe_put_user(err ? 0 : SIGCHLD, &infop->si_signo, Efault); | ||
1617 | unsafe_put_user(0, &infop->si_errno, Efault); | ||
1618 | unsafe_put_user((short)info.cause, &infop->si_code, Efault); | ||
1619 | unsafe_put_user(info.pid, &infop->si_pid, Efault); | ||
1620 | unsafe_put_user(info.uid, &infop->si_uid, Efault); | ||
1621 | unsafe_put_user(info.status, &infop->si_status, Efault); | ||
1622 | user_access_end(); | ||
1623 | return err; | ||
1624 | Efault: | ||
1625 | user_access_end(); | ||
1626 | return -EFAULT; | ||
1627 | } | ||
1628 | |||
1629 | long kernel_wait4(pid_t upid, int __user *stat_addr, int options, | ||
1630 | struct rusage *ru) | ||
1678 | { | 1631 | { |
1679 | struct wait_opts wo; | 1632 | struct wait_opts wo; |
1680 | struct pid *pid = NULL; | 1633 | struct pid *pid = NULL; |
@@ -1702,14 +1655,29 @@ SYSCALL_DEFINE4(wait4, pid_t, upid, int __user *, stat_addr, | |||
1702 | wo.wo_pid = pid; | 1655 | wo.wo_pid = pid; |
1703 | wo.wo_flags = options | WEXITED; | 1656 | wo.wo_flags = options | WEXITED; |
1704 | wo.wo_info = NULL; | 1657 | wo.wo_info = NULL; |
1705 | wo.wo_stat = stat_addr; | 1658 | wo.wo_stat = 0; |
1706 | wo.wo_rusage = ru; | 1659 | wo.wo_rusage = ru; |
1707 | ret = do_wait(&wo); | 1660 | ret = do_wait(&wo); |
1708 | put_pid(pid); | 1661 | put_pid(pid); |
1662 | if (ret > 0 && stat_addr && put_user(wo.wo_stat, stat_addr)) | ||
1663 | ret = -EFAULT; | ||
1709 | 1664 | ||
1710 | return ret; | 1665 | return ret; |
1711 | } | 1666 | } |
1712 | 1667 | ||
1668 | SYSCALL_DEFINE4(wait4, pid_t, upid, int __user *, stat_addr, | ||
1669 | int, options, struct rusage __user *, ru) | ||
1670 | { | ||
1671 | struct rusage r; | ||
1672 | long err = kernel_wait4(upid, stat_addr, options, ru ? &r : NULL); | ||
1673 | |||
1674 | if (err > 0) { | ||
1675 | if (ru && copy_to_user(ru, &r, sizeof(struct rusage))) | ||
1676 | return -EFAULT; | ||
1677 | } | ||
1678 | return err; | ||
1679 | } | ||
1680 | |||
1713 | #ifdef __ARCH_WANT_SYS_WAITPID | 1681 | #ifdef __ARCH_WANT_SYS_WAITPID |
1714 | 1682 | ||
1715 | /* | 1683 | /* |
@@ -1722,3 +1690,56 @@ SYSCALL_DEFINE3(waitpid, pid_t, pid, int __user *, stat_addr, int, options) | |||
1722 | } | 1690 | } |
1723 | 1691 | ||
1724 | #endif | 1692 | #endif |
1693 | |||
1694 | #ifdef CONFIG_COMPAT | ||
1695 | COMPAT_SYSCALL_DEFINE4(wait4, | ||
1696 | compat_pid_t, pid, | ||
1697 | compat_uint_t __user *, stat_addr, | ||
1698 | int, options, | ||
1699 | struct compat_rusage __user *, ru) | ||
1700 | { | ||
1701 | struct rusage r; | ||
1702 | long err = kernel_wait4(pid, stat_addr, options, ru ? &r : NULL); | ||
1703 | if (err > 0) { | ||
1704 | if (ru && put_compat_rusage(&r, ru)) | ||
1705 | return -EFAULT; | ||
1706 | } | ||
1707 | return err; | ||
1708 | } | ||
1709 | |||
1710 | COMPAT_SYSCALL_DEFINE5(waitid, | ||
1711 | int, which, compat_pid_t, pid, | ||
1712 | struct compat_siginfo __user *, infop, int, options, | ||
1713 | struct compat_rusage __user *, uru) | ||
1714 | { | ||
1715 | struct rusage ru; | ||
1716 | struct waitid_info info = {.status = 0}; | ||
1717 | long err = kernel_waitid(which, pid, &info, options, uru ? &ru : NULL); | ||
1718 | |||
1719 | if (!err && uru) { | ||
1720 | /* kernel_waitid() overwrites everything in ru */ | ||
1721 | if (COMPAT_USE_64BIT_TIME) | ||
1722 | err = copy_to_user(uru, &ru, sizeof(ru)); | ||
1723 | else | ||
1724 | err = put_compat_rusage(&ru, uru); | ||
1725 | if (err) | ||
1726 | return -EFAULT; | ||
1727 | } | ||
1728 | |||
1729 | if (!infop) | ||
1730 | return err; | ||
1731 | |||
1732 | user_access_begin(); | ||
1733 | unsafe_put_user(err ? 0 : SIGCHLD, &infop->si_signo, Efault); | ||
1734 | unsafe_put_user(0, &infop->si_errno, Efault); | ||
1735 | unsafe_put_user((short)info.cause, &infop->si_code, Efault); | ||
1736 | unsafe_put_user(info.pid, &infop->si_pid, Efault); | ||
1737 | unsafe_put_user(info.uid, &infop->si_uid, Efault); | ||
1738 | unsafe_put_user(info.status, &infop->si_status, Efault); | ||
1739 | user_access_end(); | ||
1740 | return err; | ||
1741 | Efault: | ||
1742 | user_access_end(); | ||
1743 | return -EFAULT; | ||
1744 | } | ||
1745 | #endif | ||
diff --git a/kernel/sys.c b/kernel/sys.c index 8a94b4eabcaa..dab1a0658a92 100644 --- a/kernel/sys.c +++ b/kernel/sys.c | |||
@@ -1552,7 +1552,7 @@ static void accumulate_thread_rusage(struct task_struct *t, struct rusage *r) | |||
1552 | r->ru_oublock += task_io_get_oublock(t); | 1552 | r->ru_oublock += task_io_get_oublock(t); |
1553 | } | 1553 | } |
1554 | 1554 | ||
1555 | static void k_getrusage(struct task_struct *p, int who, struct rusage *r) | 1555 | void getrusage(struct task_struct *p, int who, struct rusage *r) |
1556 | { | 1556 | { |
1557 | struct task_struct *t; | 1557 | struct task_struct *t; |
1558 | unsigned long flags; | 1558 | unsigned long flags; |
@@ -1626,20 +1626,16 @@ out: | |||
1626 | r->ru_maxrss = maxrss * (PAGE_SIZE / 1024); /* convert pages to KBs */ | 1626 | r->ru_maxrss = maxrss * (PAGE_SIZE / 1024); /* convert pages to KBs */ |
1627 | } | 1627 | } |
1628 | 1628 | ||
1629 | int getrusage(struct task_struct *p, int who, struct rusage __user *ru) | 1629 | SYSCALL_DEFINE2(getrusage, int, who, struct rusage __user *, ru) |
1630 | { | 1630 | { |
1631 | struct rusage r; | 1631 | struct rusage r; |
1632 | 1632 | ||
1633 | k_getrusage(p, who, &r); | ||
1634 | return copy_to_user(ru, &r, sizeof(r)) ? -EFAULT : 0; | ||
1635 | } | ||
1636 | |||
1637 | SYSCALL_DEFINE2(getrusage, int, who, struct rusage __user *, ru) | ||
1638 | { | ||
1639 | if (who != RUSAGE_SELF && who != RUSAGE_CHILDREN && | 1633 | if (who != RUSAGE_SELF && who != RUSAGE_CHILDREN && |
1640 | who != RUSAGE_THREAD) | 1634 | who != RUSAGE_THREAD) |
1641 | return -EINVAL; | 1635 | return -EINVAL; |
1642 | return getrusage(current, who, ru); | 1636 | |
1637 | getrusage(current, who, &r); | ||
1638 | return copy_to_user(ru, &r, sizeof(r)) ? -EFAULT : 0; | ||
1643 | } | 1639 | } |
1644 | 1640 | ||
1645 | #ifdef CONFIG_COMPAT | 1641 | #ifdef CONFIG_COMPAT |
@@ -1651,7 +1647,7 @@ COMPAT_SYSCALL_DEFINE2(getrusage, int, who, struct compat_rusage __user *, ru) | |||
1651 | who != RUSAGE_THREAD) | 1647 | who != RUSAGE_THREAD) |
1652 | return -EINVAL; | 1648 | return -EINVAL; |
1653 | 1649 | ||
1654 | k_getrusage(current, who, &r); | 1650 | getrusage(current, who, &r); |
1655 | return put_compat_rusage(&r, ru); | 1651 | return put_compat_rusage(&r, ru); |
1656 | } | 1652 | } |
1657 | #endif | 1653 | #endif |