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 /kernel | |
| 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
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/compat.c | 66 | ||||
| -rw-r--r-- | kernel/exit.c | 307 | ||||
| -rw-r--r-- | kernel/sys.c | 16 |
3 files changed, 170 insertions, 219 deletions
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 |
