diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2017-05-14 20:53:13 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2017-05-21 13:13:07 -0400 |
commit | 67d7ddded322db99f451a7959d56ed6c70a6c4aa (patch) | |
tree | 06db8587425d42836322b26688d1939568d68ab4 | |
parent | 359566faefa850504d146839d74496f0cf12d3b9 (diff) |
waitid(2): leave copyout of siginfo to syscall itself
have kernel_waitid() collect the information needed for siginfo into
a small structure (waitid_info) passed to it; deal with copyout in
sys_waitid()/compat_sys_waitid().
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r-- | kernel/exit.c | 168 |
1 files changed, 64 insertions, 104 deletions
diff --git a/kernel/exit.c b/kernel/exit.c index 94cdccf8e7e7..42f26480b3cc 100644 --- a/kernel/exit.c +++ b/kernel/exit.c | |||
@@ -996,12 +996,19 @@ SYSCALL_DEFINE1(exit_group, int, error_code) | |||
996 | return 0; | 996 | return 0; |
997 | } | 997 | } |
998 | 998 | ||
999 | struct waitid_info { | ||
1000 | pid_t pid; | ||
1001 | uid_t uid; | ||
1002 | int status; | ||
1003 | int cause; | ||
1004 | }; | ||
1005 | |||
999 | struct wait_opts { | 1006 | struct wait_opts { |
1000 | enum pid_type wo_type; | 1007 | enum pid_type wo_type; |
1001 | int wo_flags; | 1008 | int wo_flags; |
1002 | struct pid *wo_pid; | 1009 | struct pid *wo_pid; |
1003 | 1010 | ||
1004 | struct siginfo __user *wo_info; | 1011 | struct waitid_info *wo_info; |
1005 | int wo_stat; | 1012 | int wo_stat; |
1006 | struct rusage *wo_rusage; | 1013 | struct rusage *wo_rusage; |
1007 | 1014 | ||
@@ -1053,8 +1060,7 @@ eligible_child(struct wait_opts *wo, bool ptrace, struct task_struct *p) | |||
1053 | static int wait_noreap_copyout(struct wait_opts *wo, struct task_struct *p, | 1060 | static int wait_noreap_copyout(struct wait_opts *wo, struct task_struct *p, |
1054 | pid_t pid, uid_t uid, int why, int status) | 1061 | pid_t pid, uid_t uid, int why, int status) |
1055 | { | 1062 | { |
1056 | struct siginfo __user *infop; | 1063 | struct waitid_info *infop; |
1057 | int retval = 0; | ||
1058 | 1064 | ||
1059 | if (wo->wo_rusage) | 1065 | if (wo->wo_rusage) |
1060 | getrusage(p, RUSAGE_BOTH, wo->wo_rusage); | 1066 | getrusage(p, RUSAGE_BOTH, wo->wo_rusage); |
@@ -1062,22 +1068,12 @@ static int wait_noreap_copyout(struct wait_opts *wo, struct task_struct *p, | |||
1062 | put_task_struct(p); | 1068 | put_task_struct(p); |
1063 | infop = wo->wo_info; | 1069 | infop = wo->wo_info; |
1064 | if (infop) { | 1070 | if (infop) { |
1065 | if (!retval) | 1071 | infop->cause = why; |
1066 | retval = put_user(SIGCHLD, &infop->si_signo); | 1072 | infop->pid = pid; |
1067 | if (!retval) | 1073 | infop->uid = uid; |
1068 | retval = put_user(0, &infop->si_errno); | 1074 | infop->status = status; |
1069 | if (!retval) | ||
1070 | retval = put_user((short)why, &infop->si_code); | ||
1071 | if (!retval) | ||
1072 | retval = put_user(pid, &infop->si_pid); | ||
1073 | if (!retval) | ||
1074 | retval = put_user(uid, &infop->si_uid); | ||
1075 | if (!retval) | ||
1076 | retval = put_user(status, &infop->si_status); | ||
1077 | } | 1075 | } |
1078 | if (!retval) | 1076 | return pid; |
1079 | retval = pid; | ||
1080 | return retval; | ||
1081 | } | 1077 | } |
1082 | 1078 | ||
1083 | /* | 1079 | /* |
@@ -1088,10 +1084,10 @@ static int wait_noreap_copyout(struct wait_opts *wo, struct task_struct *p, | |||
1088 | */ | 1084 | */ |
1089 | static int wait_task_zombie(struct wait_opts *wo, struct task_struct *p) | 1085 | static int wait_task_zombie(struct wait_opts *wo, struct task_struct *p) |
1090 | { | 1086 | { |
1091 | int state, retval, status; | 1087 | int state, status; |
1092 | pid_t pid = task_pid_vnr(p); | 1088 | pid_t pid = task_pid_vnr(p); |
1093 | uid_t uid = from_kuid_munged(current_user_ns(), task_uid(p)); | 1089 | uid_t uid = from_kuid_munged(current_user_ns(), task_uid(p)); |
1094 | struct siginfo __user *infop; | 1090 | struct waitid_info *infop; |
1095 | 1091 | ||
1096 | if (!likely(wo->wo_flags & WEXITED)) | 1092 | if (!likely(wo->wo_flags & WEXITED)) |
1097 | return 0; | 1093 | return 0; |
@@ -1186,36 +1182,22 @@ static int wait_task_zombie(struct wait_opts *wo, struct task_struct *p) | |||
1186 | 1182 | ||
1187 | if (wo->wo_rusage) | 1183 | if (wo->wo_rusage) |
1188 | getrusage(p, RUSAGE_BOTH, wo->wo_rusage); | 1184 | getrusage(p, RUSAGE_BOTH, wo->wo_rusage); |
1189 | retval = 0; | ||
1190 | status = (p->signal->flags & SIGNAL_GROUP_EXIT) | 1185 | status = (p->signal->flags & SIGNAL_GROUP_EXIT) |
1191 | ? p->signal->group_exit_code : p->exit_code; | 1186 | ? p->signal->group_exit_code : p->exit_code; |
1192 | wo->wo_stat = status; | 1187 | wo->wo_stat = status; |
1193 | 1188 | ||
1194 | infop = wo->wo_info; | 1189 | infop = wo->wo_info; |
1195 | if (!retval && infop) | 1190 | if (infop) { |
1196 | retval = put_user(SIGCHLD, &infop->si_signo); | ||
1197 | if (!retval && infop) | ||
1198 | retval = put_user(0, &infop->si_errno); | ||
1199 | if (!retval && infop) { | ||
1200 | int why; | ||
1201 | |||
1202 | if ((status & 0x7f) == 0) { | 1191 | if ((status & 0x7f) == 0) { |
1203 | why = CLD_EXITED; | 1192 | infop->cause = CLD_EXITED; |
1204 | status >>= 8; | 1193 | infop->status = status >> 8; |
1205 | } else { | 1194 | } else { |
1206 | why = (status & 0x80) ? CLD_DUMPED : CLD_KILLED; | 1195 | infop->cause = (status & 0x80) ? CLD_DUMPED : CLD_KILLED; |
1207 | status &= 0x7f; | 1196 | infop->status = status & 0x7f; |
1208 | } | 1197 | } |
1209 | retval = put_user((short)why, &infop->si_code); | 1198 | infop->pid = pid; |
1210 | if (!retval) | 1199 | infop->uid = uid; |
1211 | retval = put_user(status, &infop->si_status); | ||
1212 | } | 1200 | } |
1213 | if (!retval && infop) | ||
1214 | retval = put_user(pid, &infop->si_pid); | ||
1215 | if (!retval && infop) | ||
1216 | retval = put_user(uid, &infop->si_uid); | ||
1217 | if (!retval) | ||
1218 | retval = pid; | ||
1219 | 1201 | ||
1220 | if (state == EXIT_TRACE) { | 1202 | if (state == EXIT_TRACE) { |
1221 | write_lock_irq(&tasklist_lock); | 1203 | write_lock_irq(&tasklist_lock); |
@@ -1232,7 +1214,7 @@ static int wait_task_zombie(struct wait_opts *wo, struct task_struct *p) | |||
1232 | if (state == EXIT_DEAD) | 1214 | if (state == EXIT_DEAD) |
1233 | release_task(p); | 1215 | release_task(p); |
1234 | 1216 | ||
1235 | return retval; | 1217 | return pid; |
1236 | } | 1218 | } |
1237 | 1219 | ||
1238 | static int *task_stopped_code(struct task_struct *p, bool ptrace) | 1220 | static int *task_stopped_code(struct task_struct *p, bool ptrace) |
@@ -1268,8 +1250,8 @@ static int *task_stopped_code(struct task_struct *p, bool ptrace) | |||
1268 | static int wait_task_stopped(struct wait_opts *wo, | 1250 | static int wait_task_stopped(struct wait_opts *wo, |
1269 | int ptrace, struct task_struct *p) | 1251 | int ptrace, struct task_struct *p) |
1270 | { | 1252 | { |
1271 | struct siginfo __user *infop; | 1253 | struct waitid_info *infop; |
1272 | int retval, exit_code, *p_code, why; | 1254 | int exit_code, *p_code, why; |
1273 | uid_t uid = 0; /* unneeded, required by compiler */ | 1255 | uid_t uid = 0; /* unneeded, required by compiler */ |
1274 | pid_t pid; | 1256 | pid_t pid; |
1275 | 1257 | ||
@@ -1320,28 +1302,19 @@ unlock_sig: | |||
1320 | 1302 | ||
1321 | if (wo->wo_rusage) | 1303 | if (wo->wo_rusage) |
1322 | getrusage(p, RUSAGE_BOTH, wo->wo_rusage); | 1304 | getrusage(p, RUSAGE_BOTH, wo->wo_rusage); |
1323 | retval = 0; | ||
1324 | wo->wo_stat = (exit_code << 8) | 0x7f; | 1305 | wo->wo_stat = (exit_code << 8) | 0x7f; |
1325 | 1306 | ||
1326 | infop = wo->wo_info; | 1307 | infop = wo->wo_info; |
1327 | if (!retval && infop) | 1308 | if (infop) { |
1328 | retval = put_user(SIGCHLD, &infop->si_signo); | 1309 | infop->cause = why; |
1329 | if (!retval && infop) | 1310 | infop->status = exit_code; |
1330 | retval = put_user(0, &infop->si_errno); | 1311 | infop->pid = pid; |
1331 | if (!retval && infop) | 1312 | infop->uid = uid; |
1332 | retval = put_user((short)why, &infop->si_code); | 1313 | } |
1333 | if (!retval && infop) | ||
1334 | retval = put_user(exit_code, &infop->si_status); | ||
1335 | if (!retval && infop) | ||
1336 | retval = put_user(pid, &infop->si_pid); | ||
1337 | if (!retval && infop) | ||
1338 | retval = put_user(uid, &infop->si_uid); | ||
1339 | if (!retval) | ||
1340 | retval = pid; | ||
1341 | put_task_struct(p); | 1314 | put_task_struct(p); |
1342 | 1315 | ||
1343 | BUG_ON(!retval); | 1316 | BUG_ON(!pid); |
1344 | return retval; | 1317 | return pid; |
1345 | } | 1318 | } |
1346 | 1319 | ||
1347 | /* | 1320 | /* |
@@ -1618,7 +1591,7 @@ end: | |||
1618 | return retval; | 1591 | return retval; |
1619 | } | 1592 | } |
1620 | 1593 | ||
1621 | static long kernel_waitid(int which, pid_t upid, struct siginfo __user *infop, | 1594 | static long kernel_waitid(int which, pid_t upid, struct waitid_info *infop, |
1622 | int options, struct rusage *ru) | 1595 | int options, struct rusage *ru) |
1623 | { | 1596 | { |
1624 | struct wait_opts wo; | 1597 | struct wait_opts wo; |
@@ -1660,27 +1633,8 @@ static long kernel_waitid(int which, pid_t upid, struct siginfo __user *infop, | |||
1660 | wo.wo_rusage = ru; | 1633 | wo.wo_rusage = ru; |
1661 | ret = do_wait(&wo); | 1634 | ret = do_wait(&wo); |
1662 | 1635 | ||
1663 | if (ret > 0) { | 1636 | if (ret > 0) |
1664 | ret = 0; | 1637 | ret = 0; |
1665 | } else if (infop) { | ||
1666 | /* | ||
1667 | * For a WNOHANG return, clear out all the fields | ||
1668 | * we would set so the user can easily tell the | ||
1669 | * difference. | ||
1670 | */ | ||
1671 | if (!ret) | ||
1672 | ret = put_user(0, &infop->si_signo); | ||
1673 | if (!ret) | ||
1674 | ret = put_user(0, &infop->si_errno); | ||
1675 | if (!ret) | ||
1676 | ret = put_user(0, &infop->si_code); | ||
1677 | if (!ret) | ||
1678 | ret = put_user(0, &infop->si_pid); | ||
1679 | if (!ret) | ||
1680 | ret = put_user(0, &infop->si_uid); | ||
1681 | if (!ret) | ||
1682 | ret = put_user(0, &infop->si_status); | ||
1683 | } | ||
1684 | 1638 | ||
1685 | put_pid(pid); | 1639 | put_pid(pid); |
1686 | return ret; | 1640 | return ret; |
@@ -1690,12 +1644,24 @@ SYSCALL_DEFINE5(waitid, int, which, pid_t, upid, struct siginfo __user *, | |||
1690 | infop, int, options, struct rusage __user *, ru) | 1644 | infop, int, options, struct rusage __user *, ru) |
1691 | { | 1645 | { |
1692 | struct rusage r; | 1646 | struct rusage r; |
1693 | long err = kernel_waitid(which, upid, infop, options, ru ? &r : NULL); | 1647 | struct waitid_info info = {.status = 0}; |
1648 | long err = kernel_waitid(which, upid, &info, options, ru ? &r : NULL); | ||
1694 | 1649 | ||
1695 | if (!err) { | 1650 | if (!err) { |
1696 | if (ru && copy_to_user(ru, &r, sizeof(struct rusage))) | 1651 | if (ru && copy_to_user(ru, &r, sizeof(struct rusage))) |
1697 | return -EFAULT; | 1652 | return -EFAULT; |
1698 | } | 1653 | } |
1654 | if (!infop) | ||
1655 | return err; | ||
1656 | |||
1657 | if (put_user(err ? 0 : SIGCHLD, &infop->si_signo) || | ||
1658 | put_user(0, &infop->si_errno) || | ||
1659 | put_user((short)info.cause, &infop->si_code) || | ||
1660 | put_user(info.pid, &infop->si_pid) || | ||
1661 | put_user(info.uid, &infop->si_uid) || | ||
1662 | put_user(info.status, &infop->si_status)) | ||
1663 | err = -EFAULT; | ||
1664 | |||
1699 | return err; | 1665 | return err; |
1700 | } | 1666 | } |
1701 | 1667 | ||
@@ -1785,33 +1751,27 @@ COMPAT_SYSCALL_DEFINE5(waitid, | |||
1785 | struct compat_siginfo __user *, infop, int, options, | 1751 | struct compat_siginfo __user *, infop, int, options, |
1786 | struct compat_rusage __user *, uru) | 1752 | struct compat_rusage __user *, uru) |
1787 | { | 1753 | { |
1788 | siginfo_t info; | ||
1789 | struct rusage ru; | 1754 | struct rusage ru; |
1790 | long ret; | 1755 | struct waitid_info info = {.status = 0}; |
1791 | mm_segment_t old_fs = get_fs(); | 1756 | long err = kernel_waitid(which, pid, &info, options, uru ? &ru : NULL); |
1792 | |||
1793 | memset(&info, 0, sizeof(info)); | ||
1794 | 1757 | ||
1795 | set_fs(KERNEL_DS); | 1758 | if (!err && uru) { |
1796 | ret = kernel_waitid(which, pid, (siginfo_t __user *)&info, options, | 1759 | /* kernel_waitid() overwrites everything in ru */ |
1797 | uru ? &ru : NULL); | ||
1798 | set_fs(old_fs); | ||
1799 | |||
1800 | if ((ret < 0) || (info.si_signo == 0)) | ||
1801 | return ret; | ||
1802 | |||
1803 | if (uru) { | ||
1804 | /* sys_waitid() overwrites everything in ru */ | ||
1805 | if (COMPAT_USE_64BIT_TIME) | 1760 | if (COMPAT_USE_64BIT_TIME) |
1806 | ret = copy_to_user(uru, &ru, sizeof(ru)); | 1761 | err = copy_to_user(uru, &ru, sizeof(ru)); |
1807 | else | 1762 | else |
1808 | ret = put_compat_rusage(&ru, uru); | 1763 | err = put_compat_rusage(&ru, uru); |
1809 | if (ret) | 1764 | if (err) |
1810 | return -EFAULT; | 1765 | return -EFAULT; |
1811 | } | 1766 | } |
1812 | 1767 | ||
1813 | BUG_ON(info.si_code & __SI_MASK); | 1768 | if (put_user(err ? 0 : SIGCHLD, &infop->si_signo) || |
1814 | info.si_code |= __SI_CHLD; | 1769 | put_user(0, &infop->si_errno) || |
1815 | return copy_siginfo_to_user32(infop, &info); | 1770 | put_user((short)info.cause, &infop->si_code) || |
1771 | put_user(info.pid, &infop->si_pid) || | ||
1772 | put_user(info.uid, &infop->si_uid) || | ||
1773 | put_user(info.status, &infop->si_status)) | ||
1774 | err = -EFAULT; | ||
1775 | return err; | ||
1816 | } | 1776 | } |
1817 | #endif | 1777 | #endif |