semctl(): separate all layout-dependent copyin/copyout
[linux-2.6-block.git] / ipc / compat.c
CommitLineData
1da177e4
LT
1/*
2 * 32 bit compatibility code for System V IPC
3 *
4 * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
5 * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
6 * Copyright (C) 1999 Arun Sharma <arun.sharma@intel.com>
7 * Copyright (C) 2000 VA Linux Co
8 * Copyright (C) 2000 Don Dugger <n0ano@valinux.com>
9 * Copyright (C) 2000 Hewlett-Packard Co.
10 * Copyright (C) 2000 David Mosberger-Tang <davidm@hpl.hp.com>
11 * Copyright (C) 2000 Gerhard Tonn (ton@de.ibm.com)
12 * Copyright (C) 2000-2002 Andi Kleen, SuSE Labs (x86-64 port)
13 * Copyright (C) 2000 Silicon Graphics, Inc.
14 * Copyright (C) 2001 IBM
15 * Copyright (C) 2004 IBM Deutschland Entwicklung GmbH, IBM Corporation
16 * Copyright (C) 2004 Arnd Bergmann (arnd@arndb.de)
17 *
18 * This code is collected from the versions for sparc64, mips64, s390x, ia64,
19 * ppc64 and x86_64, all of which are based on the original sparc64 version
20 * by Jakub Jelinek.
21 *
22 */
23#include <linux/compat.h>
1da177e4
LT
24#include <linux/errno.h>
25#include <linux/highuid.h>
26#include <linux/init.h>
27#include <linux/msg.h>
28#include <linux/shm.h>
1da177e4 29#include <linux/syscalls.h>
48b25c43 30#include <linux/ptrace.h>
1da177e4 31
5f921ae9 32#include <linux/mutex.h>
7153e402 33#include <linux/uaccess.h>
1da177e4
LT
34
35#include "util.h"
36
37struct compat_msgbuf {
38 compat_long_t mtype;
39 char mtext[1];
40};
41
1da177e4
LT
42struct 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
1da177e4
LT
53struct compat_ipc_kludge {
54 compat_uptr_t msgp;
55 compat_long_t msgtyp;
56};
57
553f770e 58static inline int __compat_ipc_parse_version(int *cmd)
1da177e4 59{
c1d7e01d 60#ifdef CONFIG_ARCH_WANT_COMPAT_IPC_PARSE_VERSION
1da177e4
LT
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;
48b25c43
CM
72#else
73 /* With the asm-generic APIs, we always use the 64-bit versions. */
74 return IPC_64;
75#endif
1da177e4
LT
76}
77
78static 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
89static 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
100static 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
115static inline int __put_compat_ipc_perm(struct ipc64_perm *p,
0d5e7580 116 struct compat_ipc_perm __user *uip)
1da177e4
LT
117{
118 int err;
202e5979
SR
119 __compat_uid_t u;
120 __compat_gid_t g;
1da177e4 121
0d5e7580 122 err = __put_user(p->key, &uip->key);
1da177e4 123 SET_UID(u, p->uid);
0d5e7580 124 err |= __put_user(u, &uip->uid);
1da177e4 125 SET_GID(g, p->gid);
0d5e7580 126 err |= __put_user(g, &uip->gid);
1da177e4 127 SET_UID(u, p->cuid);
0d5e7580 128 err |= __put_user(u, &uip->cuid);
1da177e4 129 SET_GID(g, p->cgid);
0d5e7580
MR
130 err |= __put_user(g, &uip->cgid);
131 err |= __put_user(p->mode, &uip->mode);
132 err |= __put_user(p->seq, &uip->seq);
1da177e4
LT
133 return err;
134}
135
0d5e7580 136static inline int get_compat_semid64_ds(struct semid64_ds *sem64,
1da177e4
LT
137 struct compat_semid64_ds __user *up64)
138{
239521f3 139 if (!access_ok(VERIFY_READ, up64, sizeof(*up64)))
1da177e4 140 return -EFAULT;
0d5e7580 141 return __get_compat_ipc64_perm(&sem64->sem_perm, &up64->sem_perm);
1da177e4
LT
142}
143
144static inline int get_compat_semid_ds(struct semid64_ds *s,
145 struct compat_semid_ds __user *up)
146{
239521f3 147 if (!access_ok(VERIFY_READ, up, sizeof(*up)))
1da177e4
LT
148 return -EFAULT;
149 return __get_compat_ipc_perm(&s->sem_perm, &up->sem_perm);
150}
151
0d5e7580 152static inline int put_compat_semid64_ds(struct semid64_ds *sem64,
1da177e4
LT
153 struct compat_semid64_ds __user *up64)
154{
155 int err;
156
239521f3 157 if (!access_ok(VERIFY_WRITE, up64, sizeof(*up64)))
1da177e4 158 return -EFAULT;
0d5e7580
MR
159 err = __put_compat_ipc64_perm(&sem64->sem_perm, &up64->sem_perm);
160 err |= __put_user(sem64->sem_otime, &up64->sem_otime);
161 err |= __put_user(sem64->sem_ctime, &up64->sem_ctime);
162 err |= __put_user(sem64->sem_nsems, &up64->sem_nsems);
1da177e4
LT
163 return err;
164}
165
166static inline int put_compat_semid_ds(struct semid64_ds *s,
167 struct compat_semid_ds __user *up)
168{
169 int err;
170
239521f3 171 if (!access_ok(VERIFY_WRITE, up, sizeof(*up)))
d57d9731 172 return -EFAULT;
1da177e4
LT
173 err = __put_compat_ipc_perm(&s->sem_perm, &up->sem_perm);
174 err |= __put_user(s->sem_otime, &up->sem_otime);
175 err |= __put_user(s->sem_ctime, &up->sem_ctime);
176 err |= __put_user(s->sem_nsems, &up->sem_nsems);
177 return err;
178}
179
48b25c43 180static long do_compat_semctl(int first, int second, int third, u32 pad)
1da177e4 181{
e1fd1f49 182 unsigned long fourth;
1da177e4 183 int err, err2;
0d5e7580 184 struct semid64_ds sem64;
1da177e4 185 struct semid64_ds __user *up64;
553f770e 186 int version = __compat_ipc_parse_version(&third);
1da177e4 187
0d5e7580 188 memset(&sem64, 0, sizeof(sem64));
03145beb 189
1da177e4 190 if ((third & (~IPC_64)) == SETVAL)
e1fd1f49
AV
191#ifdef __BIG_ENDIAN
192 fourth = (unsigned long)pad << 32;
193#else
194 fourth = pad;
195#endif
1da177e4 196 else
e1fd1f49 197 fourth = (unsigned long)compat_ptr(pad);
1da177e4
LT
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:
0d5e7580 214 up64 = compat_alloc_user_space(sizeof(sem64));
e1fd1f49 215 fourth = (unsigned long)up64;
1da177e4
LT
216 err = sys_semctl(first, second, third, fourth);
217 if (err < 0)
218 break;
0d5e7580 219 if (copy_from_user(&sem64, up64, sizeof(sem64)))
1da177e4
LT
220 err2 = -EFAULT;
221 else if (version == IPC_64)
0d5e7580 222 err2 = put_compat_semid64_ds(&sem64, compat_ptr(pad));
1da177e4 223 else
0d5e7580 224 err2 = put_compat_semid_ds(&sem64, compat_ptr(pad));
1da177e4
LT
225 if (err2)
226 err = -EFAULT;
227 break;
228
229 case IPC_SET:
3ab08fe2 230 if (version == IPC_64)
0d5e7580 231 err = get_compat_semid64_ds(&sem64, compat_ptr(pad));
3ab08fe2 232 else
0d5e7580 233 err = get_compat_semid_ds(&sem64, compat_ptr(pad));
3ab08fe2 234
0d5e7580
MR
235 up64 = compat_alloc_user_space(sizeof(sem64));
236 if (copy_to_user(up64, &sem64, sizeof(sem64)))
1da177e4
LT
237 err = -EFAULT;
238 if (err)
239 break;
240
e1fd1f49 241 fourth = (unsigned long)up64;
1da177e4
LT
242 err = sys_semctl(first, second, third, fourth);
243 break;
244
245 default:
246 err = -EINVAL;
247 break;
248 }
249 return err;
250}
251
0e65a81b 252static long compat_do_msg_fill(void __user *dest, struct msg_msg *msg, size_t bufsz)
f9dd87f4
SK
253{
254 struct compat_msgbuf __user *msgp = dest;
255 size_t msgsz;
256
257 if (put_user(msg->m_type, &msgp->mtype))
258 return -EFAULT;
259
260 msgsz = (bufsz > msg->m_ts) ? msg->m_ts : bufsz;
261 if (store_msg(msgp->mtext, msg, msgsz))
262 return -EFAULT;
263 return msgsz;
264}
265
0e65a81b
AV
266#ifndef COMPAT_SHMLBA
267#define COMPAT_SHMLBA SHMLBA
268#endif
56e41d3c 269
0e65a81b 270#ifdef CONFIG_ARCH_WANT_OLD_COMPAT_IPC
56e41d3c
AV
271COMPAT_SYSCALL_DEFINE6(ipc, u32, call, int, first, int, second,
272 u32, third, compat_uptr_t, ptr, u32, fifth)
273{
274 int version;
0e65a81b 275 u32 pad;
56e41d3c
AV
276
277 version = call >> 16; /* hack for backward compatibility */
278 call &= 0xffff;
279
280 switch (call) {
281 case SEMOP:
282 /* struct sembuf is the same on 32 and 64bit :)) */
283 return sys_semtimedop(first, compat_ptr(ptr), second, NULL);
284 case SEMTIMEDOP:
285 return compat_sys_semtimedop(first, compat_ptr(ptr), second,
286 compat_ptr(fifth));
287 case SEMGET:
288 return sys_semget(first, second, third);
289 case SEMCTL:
0e65a81b
AV
290 if (!ptr)
291 return -EINVAL;
292 if (get_user(pad, (u32 __user *) compat_ptr(ptr)))
293 return -EFAULT;
294 return do_compat_semctl(first, second, third, pad);
295
296 case MSGSND: {
297 struct compat_msgbuf __user *up = compat_ptr(ptr);
298 compat_long_t type;
299
300 if (first < 0 || second < 0)
301 return -EINVAL;
56e41d3c 302
0e65a81b
AV
303 if (get_user(type, &up->mtype))
304 return -EFAULT;
305
306 return do_msgsnd(first, type, up->mtext, second, third);
307 }
308 case MSGRCV: {
309 void __user *uptr = compat_ptr(ptr);
310
311 if (first < 0 || second < 0)
312 return -EINVAL;
313
314 if (!version) {
315 struct compat_ipc_kludge ipck;
316 if (!uptr)
317 return -EINVAL;
239521f3 318 if (copy_from_user(&ipck, uptr, sizeof(ipck)))
0e65a81b
AV
319 return -EFAULT;
320 uptr = compat_ptr(ipck.msgp);
321 fifth = ipck.msgtyp;
322 }
e7ca2552 323 return do_msgrcv(first, uptr, second, (s32)fifth, third,
0e65a81b
AV
324 compat_do_msg_fill);
325 }
56e41d3c
AV
326 case MSGGET:
327 return sys_msgget(first, second);
328 case MSGCTL:
329 return compat_sys_msgctl(first, second, compat_ptr(ptr));
330
0e65a81b
AV
331 case SHMAT: {
332 int err;
333 unsigned long raddr;
334
335 if (version == 1)
336 return -EINVAL;
337 err = do_shmat(first, compat_ptr(ptr), second, &raddr,
338 COMPAT_SHMLBA);
339 if (err < 0)
340 return err;
341 return put_user(raddr, (compat_ulong_t *)compat_ptr(third));
342 }
56e41d3c
AV
343 case SHMDT:
344 return sys_shmdt(compat_ptr(ptr));
345 case SHMGET:
346 return sys_shmget(first, (unsigned)second, third);
347 case SHMCTL:
348 return compat_sys_shmctl(first, second, compat_ptr(ptr));
349 }
350
351 return -ENOSYS;
352}
0e65a81b
AV
353#endif
354
355COMPAT_SYSCALL_DEFINE4(semctl, int, semid, int, semnum, int, cmd, int, arg)
48b25c43
CM
356{
357 return do_compat_semctl(semid, semnum, cmd, arg);
358}
359
0e65a81b
AV
360COMPAT_SYSCALL_DEFINE4(msgsnd, int, msqid, compat_uptr_t, msgp,
361 compat_ssize_t, msgsz, int, msgflg)
48b25c43 362{
0e65a81b 363 struct compat_msgbuf __user *up = compat_ptr(msgp);
48b25c43
CM
364 compat_long_t mtype;
365
0e65a81b 366 if (get_user(mtype, &up->mtype))
48b25c43 367 return -EFAULT;
0e65a81b 368 return do_msgsnd(msqid, mtype, up->mtext, (ssize_t)msgsz, msgflg);
48b25c43
CM
369}
370
0e65a81b 371COMPAT_SYSCALL_DEFINE5(msgrcv, int, msqid, compat_uptr_t, msgp,
291fdb0b 372 compat_ssize_t, msgsz, compat_long_t, msgtyp, int, msgflg)
48b25c43 373{
291fdb0b 374 return do_msgrcv(msqid, compat_ptr(msgp), (ssize_t)msgsz, (long)msgtyp,
0e65a81b 375 msgflg, compat_do_msg_fill);
48b25c43 376}
1da177e4 377
0e65a81b 378COMPAT_SYSCALL_DEFINE3(shmat, int, shmid, compat_uptr_t, shmaddr, int, shmflg)
48b25c43
CM
379{
380 unsigned long ret;
381 long err;
382
079a96ae 383 err = do_shmat(shmid, compat_ptr(shmaddr), shmflg, &ret, COMPAT_SHMLBA);
48b25c43
CM
384 if (err)
385 return err;
386 force_successful_syscall_return();
387 return (long)ret;
388}
1da177e4 389
5d70a596
HC
390COMPAT_SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsems,
391 unsigned, nsops,
392 const struct compat_timespec __user *, timeout)
1da177e4 393{
81993e81
PA
394 struct timespec __user *ts64;
395 if (compat_convert_timespec(&ts64, timeout))
396 return -EFAULT;
1da177e4
LT
397 return sys_semtimedop(semid, tsems, nsops, ts64);
398}