diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2017-07-09 10:03:23 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2017-07-15 20:46:44 -0400 |
commit | c0ebccb6fa1e2c9c3377fa8136e6d8bc006fca64 (patch) | |
tree | b4c5565819658fca6533ef779fec432ba3654b00 | |
parent | 45a4a64ab485d5c3e76ee79163a24303bf5077fd (diff) |
semctl(): move compat to native
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r-- | ipc/compat.c | 230 | ||||
-rw-r--r-- | ipc/sem.c | 94 | ||||
-rw-r--r-- | ipc/util.h | 6 |
3 files changed, 133 insertions, 197 deletions
diff --git a/ipc/compat.c b/ipc/compat.c index c83099a3b265..3c25ca1e46c7 100644 --- a/ipc/compat.c +++ b/ipc/compat.c | |||
@@ -39,214 +39,55 @@ struct compat_msgbuf { | |||
39 | char mtext[1]; | 39 | char mtext[1]; |
40 | }; | 40 | }; |
41 | 41 | ||
42 | struct compat_semid_ds { | ||
43 | struct compat_ipc_perm sem_perm; | ||
44 | compat_time_t sem_otime; | ||
45 | compat_time_t sem_ctime; | ||
46 | compat_uptr_t sem_base; | ||
47 | compat_uptr_t sem_pending; | ||
48 | compat_uptr_t sem_pending_last; | ||
49 | compat_uptr_t undo; | ||
50 | unsigned short sem_nsems; | ||
51 | }; | ||
52 | |||
53 | struct compat_ipc_kludge { | 42 | struct compat_ipc_kludge { |
54 | compat_uptr_t msgp; | 43 | compat_uptr_t msgp; |
55 | compat_long_t msgtyp; | 44 | compat_long_t msgtyp; |
56 | }; | 45 | }; |
57 | 46 | ||
58 | static inline int __compat_ipc_parse_version(int *cmd) | 47 | int get_compat_ipc64_perm(struct ipc64_perm *to, |
59 | { | 48 | struct compat_ipc64_perm __user *from) |
60 | #ifdef CONFIG_ARCH_WANT_COMPAT_IPC_PARSE_VERSION | ||
61 | int version = *cmd & IPC_64; | ||
62 | |||
63 | /* this is tricky: architectures that have support for the old | ||
64 | * ipc structures in 64 bit binaries need to have IPC_64 set | ||
65 | * in cmd, the others need to have it cleared */ | ||
66 | #ifndef ipc_parse_version | ||
67 | *cmd |= IPC_64; | ||
68 | #else | ||
69 | *cmd &= ~IPC_64; | ||
70 | #endif | ||
71 | return version; | ||
72 | #else | ||
73 | /* With the asm-generic APIs, we always use the 64-bit versions. */ | ||
74 | return IPC_64; | ||
75 | #endif | ||
76 | } | ||
77 | |||
78 | static inline int __get_compat_ipc64_perm(struct ipc64_perm *p64, | ||
79 | struct compat_ipc64_perm __user *up64) | ||
80 | { | ||
81 | int err; | ||
82 | |||
83 | err = __get_user(p64->uid, &up64->uid); | ||
84 | err |= __get_user(p64->gid, &up64->gid); | ||
85 | err |= __get_user(p64->mode, &up64->mode); | ||
86 | return err; | ||
87 | } | ||
88 | |||
89 | static inline int __get_compat_ipc_perm(struct ipc64_perm *p, | ||
90 | struct compat_ipc_perm __user *up) | ||
91 | { | ||
92 | int err; | ||
93 | |||
94 | err = __get_user(p->uid, &up->uid); | ||
95 | err |= __get_user(p->gid, &up->gid); | ||
96 | err |= __get_user(p->mode, &up->mode); | ||
97 | return err; | ||
98 | } | ||
99 | |||
100 | static inline int __put_compat_ipc64_perm(struct ipc64_perm *p64, | ||
101 | struct compat_ipc64_perm __user *up64) | ||
102 | { | ||
103 | int err; | ||
104 | |||
105 | err = __put_user(p64->key, &up64->key); | ||
106 | err |= __put_user(p64->uid, &up64->uid); | ||
107 | err |= __put_user(p64->gid, &up64->gid); | ||
108 | err |= __put_user(p64->cuid, &up64->cuid); | ||
109 | err |= __put_user(p64->cgid, &up64->cgid); | ||
110 | err |= __put_user(p64->mode, &up64->mode); | ||
111 | err |= __put_user(p64->seq, &up64->seq); | ||
112 | return err; | ||
113 | } | ||
114 | |||
115 | static inline int __put_compat_ipc_perm(struct ipc64_perm *p, | ||
116 | struct compat_ipc_perm __user *uip) | ||
117 | { | ||
118 | int err; | ||
119 | __compat_uid_t u; | ||
120 | __compat_gid_t g; | ||
121 | |||
122 | err = __put_user(p->key, &uip->key); | ||
123 | SET_UID(u, p->uid); | ||
124 | err |= __put_user(u, &uip->uid); | ||
125 | SET_GID(g, p->gid); | ||
126 | err |= __put_user(g, &uip->gid); | ||
127 | SET_UID(u, p->cuid); | ||
128 | err |= __put_user(u, &uip->cuid); | ||
129 | SET_GID(g, p->cgid); | ||
130 | err |= __put_user(g, &uip->cgid); | ||
131 | err |= __put_user(p->mode, &uip->mode); | ||
132 | err |= __put_user(p->seq, &uip->seq); | ||
133 | return err; | ||
134 | } | ||
135 | |||
136 | static inline int get_compat_semid64_ds(struct semid64_ds *sem64, | ||
137 | struct compat_semid64_ds __user *up64) | ||
138 | { | 49 | { |
139 | if (!access_ok(VERIFY_READ, up64, sizeof(*up64))) | 50 | struct compat_ipc64_perm v; |
51 | if (copy_from_user(&v, from, sizeof(v))) | ||
140 | return -EFAULT; | 52 | return -EFAULT; |
141 | return __get_compat_ipc64_perm(&sem64->sem_perm, &up64->sem_perm); | 53 | to->uid = v.uid; |
54 | to->gid = v.gid; | ||
55 | to->mode = v.mode; | ||
56 | return 0; | ||
142 | } | 57 | } |
143 | 58 | ||
144 | static inline int get_compat_semid_ds(struct semid64_ds *s, | 59 | int get_compat_ipc_perm(struct ipc64_perm *to, |
145 | struct compat_semid_ds __user *up) | 60 | struct compat_ipc_perm __user *from) |
146 | { | 61 | { |
147 | if (!access_ok(VERIFY_READ, up, sizeof(*up))) | 62 | struct compat_ipc_perm v; |
63 | if (copy_from_user(&v, from, sizeof(v))) | ||
148 | return -EFAULT; | 64 | return -EFAULT; |
149 | return __get_compat_ipc_perm(&s->sem_perm, &up->sem_perm); | 65 | to->uid = v.uid; |
66 | to->gid = v.gid; | ||
67 | to->mode = v.mode; | ||
68 | return 0; | ||
150 | } | 69 | } |
151 | 70 | ||
152 | static inline int put_compat_semid64_ds(struct semid64_ds *sem64, | 71 | void to_compat_ipc64_perm(struct compat_ipc64_perm *to, struct ipc64_perm *from) |
153 | struct compat_semid64_ds __user *up64) | ||
154 | { | 72 | { |
155 | int err; | 73 | to->key = from->key; |
156 | 74 | to->uid = from->uid; | |
157 | if (!access_ok(VERIFY_WRITE, up64, sizeof(*up64))) | 75 | to->gid = from->gid; |
158 | return -EFAULT; | 76 | to->cuid = from->cuid; |
159 | err = __put_compat_ipc64_perm(&sem64->sem_perm, &up64->sem_perm); | 77 | to->cgid = from->cgid; |
160 | err |= __put_user(sem64->sem_otime, &up64->sem_otime); | 78 | to->mode = from->mode; |
161 | err |= __put_user(sem64->sem_ctime, &up64->sem_ctime); | 79 | to->seq = from->seq; |
162 | err |= __put_user(sem64->sem_nsems, &up64->sem_nsems); | ||
163 | return err; | ||
164 | } | 80 | } |
165 | 81 | ||
166 | static inline int put_compat_semid_ds(struct semid64_ds *s, | 82 | void to_compat_ipc_perm(struct compat_ipc_perm *to, struct ipc64_perm *from) |
167 | struct compat_semid_ds __user *up) | ||
168 | { | 83 | { |
169 | int err; | 84 | to->key = from->key; |
170 | 85 | SET_UID(to->uid, from->uid); | |
171 | if (!access_ok(VERIFY_WRITE, up, sizeof(*up))) | 86 | SET_GID(to->gid, from->gid); |
172 | return -EFAULT; | 87 | SET_UID(to->cuid, from->cuid); |
173 | err = __put_compat_ipc_perm(&s->sem_perm, &up->sem_perm); | 88 | SET_GID(to->cgid, from->cgid); |
174 | err |= __put_user(s->sem_otime, &up->sem_otime); | 89 | to->mode = from->mode; |
175 | err |= __put_user(s->sem_ctime, &up->sem_ctime); | 90 | to->seq = from->seq; |
176 | err |= __put_user(s->sem_nsems, &up->sem_nsems); | ||
177 | return err; | ||
178 | } | ||
179 | |||
180 | static long do_compat_semctl(int first, int second, int third, u32 pad) | ||
181 | { | ||
182 | unsigned long fourth; | ||
183 | int err, err2; | ||
184 | struct semid64_ds sem64; | ||
185 | struct semid64_ds __user *up64; | ||
186 | int version = __compat_ipc_parse_version(&third); | ||
187 | |||
188 | memset(&sem64, 0, sizeof(sem64)); | ||
189 | |||
190 | if ((third & (~IPC_64)) == SETVAL) | ||
191 | #ifdef __BIG_ENDIAN | ||
192 | fourth = (unsigned long)pad << 32; | ||
193 | #else | ||
194 | fourth = pad; | ||
195 | #endif | ||
196 | else | ||
197 | fourth = (unsigned long)compat_ptr(pad); | ||
198 | switch (third & (~IPC_64)) { | ||
199 | case IPC_INFO: | ||
200 | case IPC_RMID: | ||
201 | case SEM_INFO: | ||
202 | case GETVAL: | ||
203 | case GETPID: | ||
204 | case GETNCNT: | ||
205 | case GETZCNT: | ||
206 | case GETALL: | ||
207 | case SETVAL: | ||
208 | case SETALL: | ||
209 | err = sys_semctl(first, second, third, fourth); | ||
210 | break; | ||
211 | |||
212 | case IPC_STAT: | ||
213 | case SEM_STAT: | ||
214 | up64 = compat_alloc_user_space(sizeof(sem64)); | ||
215 | fourth = (unsigned long)up64; | ||
216 | err = sys_semctl(first, second, third, fourth); | ||
217 | if (err < 0) | ||
218 | break; | ||
219 | if (copy_from_user(&sem64, up64, sizeof(sem64))) | ||
220 | err2 = -EFAULT; | ||
221 | else if (version == IPC_64) | ||
222 | err2 = put_compat_semid64_ds(&sem64, compat_ptr(pad)); | ||
223 | else | ||
224 | err2 = put_compat_semid_ds(&sem64, compat_ptr(pad)); | ||
225 | if (err2) | ||
226 | err = -EFAULT; | ||
227 | break; | ||
228 | |||
229 | case IPC_SET: | ||
230 | if (version == IPC_64) | ||
231 | err = get_compat_semid64_ds(&sem64, compat_ptr(pad)); | ||
232 | else | ||
233 | err = get_compat_semid_ds(&sem64, compat_ptr(pad)); | ||
234 | |||
235 | up64 = compat_alloc_user_space(sizeof(sem64)); | ||
236 | if (copy_to_user(up64, &sem64, sizeof(sem64))) | ||
237 | err = -EFAULT; | ||
238 | if (err) | ||
239 | break; | ||
240 | |||
241 | fourth = (unsigned long)up64; | ||
242 | err = sys_semctl(first, second, third, fourth); | ||
243 | break; | ||
244 | |||
245 | default: | ||
246 | err = -EINVAL; | ||
247 | break; | ||
248 | } | ||
249 | return err; | ||
250 | } | 91 | } |
251 | 92 | ||
252 | static long compat_do_msg_fill(void __user *dest, struct msg_msg *msg, size_t bufsz) | 93 | static long compat_do_msg_fill(void __user *dest, struct msg_msg *msg, size_t bufsz) |
@@ -291,7 +132,7 @@ COMPAT_SYSCALL_DEFINE6(ipc, u32, call, int, first, int, second, | |||
291 | return -EINVAL; | 132 | return -EINVAL; |
292 | if (get_user(pad, (u32 __user *) compat_ptr(ptr))) | 133 | if (get_user(pad, (u32 __user *) compat_ptr(ptr))) |
293 | return -EFAULT; | 134 | return -EFAULT; |
294 | return do_compat_semctl(first, second, third, pad); | 135 | return compat_sys_semctl(first, second, third, pad); |
295 | 136 | ||
296 | case MSGSND: { | 137 | case MSGSND: { |
297 | struct compat_msgbuf __user *up = compat_ptr(ptr); | 138 | struct compat_msgbuf __user *up = compat_ptr(ptr); |
@@ -352,11 +193,6 @@ COMPAT_SYSCALL_DEFINE6(ipc, u32, call, int, first, int, second, | |||
352 | } | 193 | } |
353 | #endif | 194 | #endif |
354 | 195 | ||
355 | COMPAT_SYSCALL_DEFINE4(semctl, int, semid, int, semnum, int, cmd, int, arg) | ||
356 | { | ||
357 | return do_compat_semctl(semid, semnum, cmd, arg); | ||
358 | } | ||
359 | |||
360 | COMPAT_SYSCALL_DEFINE4(msgsnd, int, msqid, compat_uptr_t, msgp, | 196 | COMPAT_SYSCALL_DEFINE4(msgsnd, int, msqid, compat_uptr_t, msgp, |
361 | compat_ssize_t, msgsz, int, msgflg) | 197 | compat_ssize_t, msgsz, int, msgflg) |
362 | { | 198 | { |
@@ -1617,6 +1617,100 @@ SYSCALL_DEFINE4(semctl, int, semid, int, semnum, int, cmd, unsigned long, arg) | |||
1617 | } | 1617 | } |
1618 | } | 1618 | } |
1619 | 1619 | ||
1620 | #ifdef CONFIG_COMPAT | ||
1621 | |||
1622 | struct compat_semid_ds { | ||
1623 | struct compat_ipc_perm sem_perm; | ||
1624 | compat_time_t sem_otime; | ||
1625 | compat_time_t sem_ctime; | ||
1626 | compat_uptr_t sem_base; | ||
1627 | compat_uptr_t sem_pending; | ||
1628 | compat_uptr_t sem_pending_last; | ||
1629 | compat_uptr_t undo; | ||
1630 | unsigned short sem_nsems; | ||
1631 | }; | ||
1632 | |||
1633 | static int copy_compat_semid_from_user(struct semid64_ds *out, void __user *buf, | ||
1634 | int version) | ||
1635 | { | ||
1636 | memset(out, 0, sizeof(*out)); | ||
1637 | if (version == IPC_64) { | ||
1638 | struct compat_semid64_ds *p = buf; | ||
1639 | return get_compat_ipc64_perm(&out->sem_perm, &p->sem_perm); | ||
1640 | } else { | ||
1641 | struct compat_semid_ds *p = buf; | ||
1642 | return get_compat_ipc_perm(&out->sem_perm, &p->sem_perm); | ||
1643 | } | ||
1644 | } | ||
1645 | |||
1646 | static int copy_compat_semid_to_user(void __user *buf, struct semid64_ds *in, | ||
1647 | int version) | ||
1648 | { | ||
1649 | if (version == IPC_64) { | ||
1650 | struct compat_semid64_ds v; | ||
1651 | memset(&v, 0, sizeof(v)); | ||
1652 | to_compat_ipc64_perm(&v.sem_perm, &in->sem_perm); | ||
1653 | v.sem_otime = in->sem_otime; | ||
1654 | v.sem_ctime = in->sem_ctime; | ||
1655 | v.sem_nsems = in->sem_nsems; | ||
1656 | return copy_to_user(buf, &v, sizeof(v)); | ||
1657 | } else { | ||
1658 | struct compat_semid_ds v; | ||
1659 | memset(&v, 0, sizeof(v)); | ||
1660 | to_compat_ipc_perm(&v.sem_perm, &in->sem_perm); | ||
1661 | v.sem_otime = in->sem_otime; | ||
1662 | v.sem_ctime = in->sem_ctime; | ||
1663 | v.sem_nsems = in->sem_nsems; | ||
1664 | return copy_to_user(buf, &v, sizeof(v)); | ||
1665 | } | ||
1666 | } | ||
1667 | |||
1668 | COMPAT_SYSCALL_DEFINE4(semctl, int, semid, int, semnum, int, cmd, int, arg) | ||
1669 | { | ||
1670 | void __user *p = compat_ptr(arg); | ||
1671 | struct ipc_namespace *ns; | ||
1672 | struct semid64_ds semid64; | ||
1673 | int version = compat_ipc_parse_version(&cmd); | ||
1674 | int err; | ||
1675 | |||
1676 | ns = current->nsproxy->ipc_ns; | ||
1677 | |||
1678 | if (semid < 0) | ||
1679 | return -EINVAL; | ||
1680 | |||
1681 | switch (cmd & (~IPC_64)) { | ||
1682 | case IPC_INFO: | ||
1683 | case SEM_INFO: | ||
1684 | return semctl_info(ns, semid, cmd, p); | ||
1685 | case IPC_STAT: | ||
1686 | case SEM_STAT: | ||
1687 | err = semctl_stat(ns, semid, cmd, &semid64); | ||
1688 | if (err < 0) | ||
1689 | return err; | ||
1690 | if (copy_compat_semid_to_user(p, &semid64, version)) | ||
1691 | err = -EFAULT; | ||
1692 | return err; | ||
1693 | case GETVAL: | ||
1694 | case GETPID: | ||
1695 | case GETNCNT: | ||
1696 | case GETZCNT: | ||
1697 | case GETALL: | ||
1698 | case SETALL: | ||
1699 | return semctl_main(ns, semid, semnum, cmd, p); | ||
1700 | case SETVAL: | ||
1701 | return semctl_setval(ns, semid, semnum, arg); | ||
1702 | case IPC_SET: | ||
1703 | if (copy_compat_semid_from_user(&semid64, p, version)) | ||
1704 | return -EFAULT; | ||
1705 | /* fallthru */ | ||
1706 | case IPC_RMID: | ||
1707 | return semctl_down(ns, semid, cmd, &semid64); | ||
1708 | default: | ||
1709 | return -EINVAL; | ||
1710 | } | ||
1711 | } | ||
1712 | #endif | ||
1713 | |||
1620 | /* If the task doesn't already have a undo_list, then allocate one | 1714 | /* If the task doesn't already have a undo_list, then allocate one |
1621 | * here. We guarantee there is only one thread using this undo list, | 1715 | * here. We guarantee there is only one thread using this undo list, |
1622 | * and current is THE ONE | 1716 | * and current is THE ONE |
diff --git a/ipc/util.h b/ipc/util.h index 3a3dfe137bee..c7b7a5ff1f0b 100644 --- a/ipc/util.h +++ b/ipc/util.h | |||
@@ -204,6 +204,12 @@ struct compat_ipc_perm { | |||
204 | unsigned short seq; | 204 | unsigned short seq; |
205 | }; | 205 | }; |
206 | 206 | ||
207 | void to_compat_ipc_perm(struct compat_ipc_perm *, struct ipc64_perm *); | ||
208 | void to_compat_ipc64_perm(struct compat_ipc64_perm *, struct ipc64_perm *); | ||
209 | int get_compat_ipc_perm(struct ipc64_perm *, struct compat_ipc_perm __user *); | ||
210 | int get_compat_ipc64_perm(struct ipc64_perm *, | ||
211 | struct compat_ipc64_perm __user *); | ||
212 | |||
207 | static inline int compat_ipc_parse_version(int *cmd) | 213 | static inline int compat_ipc_parse_version(int *cmd) |
208 | { | 214 | { |
209 | #ifdef CONFIG_ARCH_WANT_COMPAT_IPC_PARSE_VERSION | 215 | #ifdef CONFIG_ARCH_WANT_COMPAT_IPC_PARSE_VERSION |