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> 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> 29#include <linux/slab.h> 30#include <linux/syscalls.h> 31 32#include <linux/mutex.h> 33#include <asm/uaccess.h> 34 35#include "util.h" 36 37struct compat_msgbuf { 38 compat_long_t mtype; 39 char mtext[1]; 40}; 41 42struct compat_ipc_perm { 43 key_t key; 44 __compat_uid_t uid; 45 __compat_gid_t gid; 46 __compat_uid_t cuid; 47 __compat_gid_t cgid; 48 compat_mode_t mode; 49 unsigned short seq; 50}; 51 52struct compat_semid_ds { 53 struct compat_ipc_perm sem_perm; 54 compat_time_t sem_otime; 55 compat_time_t sem_ctime; 56 compat_uptr_t sem_base; 57 compat_uptr_t sem_pending; 58 compat_uptr_t sem_pending_last; 59 compat_uptr_t undo; 60 unsigned short sem_nsems; 61}; 62 63struct compat_msqid_ds { 64 struct compat_ipc_perm msg_perm; 65 compat_uptr_t msg_first; 66 compat_uptr_t msg_last; 67 compat_time_t msg_stime; 68 compat_time_t msg_rtime; 69 compat_time_t msg_ctime; 70 compat_ulong_t msg_lcbytes; 71 compat_ulong_t msg_lqbytes; 72 unsigned short msg_cbytes; 73 unsigned short msg_qnum; 74 unsigned short msg_qbytes; 75 compat_ipc_pid_t msg_lspid; 76 compat_ipc_pid_t msg_lrpid; 77}; 78 79struct compat_shmid_ds { 80 struct compat_ipc_perm shm_perm; 81 int shm_segsz; 82 compat_time_t shm_atime; 83 compat_time_t shm_dtime; 84 compat_time_t shm_ctime; 85 compat_ipc_pid_t shm_cpid; 86 compat_ipc_pid_t shm_lpid; 87 unsigned short shm_nattch; 88 unsigned short shm_unused; 89 compat_uptr_t shm_unused2; 90 compat_uptr_t shm_unused3; 91}; 92 93struct compat_ipc_kludge { 94 compat_uptr_t msgp; 95 compat_long_t msgtyp; 96}; 97 98struct compat_shminfo64 { 99 compat_ulong_t shmmax; 100 compat_ulong_t shmmin; 101 compat_ulong_t shmmni; 102 compat_ulong_t shmseg; 103 compat_ulong_t shmall; 104 compat_ulong_t __unused1; 105 compat_ulong_t __unused2; 106 compat_ulong_t __unused3; 107 compat_ulong_t __unused4; 108}; 109 110struct compat_shm_info { 111 compat_int_t used_ids; 112 compat_ulong_t shm_tot, shm_rss, shm_swp; 113 compat_ulong_t swap_attempts, swap_successes; 114}; 115 116extern int sem_ctls[]; 117#define sc_semopm (sem_ctls[2]) 118 119static inline int compat_ipc_parse_version(int *cmd) 120{ 121 int version = *cmd & IPC_64; 122 123 /* this is tricky: architectures that have support for the old 124 * ipc structures in 64 bit binaries need to have IPC_64 set 125 * in cmd, the others need to have it cleared */ 126#ifndef ipc_parse_version 127 *cmd |= IPC_64; 128#else 129 *cmd &= ~IPC_64; 130#endif 131 return version; 132} 133 134static inline int __get_compat_ipc64_perm(struct ipc64_perm *p64, 135 struct compat_ipc64_perm __user *up64) 136{ 137 int err; 138 139 err = __get_user(p64->uid, &up64->uid); 140 err |= __get_user(p64->gid, &up64->gid); 141 err |= __get_user(p64->mode, &up64->mode); 142 return err; 143} 144 145static inline int __get_compat_ipc_perm(struct ipc64_perm *p, 146 struct compat_ipc_perm __user *up) 147{ 148 int err; 149 150 err = __get_user(p->uid, &up->uid); 151 err |= __get_user(p->gid, &up->gid); 152 err |= __get_user(p->mode, &up->mode); 153 return err; 154} 155 156static inline int __put_compat_ipc64_perm(struct ipc64_perm *p64, 157 struct compat_ipc64_perm __user *up64) 158{ 159 int err; 160 161 err = __put_user(p64->key, &up64->key); 162 err |= __put_user(p64->uid, &up64->uid); 163 err |= __put_user(p64->gid, &up64->gid); 164 err |= __put_user(p64->cuid, &up64->cuid); 165 err |= __put_user(p64->cgid, &up64->cgid); 166 err |= __put_user(p64->mode, &up64->mode); 167 err |= __put_user(p64->seq, &up64->seq); 168 return err; 169} 170 171static inline int __put_compat_ipc_perm(struct ipc64_perm *p, 172 struct compat_ipc_perm __user *up) 173{ 174 int err; 175 __compat_uid_t u; 176 __compat_gid_t g; 177 178 err = __put_user(p->key, &up->key); 179 SET_UID(u, p->uid); 180 err |= __put_user(u, &up->uid); 181 SET_GID(g, p->gid); 182 err |= __put_user(g, &up->gid); 183 SET_UID(u, p->cuid); 184 err |= __put_user(u, &up->cuid); 185 SET_GID(g, p->cgid); 186 err |= __put_user(g, &up->cgid); 187 err |= __put_user(p->mode, &up->mode); 188 err |= __put_user(p->seq, &up->seq); 189 return err; 190} 191 192static inline int get_compat_semid64_ds(struct semid64_ds *s64, 193 struct compat_semid64_ds __user *up64) 194{ 195 if (!access_ok (VERIFY_READ, up64, sizeof(*up64))) 196 return -EFAULT; 197 return __get_compat_ipc64_perm(&s64->sem_perm, &up64->sem_perm); 198} 199 200static inline int get_compat_semid_ds(struct semid64_ds *s, 201 struct compat_semid_ds __user *up) 202{ 203 if (!access_ok (VERIFY_READ, up, sizeof(*up))) 204 return -EFAULT; 205 return __get_compat_ipc_perm(&s->sem_perm, &up->sem_perm); 206} 207 208static inline int put_compat_semid64_ds(struct semid64_ds *s64, 209 struct compat_semid64_ds __user *up64) 210{ 211 int err; 212 213 if (!access_ok (VERIFY_WRITE, up64, sizeof(*up64))) 214 return -EFAULT; 215 err = __put_compat_ipc64_perm(&s64->sem_perm, &up64->sem_perm); 216 err |= __put_user(s64->sem_otime, &up64->sem_otime); 217 err |= __put_user(s64->sem_ctime, &up64->sem_ctime); 218 err |= __put_user(s64->sem_nsems, &up64->sem_nsems); 219 return err; 220} 221 222static inline int put_compat_semid_ds(struct semid64_ds *s, 223 struct compat_semid_ds __user *up) 224{ 225 int err; 226 227 if (!access_ok (VERIFY_WRITE, up, sizeof(*up))) 228 return -EFAULT; 229 err = __put_compat_ipc_perm(&s->sem_perm, &up->sem_perm); 230 err |= __put_user(s->sem_otime, &up->sem_otime); 231 err |= __put_user(s->sem_ctime, &up->sem_ctime); 232 err |= __put_user(s->sem_nsems, &up->sem_nsems); 233 return err; 234} 235 236long compat_sys_semctl(int first, int second, int third, void __user *uptr) 237{ 238 union semun fourth; 239 u32 pad; 240 int err, err2; 241 struct semid64_ds s64; 242 struct semid64_ds __user *up64; 243 int version = compat_ipc_parse_version(&third); 244 245 if (!uptr) 246 return -EINVAL; 247 if (get_user(pad, (u32 __user *) uptr)) 248 return -EFAULT; 249 if ((third & (~IPC_64)) == SETVAL) 250 fourth.val = (int) pad; 251 else 252 fourth.__pad = compat_ptr(pad); 253 switch (third & (~IPC_64)) { 254 case IPC_INFO: 255 case IPC_RMID: 256 case SEM_INFO: 257 case GETVAL: 258 case GETPID: 259 case GETNCNT: 260 case GETZCNT: 261 case GETALL: 262 case SETVAL: 263 case SETALL: 264 err = sys_semctl(first, second, third, fourth); 265 break; 266 267 case IPC_STAT: 268 case SEM_STAT: 269 up64 = compat_alloc_user_space(sizeof(s64)); 270 fourth.__pad = up64; 271 err = sys_semctl(first, second, third, fourth); 272 if (err < 0) 273 break; 274 if (copy_from_user(&s64, up64, sizeof(s64))) 275 err2 = -EFAULT; 276 else if (version == IPC_64) 277 err2 = put_compat_semid64_ds(&s64, compat_ptr(pad)); 278 else 279 err2 = put_compat_semid_ds(&s64, compat_ptr(pad)); 280 if (err2) 281 err = -EFAULT; 282 break; 283 284 case IPC_SET: 285 if (version == IPC_64) { 286 err = get_compat_semid64_ds(&s64, compat_ptr(pad)); 287 } else { 288 err = get_compat_semid_ds(&s64, compat_ptr(pad)); 289 } 290 up64 = compat_alloc_user_space(sizeof(s64)); 291 if (copy_to_user(up64, &s64, sizeof(s64))) 292 err = -EFAULT; 293 if (err) 294 break; 295 296 fourth.__pad = up64; 297 err = sys_semctl(first, second, third, fourth); 298 break; 299 300 default: 301 err = -EINVAL; 302 break; 303 } 304 return err; 305} 306 307long compat_sys_msgsnd(int first, int second, int third, void __user *uptr) 308{ 309 struct compat_msgbuf __user *up = uptr; 310 long type; 311 312 if (first < 0) 313 return -EINVAL; 314 if (second < 0) 315 return -EINVAL; 316 317 if (get_user(type, &up->mtype)) 318 return -EFAULT; 319 320 return do_msgsnd(first, type, up->mtext, second, third); 321} 322 323long compat_sys_msgrcv(int first, int second, int msgtyp, int third, 324 int version, void __user *uptr) 325{ 326 struct compat_msgbuf __user *up; 327 long type; 328 int err; 329 330 if (first < 0) 331 return -EINVAL; 332 if (second < 0) 333 return -EINVAL; 334 335 if (!version) { 336 struct compat_ipc_kludge ipck; 337 err = -EINVAL; 338 if (!uptr) 339 goto out; 340 err = -EFAULT; 341 if (copy_from_user (&ipck, uptr, sizeof(ipck))) 342 goto out; 343 uptr = compat_ptr(ipck.msgp); 344 msgtyp = ipck.msgtyp; 345 } 346 up = uptr; 347 err = do_msgrcv(first, &type, up->mtext, second, msgtyp, third); 348 if (err < 0) 349 goto out; 350 if (put_user(type, &up->mtype)) 351 err = -EFAULT; 352out: 353 return err; 354} 355 356static inline int get_compat_msqid64(struct msqid64_ds *m64, 357 struct compat_msqid64_ds __user *up64) 358{ 359 int err; 360 361 if (!access_ok(VERIFY_READ, up64, sizeof(*up64))) 362 return -EFAULT; 363 err = __get_compat_ipc64_perm(&m64->msg_perm, &up64->msg_perm); 364 err |= __get_user(m64->msg_qbytes, &up64->msg_qbytes); 365 return err; 366} 367 368static inline int get_compat_msqid(struct msqid64_ds *m, 369 struct compat_msqid_ds __user *up) 370{ 371 int err; 372 373 if (!access_ok(VERIFY_READ, up, sizeof(*up))) 374 return -EFAULT; 375 err = __get_compat_ipc_perm(&m->msg_perm, &up->msg_perm); 376 err |= __get_user(m->msg_qbytes, &up->msg_qbytes); 377 return err; 378} 379 380static inline int put_compat_msqid64_ds(struct msqid64_ds *m64, 381 struct compat_msqid64_ds __user *up64) 382{ 383 int err; 384 385 if (!access_ok(VERIFY_WRITE, up64, sizeof(*up64))) 386 return -EFAULT; 387 err = __put_compat_ipc64_perm(&m64->msg_perm, &up64->msg_perm); 388 err |= __put_user(m64->msg_stime, &up64->msg_stime); 389 err |= __put_user(m64->msg_rtime, &up64->msg_rtime); 390 err |= __put_user(m64->msg_ctime, &up64->msg_ctime); 391 err |= __put_user(m64->msg_cbytes, &up64->msg_cbytes); 392 err |= __put_user(m64->msg_qnum, &up64->msg_qnum); 393 err |= __put_user(m64->msg_qbytes, &up64->msg_qbytes); 394 err |= __put_user(m64->msg_lspid, &up64->msg_lspid); 395 err |= __put_user(m64->msg_lrpid, &up64->msg_lrpid); 396 return err; 397} 398 399static inline int put_compat_msqid_ds(struct msqid64_ds *m, 400 struct compat_msqid_ds __user *up) 401{ 402 int err; 403 404 if (!access_ok(VERIFY_WRITE, up, sizeof(*up))) 405 return -EFAULT; 406 err = __put_compat_ipc_perm(&m->msg_perm, &up->msg_perm); 407 err |= __put_user(m->msg_stime, &up->msg_stime); 408 err |= __put_user(m->msg_rtime, &up->msg_rtime); 409 err |= __put_user(m->msg_ctime, &up->msg_ctime); 410 err |= __put_user(m->msg_cbytes, &up->msg_cbytes); 411 err |= __put_user(m->msg_qnum, &up->msg_qnum); 412 err |= __put_user(m->msg_qbytes, &up->msg_qbytes); 413 err |= __put_user(m->msg_lspid, &up->msg_lspid); 414 err |= __put_user(m->msg_lrpid, &up->msg_lrpid); 415 return err; 416} 417 418long compat_sys_msgctl(int first, int second, void __user *uptr) 419{ 420 int err, err2; 421 struct msqid64_ds m64; 422 int version = compat_ipc_parse_version(&second); 423 void __user *p; 424 425 switch (second & (~IPC_64)) { 426 case IPC_INFO: 427 case IPC_RMID: 428 case MSG_INFO: 429 err = sys_msgctl(first, second, uptr); 430 break; 431 432 case IPC_SET: 433 if (version == IPC_64) { 434 err = get_compat_msqid64(&m64, uptr); 435 } else { 436 err = get_compat_msqid(&m64, uptr); 437 } 438 if (err) 439 break; 440 p = compat_alloc_user_space(sizeof(m64)); 441 if (copy_to_user(p, &m64, sizeof(m64))) 442 err = -EFAULT; 443 else 444 err = sys_msgctl(first, second, p); 445 break; 446 447 case IPC_STAT: 448 case MSG_STAT: 449 p = compat_alloc_user_space(sizeof(m64)); 450 err = sys_msgctl(first, second, p); 451 if (err < 0) 452 break; 453 if (copy_from_user(&m64, p, sizeof(m64))) 454 err2 = -EFAULT; 455 else if (version == IPC_64) 456 err2 = put_compat_msqid64_ds(&m64, uptr); 457 else 458 err2 = put_compat_msqid_ds(&m64, uptr); 459 if (err2) 460 err = -EFAULT; 461 break; 462 463 default: 464 err = -EINVAL; 465 break; 466 } 467 return err; 468} 469 470long compat_sys_shmat(int first, int second, compat_uptr_t third, int version, 471 void __user *uptr) 472{ 473 int err; 474 unsigned long raddr; 475 compat_ulong_t __user *uaddr; 476 477 if (version == 1) 478 return -EINVAL; 479 err = do_shmat(first, uptr, second, &raddr); 480 if (err < 0) 481 return err; 482 uaddr = compat_ptr(third); 483 return put_user(raddr, uaddr); 484} 485 486static inline int get_compat_shmid64_ds(struct shmid64_ds *s64, 487 struct compat_shmid64_ds __user *up64) 488{ 489 if (!access_ok(VERIFY_READ, up64, sizeof(*up64))) 490 return -EFAULT; 491 return __get_compat_ipc64_perm(&s64->shm_perm, &up64->shm_perm); 492} 493 494static inline int get_compat_shmid_ds(struct shmid64_ds *s, 495 struct compat_shmid_ds __user *up) 496{ 497 if (!access_ok(VERIFY_READ, up, sizeof(*up))) 498 return -EFAULT; 499 return __get_compat_ipc_perm(&s->shm_perm, &up->shm_perm); 500} 501 502static inline int put_compat_shmid64_ds(struct shmid64_ds *s64, 503 struct compat_shmid64_ds __user *up64) 504{ 505 int err; 506 507 if (!access_ok(VERIFY_WRITE, up64, sizeof(*up64))) 508 return -EFAULT; 509 err = __put_compat_ipc64_perm(&s64->shm_perm, &up64->shm_perm); 510 err |= __put_user(s64->shm_atime, &up64->shm_atime); 511 err |= __put_user(s64->shm_dtime, &up64->shm_dtime); 512 err |= __put_user(s64->shm_ctime, &up64->shm_ctime); 513 err |= __put_user(s64->shm_segsz, &up64->shm_segsz); 514 err |= __put_user(s64->shm_nattch, &up64->shm_nattch); 515 err |= __put_user(s64->shm_cpid, &up64->shm_cpid); 516 err |= __put_user(s64->shm_lpid, &up64->shm_lpid); 517 return err; 518} 519 520static inline int put_compat_shmid_ds(struct shmid64_ds *s, 521 struct compat_shmid_ds __user *up) 522{ 523 int err; 524 525 if (!access_ok(VERIFY_WRITE, up, sizeof(*up))) 526 return -EFAULT; 527 err = __put_compat_ipc_perm(&s->shm_perm, &up->shm_perm); 528 err |= __put_user(s->shm_atime, &up->shm_atime); 529 err |= __put_user(s->shm_dtime, &up->shm_dtime); 530 err |= __put_user(s->shm_ctime, &up->shm_ctime); 531 err |= __put_user(s->shm_segsz, &up->shm_segsz); 532 err |= __put_user(s->shm_nattch, &up->shm_nattch); 533 err |= __put_user(s->shm_cpid, &up->shm_cpid); 534 err |= __put_user(s->shm_lpid, &up->shm_lpid); 535 return err; 536} 537 538static inline int put_compat_shminfo64(struct shminfo64 *smi, 539 struct compat_shminfo64 __user *up64) 540{ 541 int err; 542 543 if (!access_ok(VERIFY_WRITE, up64, sizeof(*up64))) 544 return -EFAULT; 545 if (smi->shmmax > INT_MAX) 546 smi->shmmax = INT_MAX; 547 err = __put_user(smi->shmmax, &up64->shmmax); 548 err |= __put_user(smi->shmmin, &up64->shmmin); 549 err |= __put_user(smi->shmmni, &up64->shmmni); 550 err |= __put_user(smi->shmseg, &up64->shmseg); 551 err |= __put_user(smi->shmall, &up64->shmall); 552 return err; 553} 554 555static inline int put_compat_shminfo(struct shminfo64 *smi, 556 struct shminfo __user *up) 557{ 558 int err; 559 560 if (!access_ok(VERIFY_WRITE, up, sizeof(*up))) 561 return -EFAULT; 562 if (smi->shmmax > INT_MAX) 563 smi->shmmax = INT_MAX; 564 err = __put_user(smi->shmmax, &up->shmmax); 565 err |= __put_user(smi->shmmin, &up->shmmin); 566 err |= __put_user(smi->shmmni, &up->shmmni); 567 err |= __put_user(smi->shmseg, &up->shmseg); 568 err |= __put_user(smi->shmall, &up->shmall); 569 return err; 570} 571 572static inline int put_compat_shm_info(struct shm_info __user *ip, 573 struct compat_shm_info __user *uip) 574{ 575 int err; 576 struct shm_info si; 577 578 if (!access_ok(VERIFY_WRITE, uip, sizeof(*uip)) || 579 copy_from_user(&si, ip, sizeof(si))) 580 return -EFAULT; 581 err = __put_user(si.used_ids, &uip->used_ids); 582 err |= __put_user(si.shm_tot, &uip->shm_tot); 583 err |= __put_user(si.shm_rss, &uip->shm_rss); 584 err |= __put_user(si.shm_swp, &uip->shm_swp); 585 err |= __put_user(si.swap_attempts, &uip->swap_attempts); 586 err |= __put_user(si.swap_successes, &uip->swap_successes); 587 return err; 588} 589 590long compat_sys_shmctl(int first, int second, void __user *uptr) 591{ 592 void __user *p; 593 struct shmid64_ds s64; 594 struct shminfo64 smi; 595 int err, err2; 596 int version = compat_ipc_parse_version(&second); 597 598 switch (second & (~IPC_64)) { 599 case IPC_RMID: 600 case SHM_LOCK: 601 case SHM_UNLOCK: 602 err = sys_shmctl(first, second, uptr); 603 break; 604 605 case IPC_INFO: 606 p = compat_alloc_user_space(sizeof(smi)); 607 err = sys_shmctl(first, second, p); 608 if (err < 0) 609 break; 610 if (copy_from_user(&smi, p, sizeof(smi))) 611 err2 = -EFAULT; 612 else if (version == IPC_64) 613 err2 = put_compat_shminfo64(&smi, uptr); 614 else 615 err2 = put_compat_shminfo(&smi, uptr); 616 if (err2) 617 err = -EFAULT; 618 break; 619 620 621 case IPC_SET: 622 if (version == IPC_64) { 623 err = get_compat_shmid64_ds(&s64, uptr); 624 } else { 625 err = get_compat_shmid_ds(&s64, uptr); 626 } 627 if (err) 628 break; 629 p = compat_alloc_user_space(sizeof(s64)); 630 if (copy_to_user(p, &s64, sizeof(s64))) 631 err = -EFAULT; 632 else 633 err = sys_shmctl(first, second, p); 634 break; 635 636 case IPC_STAT: 637 case SHM_STAT: 638 p = compat_alloc_user_space(sizeof(s64)); 639 err = sys_shmctl(first, second, p); 640 if (err < 0) 641 break; 642 if (copy_from_user(&s64, p, sizeof(s64))) 643 err2 = -EFAULT; 644 else if (version == IPC_64) 645 err2 = put_compat_shmid64_ds(&s64, uptr); 646 else 647 err2 = put_compat_shmid_ds(&s64, uptr); 648 if (err2) 649 err = -EFAULT; 650 break; 651 652 case SHM_INFO: 653 p = compat_alloc_user_space(sizeof(struct shm_info)); 654 err = sys_shmctl(first, second, p); 655 if (err < 0) 656 break; 657 err2 = put_compat_shm_info(p, uptr); 658 if (err2) 659 err = -EFAULT; 660 break; 661 662 default: 663 err = -EINVAL; 664 break; 665 } 666 return err; 667} 668 669long compat_sys_semtimedop(int semid, struct sembuf __user *tsems, 670 unsigned nsops, const struct compat_timespec __user *timeout) 671{ 672 struct timespec __user *ts64 = NULL; 673 if (timeout) { 674 struct timespec ts; 675 ts64 = compat_alloc_user_space(sizeof(*ts64)); 676 if (get_compat_timespec(&ts, timeout)) 677 return -EFAULT; 678 if (copy_to_user(ts64, &ts, sizeof(ts))) 679 return -EFAULT; 680 } 681 return sys_semtimedop(semid, tsems, nsops, ts64); 682} 683