1 /*- 2 * SPDX-License-Identifier: BSD-4-Clause AND BSD-2-Clause 3 * 4 * Copyright (c) 1994 Adam Glass and Charles Hannum. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. All advertising materials mentioning features or use of this software 15 * must display the following acknowledgement: 16 * This product includes software developed by Adam Glass and Charles 17 * Hannum. 18 * 4. The names of the authors may not be used to endorse or promote products 19 * derived from this software without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR 22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 24 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 * 32 * $NetBSD: sysv_shm.c,v 1.39 1997/10/07 10:02:03 drochner Exp $ 33 */ 34 /*- 35 * Copyright (c) 2003-2005 McAfee, Inc. 36 * Copyright (c) 2016-2017 Robert N. M. Watson 37 * All rights reserved. 38 * 39 * This software was developed for the FreeBSD Project in part by McAfee 40 * Research, the Security Research Division of McAfee, Inc under DARPA/SPAWAR 41 * contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA CHATS research 42 * program. 43 * 44 * Portions of this software were developed by BAE Systems, the University of 45 * Cambridge Computer Laboratory, and Memorial University under DARPA/AFRL 46 * contract FA8650-15-C-7558 ("CADETS"), as part of the DARPA Transparent 47 * Computing (TC) research program. 48 * 49 * Redistribution and use in source and binary forms, with or without 50 * modification, are permitted provided that the following conditions 51 * are met: 52 * 1. Redistributions of source code must retain the above copyright 53 * notice, this list of conditions and the following disclaimer. 54 * 2. Redistributions in binary form must reproduce the above copyright 55 * notice, this list of conditions and the following disclaimer in the 56 * documentation and/or other materials provided with the distribution. 57 * 58 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 59 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 60 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 61 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 62 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 63 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 64 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 65 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 66 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 67 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 68 * SUCH DAMAGE. 69 */ 70 71 #include "opt_sysvipc.h" 72 73 #include <sys/param.h> 74 #include <sys/systm.h> 75 #include <sys/abi_compat.h> 76 #include <sys/kernel.h> 77 #include <sys/limits.h> 78 #include <sys/lock.h> 79 #include <sys/sysctl.h> 80 #include <sys/shm.h> 81 #include <sys/proc.h> 82 #include <sys/malloc.h> 83 #include <sys/mman.h> 84 #include <sys/module.h> 85 #include <sys/mutex.h> 86 #include <sys/racct.h> 87 #include <sys/resourcevar.h> 88 #include <sys/rwlock.h> 89 #include <sys/stat.h> 90 #include <sys/syscall.h> 91 #include <sys/syscallsubr.h> 92 #include <sys/sysent.h> 93 #include <sys/sysproto.h> 94 #include <sys/jail.h> 95 96 #include <security/audit/audit.h> 97 #include <security/mac/mac_framework.h> 98 99 #include <vm/vm.h> 100 #include <vm/vm_param.h> 101 #include <vm/pmap.h> 102 #include <vm/vm_object.h> 103 #include <vm/vm_map.h> 104 #include <vm/vm_page.h> 105 #include <vm/vm_pager.h> 106 107 FEATURE(sysv_shm, "System V shared memory segments support"); 108 109 static MALLOC_DEFINE(M_SHM, "shm", "SVID compatible shared memory segments"); 110 111 static int shm_last_free, shm_nused, shmalloced; 112 vm_size_t shm_committed; 113 static struct shmid_kernel *shmsegs; 114 static unsigned shm_prison_slot; 115 116 struct shmmap_state { 117 vm_offset_t va; 118 int shmid; 119 }; 120 121 static void shm_deallocate_segment(struct shmid_kernel *); 122 static int shm_find_segment_by_key(struct prison *, key_t); 123 static struct shmid_kernel *shm_find_segment(struct prison *, int, bool); 124 static int shm_delete_mapping(struct vmspace *vm, struct shmmap_state *); 125 static int shmget_allocate_segment(struct thread *td, key_t key, size_t size, 126 int mode); 127 static int shmget_existing(struct thread *td, size_t size, int shmflg, 128 int mode, int segnum); 129 static void shmrealloc(void); 130 static int shminit(void); 131 static int sysvshm_modload(struct module *, int, void *); 132 static int shmunload(void); 133 #ifndef SYSVSHM 134 static void shmexit_myhook(struct vmspace *vm); 135 static void shmfork_myhook(struct proc *p1, struct proc *p2); 136 static void shmobjinfo_myhook(vm_object_t obj, key_t *key, 137 unsigned short *seq); 138 #endif 139 static int sysctl_shmsegs(SYSCTL_HANDLER_ARGS); 140 static void shm_remove(struct shmid_kernel *, int); 141 static struct prison *shm_find_prison(struct ucred *); 142 static int shm_prison_cansee(struct prison *, struct shmid_kernel *); 143 static int shm_prison_check(void *, void *); 144 static int shm_prison_set(void *, void *); 145 static int shm_prison_get(void *, void *); 146 static int shm_prison_remove(void *, void *); 147 static void shm_prison_cleanup(struct prison *); 148 149 /* 150 * Tuneable values. 151 */ 152 #ifndef SHMMAXPGS 153 #define SHMMAXPGS 131072ul /* Note: sysv shared memory is swap backed. */ 154 #endif 155 #ifndef SHMMAX 156 #define SHMMAX (SHMMAXPGS*PAGE_SIZE) 157 #endif 158 #ifndef SHMMIN 159 #define SHMMIN 1 160 #endif 161 #ifndef SHMMNI 162 #define SHMMNI 192 163 #endif 164 #ifndef SHMSEG 165 #define SHMSEG 128 166 #endif 167 #ifndef SHMALL 168 #define SHMALL (SHMMAXPGS) 169 #endif 170 171 struct shminfo shminfo = { 172 .shmmax = SHMMAX, 173 .shmmin = SHMMIN, 174 .shmmni = SHMMNI, 175 .shmseg = SHMSEG, 176 .shmall = SHMALL 177 }; 178 179 static int shm_use_phys; 180 static int shm_allow_removed = 1; 181 182 SYSCTL_ULONG(_kern_ipc, OID_AUTO, shmmax, CTLFLAG_RWTUN, &shminfo.shmmax, 0, 183 "Maximum shared memory segment size"); 184 SYSCTL_ULONG(_kern_ipc, OID_AUTO, shmmin, CTLFLAG_RWTUN, &shminfo.shmmin, 0, 185 "Minimum shared memory segment size"); 186 SYSCTL_ULONG(_kern_ipc, OID_AUTO, shmmni, CTLFLAG_RDTUN, &shminfo.shmmni, 0, 187 "Number of shared memory identifiers"); 188 SYSCTL_ULONG(_kern_ipc, OID_AUTO, shmseg, CTLFLAG_RDTUN, &shminfo.shmseg, 0, 189 "Number of segments per process"); 190 SYSCTL_ULONG(_kern_ipc, OID_AUTO, shmall, CTLFLAG_RWTUN, &shminfo.shmall, 0, 191 "Maximum number of pages available for shared memory"); 192 SYSCTL_INT(_kern_ipc, OID_AUTO, shm_use_phys, CTLFLAG_RWTUN, 193 &shm_use_phys, 0, "Enable/Disable locking of shared memory pages in core"); 194 SYSCTL_INT(_kern_ipc, OID_AUTO, shm_allow_removed, CTLFLAG_RWTUN, 195 &shm_allow_removed, 0, 196 "Enable/Disable attachment to attached segments marked for removal"); 197 SYSCTL_PROC(_kern_ipc, OID_AUTO, shmsegs, CTLTYPE_OPAQUE | CTLFLAG_RD | 198 CTLFLAG_MPSAFE, NULL, 0, sysctl_shmsegs, "", 199 "Array of struct shmid_kernel for each potential shared memory segment"); 200 201 static struct sx sysvshmsx; 202 #define SYSVSHM_LOCK() sx_xlock(&sysvshmsx) 203 #define SYSVSHM_UNLOCK() sx_xunlock(&sysvshmsx) 204 #define SYSVSHM_ASSERT_LOCKED() sx_assert(&sysvshmsx, SA_XLOCKED) 205 206 static int 207 shm_find_segment_by_key(struct prison *pr, key_t key) 208 { 209 int i; 210 211 for (i = 0; i < shmalloced; i++) 212 if ((shmsegs[i].u.shm_perm.mode & SHMSEG_ALLOCATED) && 213 shmsegs[i].cred != NULL && 214 shmsegs[i].cred->cr_prison == pr && 215 shmsegs[i].u.shm_perm.key == key) 216 return (i); 217 return (-1); 218 } 219 220 /* 221 * Finds segment either by shmid if is_shmid is true, or by segnum if 222 * is_shmid is false. 223 */ 224 static struct shmid_kernel * 225 shm_find_segment(struct prison *rpr, int arg, bool is_shmid) 226 { 227 struct shmid_kernel *shmseg; 228 int segnum; 229 230 segnum = is_shmid ? IPCID_TO_IX(arg) : arg; 231 if (segnum < 0 || segnum >= shmalloced) 232 return (NULL); 233 shmseg = &shmsegs[segnum]; 234 if ((shmseg->u.shm_perm.mode & SHMSEG_ALLOCATED) == 0 || 235 (!shm_allow_removed && 236 (shmseg->u.shm_perm.mode & SHMSEG_REMOVED) != 0) || 237 (is_shmid && shmseg->u.shm_perm.seq != IPCID_TO_SEQ(arg)) || 238 shm_prison_cansee(rpr, shmseg) != 0) 239 return (NULL); 240 return (shmseg); 241 } 242 243 static void 244 shm_deallocate_segment(struct shmid_kernel *shmseg) 245 { 246 vm_size_t size; 247 248 SYSVSHM_ASSERT_LOCKED(); 249 250 vm_object_deallocate(shmseg->object); 251 shmseg->object = NULL; 252 size = round_page(shmseg->u.shm_segsz); 253 shm_committed -= btoc(size); 254 shm_nused--; 255 shmseg->u.shm_perm.mode = SHMSEG_FREE; 256 #ifdef MAC 257 mac_sysvshm_cleanup(shmseg); 258 #endif 259 racct_sub_cred(shmseg->cred, RACCT_NSHM, 1); 260 racct_sub_cred(shmseg->cred, RACCT_SHMSIZE, size); 261 crfree(shmseg->cred); 262 shmseg->cred = NULL; 263 } 264 265 static int 266 shm_delete_mapping(struct vmspace *vm, struct shmmap_state *shmmap_s) 267 { 268 struct shmid_kernel *shmseg; 269 int segnum, result; 270 vm_size_t size; 271 272 SYSVSHM_ASSERT_LOCKED(); 273 segnum = IPCID_TO_IX(shmmap_s->shmid); 274 KASSERT(segnum >= 0 && segnum < shmalloced, 275 ("segnum %d shmalloced %d", segnum, shmalloced)); 276 277 shmseg = &shmsegs[segnum]; 278 size = round_page(shmseg->u.shm_segsz); 279 result = vm_map_remove(&vm->vm_map, shmmap_s->va, shmmap_s->va + size); 280 if (result != KERN_SUCCESS) 281 return (EINVAL); 282 shmmap_s->shmid = -1; 283 shmseg->u.shm_dtime = time_second; 284 if (--shmseg->u.shm_nattch == 0 && 285 (shmseg->u.shm_perm.mode & SHMSEG_REMOVED)) { 286 shm_deallocate_segment(shmseg); 287 shm_last_free = segnum; 288 } 289 return (0); 290 } 291 292 static void 293 shm_remove(struct shmid_kernel *shmseg, int segnum) 294 { 295 296 shmseg->u.shm_perm.key = IPC_PRIVATE; 297 shmseg->u.shm_perm.mode |= SHMSEG_REMOVED; 298 if (shmseg->u.shm_nattch == 0) { 299 shm_deallocate_segment(shmseg); 300 shm_last_free = segnum; 301 } 302 } 303 304 static struct prison * 305 shm_find_prison(struct ucred *cred) 306 { 307 struct prison *pr, *rpr; 308 309 pr = cred->cr_prison; 310 prison_lock(pr); 311 rpr = osd_jail_get(pr, shm_prison_slot); 312 prison_unlock(pr); 313 return rpr; 314 } 315 316 static int 317 shm_prison_cansee(struct prison *rpr, struct shmid_kernel *shmseg) 318 { 319 320 if (shmseg->cred == NULL || 321 !(rpr == shmseg->cred->cr_prison || 322 prison_ischild(rpr, shmseg->cred->cr_prison))) 323 return (EINVAL); 324 return (0); 325 } 326 327 static int 328 kern_shmdt_locked(struct thread *td, const void *shmaddr) 329 { 330 struct proc *p = td->td_proc; 331 struct shmmap_state *shmmap_s; 332 #ifdef MAC 333 int error; 334 #endif 335 int i; 336 337 SYSVSHM_ASSERT_LOCKED(); 338 if (shm_find_prison(td->td_ucred) == NULL) 339 return (ENOSYS); 340 shmmap_s = p->p_vmspace->vm_shm; 341 if (shmmap_s == NULL) 342 return (EINVAL); 343 AUDIT_ARG_SVIPC_ID(shmmap_s->shmid); 344 for (i = 0; i < shminfo.shmseg; i++, shmmap_s++) { 345 if (shmmap_s->shmid != -1 && 346 shmmap_s->va == (vm_offset_t)shmaddr) { 347 break; 348 } 349 } 350 if (i == shminfo.shmseg) 351 return (EINVAL); 352 #ifdef MAC 353 error = mac_sysvshm_check_shmdt(td->td_ucred, 354 &shmsegs[IPCID_TO_IX(shmmap_s->shmid)]); 355 if (error != 0) 356 return (error); 357 #endif 358 return (shm_delete_mapping(p->p_vmspace, shmmap_s)); 359 } 360 361 #ifndef _SYS_SYSPROTO_H_ 362 struct shmdt_args { 363 const void *shmaddr; 364 }; 365 #endif 366 int 367 sys_shmdt(struct thread *td, struct shmdt_args *uap) 368 { 369 int error; 370 371 SYSVSHM_LOCK(); 372 error = kern_shmdt_locked(td, uap->shmaddr); 373 SYSVSHM_UNLOCK(); 374 return (error); 375 } 376 377 static int 378 kern_shmat_locked(struct thread *td, int shmid, const void *shmaddr, 379 int shmflg) 380 { 381 struct prison *rpr; 382 struct proc *p = td->td_proc; 383 struct shmid_kernel *shmseg; 384 struct shmmap_state *shmmap_s; 385 vm_offset_t attach_va; 386 vm_prot_t prot; 387 vm_size_t size; 388 int cow, error, find_space, i, rv; 389 390 AUDIT_ARG_SVIPC_ID(shmid); 391 AUDIT_ARG_VALUE(shmflg); 392 393 SYSVSHM_ASSERT_LOCKED(); 394 rpr = shm_find_prison(td->td_ucred); 395 if (rpr == NULL) 396 return (ENOSYS); 397 shmmap_s = p->p_vmspace->vm_shm; 398 if (shmmap_s == NULL) { 399 shmmap_s = malloc(shminfo.shmseg * sizeof(struct shmmap_state), 400 M_SHM, M_WAITOK); 401 for (i = 0; i < shminfo.shmseg; i++) 402 shmmap_s[i].shmid = -1; 403 KASSERT(p->p_vmspace->vm_shm == NULL, ("raced")); 404 p->p_vmspace->vm_shm = shmmap_s; 405 } 406 shmseg = shm_find_segment(rpr, shmid, true); 407 if (shmseg == NULL) 408 return (EINVAL); 409 error = ipcperm(td, &shmseg->u.shm_perm, 410 (shmflg & SHM_RDONLY) ? IPC_R : IPC_R|IPC_W); 411 if (error != 0) 412 return (error); 413 #ifdef MAC 414 error = mac_sysvshm_check_shmat(td->td_ucred, shmseg, shmflg); 415 if (error != 0) 416 return (error); 417 #endif 418 for (i = 0; i < shminfo.shmseg; i++) { 419 if (shmmap_s->shmid == -1) 420 break; 421 shmmap_s++; 422 } 423 if (i >= shminfo.shmseg) 424 return (EMFILE); 425 size = round_page(shmseg->u.shm_segsz); 426 prot = VM_PROT_READ; 427 cow = MAP_INHERIT_SHARE | MAP_PREFAULT_PARTIAL; 428 if ((shmflg & SHM_RDONLY) == 0) 429 prot |= VM_PROT_WRITE; 430 if (shmaddr != NULL) { 431 if ((shmflg & SHM_RND) != 0) 432 attach_va = rounddown2((vm_offset_t)shmaddr, SHMLBA); 433 else if (((vm_offset_t)shmaddr & (SHMLBA-1)) == 0) 434 attach_va = (vm_offset_t)shmaddr; 435 else 436 return (EINVAL); 437 if ((shmflg & SHM_REMAP) != 0) 438 cow |= MAP_REMAP; 439 find_space = VMFS_NO_SPACE; 440 } else { 441 /* 442 * This is just a hint to vm_map_find() about where to 443 * put it. 444 */ 445 attach_va = round_page((vm_offset_t)p->p_vmspace->vm_daddr + 446 lim_max(td, RLIMIT_DATA)); 447 find_space = VMFS_OPTIMAL_SPACE; 448 } 449 450 vm_object_reference(shmseg->object); 451 rv = vm_map_find(&p->p_vmspace->vm_map, shmseg->object, 0, &attach_va, 452 size, 0, find_space, prot, prot, cow); 453 if (rv != KERN_SUCCESS) { 454 vm_object_deallocate(shmseg->object); 455 return (ENOMEM); 456 } 457 458 shmmap_s->va = attach_va; 459 shmmap_s->shmid = shmid; 460 shmseg->u.shm_lpid = p->p_pid; 461 shmseg->u.shm_atime = time_second; 462 shmseg->u.shm_nattch++; 463 td->td_retval[0] = attach_va; 464 return (error); 465 } 466 467 int 468 kern_shmat(struct thread *td, int shmid, const void *shmaddr, int shmflg) 469 { 470 int error; 471 472 SYSVSHM_LOCK(); 473 error = kern_shmat_locked(td, shmid, shmaddr, shmflg); 474 SYSVSHM_UNLOCK(); 475 return (error); 476 } 477 478 #ifndef _SYS_SYSPROTO_H_ 479 struct shmat_args { 480 int shmid; 481 const void *shmaddr; 482 int shmflg; 483 }; 484 #endif 485 int 486 sys_shmat(struct thread *td, struct shmat_args *uap) 487 { 488 489 return (kern_shmat(td, uap->shmid, uap->shmaddr, uap->shmflg)); 490 } 491 492 static int 493 kern_shmctl_locked(struct thread *td, int shmid, int cmd, void *buf, 494 size_t *bufsz) 495 { 496 struct prison *rpr; 497 struct shmid_kernel *shmseg; 498 struct shmid_ds *shmidp; 499 struct shm_info shm_info; 500 int error; 501 502 SYSVSHM_ASSERT_LOCKED(); 503 504 rpr = shm_find_prison(td->td_ucred); 505 if (rpr == NULL) 506 return (ENOSYS); 507 508 AUDIT_ARG_SVIPC_ID(shmid); 509 AUDIT_ARG_SVIPC_CMD(cmd); 510 511 switch (cmd) { 512 /* 513 * It is possible that kern_shmctl is being called from the Linux ABI 514 * layer, in which case, we will need to implement IPC_INFO. It should 515 * be noted that other shmctl calls will be funneled through here for 516 * Linix binaries as well. 517 * 518 * NB: The Linux ABI layer will convert this data to structure(s) more 519 * consistent with the Linux ABI. 520 */ 521 case IPC_INFO: 522 memcpy(buf, &shminfo, sizeof(shminfo)); 523 if (bufsz) 524 *bufsz = sizeof(shminfo); 525 td->td_retval[0] = shmalloced; 526 return (0); 527 case SHM_INFO: { 528 shm_info.used_ids = shm_nused; 529 shm_info.shm_rss = 0; /*XXX where to get from ? */ 530 shm_info.shm_tot = 0; /*XXX where to get from ? */ 531 shm_info.shm_swp = 0; /*XXX where to get from ? */ 532 shm_info.swap_attempts = 0; /*XXX where to get from ? */ 533 shm_info.swap_successes = 0; /*XXX where to get from ? */ 534 memcpy(buf, &shm_info, sizeof(shm_info)); 535 if (bufsz != NULL) 536 *bufsz = sizeof(shm_info); 537 td->td_retval[0] = shmalloced; 538 return (0); 539 } 540 } 541 shmseg = shm_find_segment(rpr, shmid, cmd != SHM_STAT); 542 if (shmseg == NULL) 543 return (EINVAL); 544 #ifdef MAC 545 error = mac_sysvshm_check_shmctl(td->td_ucred, shmseg, cmd); 546 if (error != 0) 547 return (error); 548 #endif 549 switch (cmd) { 550 case SHM_STAT: 551 case IPC_STAT: 552 shmidp = (struct shmid_ds *)buf; 553 error = ipcperm(td, &shmseg->u.shm_perm, IPC_R); 554 if (error != 0) 555 return (error); 556 memcpy(shmidp, &shmseg->u, sizeof(struct shmid_ds)); 557 if (td->td_ucred->cr_prison != shmseg->cred->cr_prison) 558 shmidp->shm_perm.key = IPC_PRIVATE; 559 if (bufsz != NULL) 560 *bufsz = sizeof(struct shmid_ds); 561 if (cmd == SHM_STAT) { 562 td->td_retval[0] = IXSEQ_TO_IPCID(shmid, 563 shmseg->u.shm_perm); 564 } 565 break; 566 case IPC_SET: 567 shmidp = (struct shmid_ds *)buf; 568 AUDIT_ARG_SVIPC_PERM(&shmidp->shm_perm); 569 error = ipcperm(td, &shmseg->u.shm_perm, IPC_M); 570 if (error != 0) 571 return (error); 572 shmseg->u.shm_perm.uid = shmidp->shm_perm.uid; 573 shmseg->u.shm_perm.gid = shmidp->shm_perm.gid; 574 shmseg->u.shm_perm.mode = 575 (shmseg->u.shm_perm.mode & ~ACCESSPERMS) | 576 (shmidp->shm_perm.mode & ACCESSPERMS); 577 shmseg->u.shm_ctime = time_second; 578 break; 579 case IPC_RMID: 580 error = ipcperm(td, &shmseg->u.shm_perm, IPC_M); 581 if (error != 0) 582 return (error); 583 shm_remove(shmseg, IPCID_TO_IX(shmid)); 584 break; 585 #if 0 586 case SHM_LOCK: 587 case SHM_UNLOCK: 588 #endif 589 default: 590 error = EINVAL; 591 break; 592 } 593 return (error); 594 } 595 596 int 597 kern_shmctl(struct thread *td, int shmid, int cmd, void *buf, size_t *bufsz) 598 { 599 int error; 600 601 SYSVSHM_LOCK(); 602 error = kern_shmctl_locked(td, shmid, cmd, buf, bufsz); 603 SYSVSHM_UNLOCK(); 604 return (error); 605 } 606 607 #ifndef _SYS_SYSPROTO_H_ 608 struct shmctl_args { 609 int shmid; 610 int cmd; 611 struct shmid_ds *buf; 612 }; 613 #endif 614 int 615 sys_shmctl(struct thread *td, struct shmctl_args *uap) 616 { 617 int error; 618 struct shmid_ds buf; 619 size_t bufsz; 620 621 /* 622 * The only reason IPC_INFO, SHM_INFO, SHM_STAT exists is to support 623 * Linux binaries. If we see the call come through the FreeBSD ABI, 624 * return an error back to the user since we do not to support this. 625 */ 626 if (uap->cmd == IPC_INFO || uap->cmd == SHM_INFO || 627 uap->cmd == SHM_STAT) 628 return (EINVAL); 629 630 /* IPC_SET needs to copyin the buffer before calling kern_shmctl */ 631 if (uap->cmd == IPC_SET) { 632 if ((error = copyin(uap->buf, &buf, sizeof(struct shmid_ds)))) 633 goto done; 634 } 635 636 error = kern_shmctl(td, uap->shmid, uap->cmd, (void *)&buf, &bufsz); 637 if (error) 638 goto done; 639 640 /* Cases in which we need to copyout */ 641 switch (uap->cmd) { 642 case IPC_STAT: 643 error = copyout(&buf, uap->buf, bufsz); 644 break; 645 } 646 647 done: 648 if (error) { 649 /* Invalidate the return value */ 650 td->td_retval[0] = -1; 651 } 652 return (error); 653 } 654 655 static int 656 shmget_existing(struct thread *td, size_t size, int shmflg, int mode, 657 int segnum) 658 { 659 struct shmid_kernel *shmseg; 660 #ifdef MAC 661 int error; 662 #endif 663 664 SYSVSHM_ASSERT_LOCKED(); 665 KASSERT(segnum >= 0 && segnum < shmalloced, 666 ("segnum %d shmalloced %d", segnum, shmalloced)); 667 shmseg = &shmsegs[segnum]; 668 if ((shmflg & (IPC_CREAT | IPC_EXCL)) == (IPC_CREAT | IPC_EXCL)) 669 return (EEXIST); 670 #ifdef MAC 671 error = mac_sysvshm_check_shmget(td->td_ucred, shmseg, shmflg); 672 if (error != 0) 673 return (error); 674 #endif 675 if (size != 0 && size > shmseg->u.shm_segsz) 676 return (EINVAL); 677 td->td_retval[0] = IXSEQ_TO_IPCID(segnum, shmseg->u.shm_perm); 678 return (0); 679 } 680 681 static int 682 shmget_allocate_segment(struct thread *td, key_t key, size_t size, int mode) 683 { 684 struct ucred *cred = td->td_ucred; 685 struct shmid_kernel *shmseg; 686 vm_object_t shm_object; 687 int i, segnum; 688 689 SYSVSHM_ASSERT_LOCKED(); 690 691 if (size < shminfo.shmmin || size > shminfo.shmmax) 692 return (EINVAL); 693 if (shm_nused >= shminfo.shmmni) /* Any shmids left? */ 694 return (ENOSPC); 695 size = round_page(size); 696 if (shm_committed + btoc(size) > shminfo.shmall) 697 return (ENOMEM); 698 if (shm_last_free < 0) { 699 shmrealloc(); /* Maybe expand the shmsegs[] array. */ 700 for (i = 0; i < shmalloced; i++) 701 if (shmsegs[i].u.shm_perm.mode & SHMSEG_FREE) 702 break; 703 if (i == shmalloced) 704 return (ENOSPC); 705 segnum = i; 706 } else { 707 segnum = shm_last_free; 708 shm_last_free = -1; 709 } 710 KASSERT(segnum >= 0 && segnum < shmalloced, 711 ("segnum %d shmalloced %d", segnum, shmalloced)); 712 shmseg = &shmsegs[segnum]; 713 #ifdef RACCT 714 if (racct_enable) { 715 PROC_LOCK(td->td_proc); 716 if (racct_add(td->td_proc, RACCT_NSHM, 1)) { 717 PROC_UNLOCK(td->td_proc); 718 return (ENOSPC); 719 } 720 if (racct_add(td->td_proc, RACCT_SHMSIZE, size)) { 721 racct_sub(td->td_proc, RACCT_NSHM, 1); 722 PROC_UNLOCK(td->td_proc); 723 return (ENOMEM); 724 } 725 PROC_UNLOCK(td->td_proc); 726 } 727 #endif 728 729 /* 730 * We make sure that we have allocated a pager before we need 731 * to. 732 */ 733 shm_object = vm_pager_allocate(shm_use_phys ? OBJT_PHYS : OBJT_SWAP, 734 0, size, VM_PROT_DEFAULT, 0, cred); 735 if (shm_object == NULL) { 736 #ifdef RACCT 737 if (racct_enable) { 738 PROC_LOCK(td->td_proc); 739 racct_sub(td->td_proc, RACCT_NSHM, 1); 740 racct_sub(td->td_proc, RACCT_SHMSIZE, size); 741 PROC_UNLOCK(td->td_proc); 742 } 743 #endif 744 return (ENOMEM); 745 } 746 747 VM_OBJECT_WLOCK(shm_object); 748 vm_object_set_flag(shm_object, OBJ_SYSVSHM); 749 VM_OBJECT_WUNLOCK(shm_object); 750 751 shmseg->object = shm_object; 752 shmseg->u.shm_perm.cuid = shmseg->u.shm_perm.uid = cred->cr_uid; 753 shmseg->u.shm_perm.cgid = shmseg->u.shm_perm.gid = cred->cr_gid; 754 shmseg->u.shm_perm.mode = (mode & ACCESSPERMS) | SHMSEG_ALLOCATED; 755 shmseg->u.shm_perm.key = key; 756 shmseg->u.shm_perm.seq = (shmseg->u.shm_perm.seq + 1) & 0x7fff; 757 shmseg->cred = crhold(cred); 758 shmseg->u.shm_segsz = size; 759 shmseg->u.shm_cpid = td->td_proc->p_pid; 760 shmseg->u.shm_lpid = shmseg->u.shm_nattch = 0; 761 shmseg->u.shm_atime = shmseg->u.shm_dtime = 0; 762 #ifdef MAC 763 mac_sysvshm_create(cred, shmseg); 764 #endif 765 shmseg->u.shm_ctime = time_second; 766 shm_committed += btoc(size); 767 shm_nused++; 768 td->td_retval[0] = IXSEQ_TO_IPCID(segnum, shmseg->u.shm_perm); 769 770 return (0); 771 } 772 773 #ifndef _SYS_SYSPROTO_H_ 774 struct shmget_args { 775 key_t key; 776 size_t size; 777 int shmflg; 778 }; 779 #endif 780 int 781 sys_shmget(struct thread *td, struct shmget_args *uap) 782 { 783 int segnum, mode; 784 int error; 785 786 if (shm_find_prison(td->td_ucred) == NULL) 787 return (ENOSYS); 788 mode = uap->shmflg & ACCESSPERMS; 789 SYSVSHM_LOCK(); 790 if (uap->key == IPC_PRIVATE) { 791 error = shmget_allocate_segment(td, uap->key, uap->size, mode); 792 } else { 793 segnum = shm_find_segment_by_key(td->td_ucred->cr_prison, 794 uap->key); 795 if (segnum >= 0) 796 error = shmget_existing(td, uap->size, uap->shmflg, 797 mode, segnum); 798 else if ((uap->shmflg & IPC_CREAT) == 0) 799 error = ENOENT; 800 else 801 error = shmget_allocate_segment(td, uap->key, 802 uap->size, mode); 803 } 804 SYSVSHM_UNLOCK(); 805 return (error); 806 } 807 808 #ifdef SYSVSHM 809 void 810 shmfork(struct proc *p1, struct proc *p2) 811 #else 812 static void 813 shmfork_myhook(struct proc *p1, struct proc *p2) 814 #endif 815 { 816 struct shmmap_state *shmmap_s; 817 size_t size; 818 int i; 819 820 SYSVSHM_LOCK(); 821 size = shminfo.shmseg * sizeof(struct shmmap_state); 822 shmmap_s = malloc(size, M_SHM, M_WAITOK); 823 bcopy(p1->p_vmspace->vm_shm, shmmap_s, size); 824 p2->p_vmspace->vm_shm = shmmap_s; 825 for (i = 0; i < shminfo.shmseg; i++, shmmap_s++) { 826 if (shmmap_s->shmid != -1) { 827 KASSERT(IPCID_TO_IX(shmmap_s->shmid) >= 0 && 828 IPCID_TO_IX(shmmap_s->shmid) < shmalloced, 829 ("segnum %d shmalloced %d", 830 IPCID_TO_IX(shmmap_s->shmid), shmalloced)); 831 shmsegs[IPCID_TO_IX(shmmap_s->shmid)].u.shm_nattch++; 832 } 833 } 834 SYSVSHM_UNLOCK(); 835 } 836 837 #ifdef SYSVSHM 838 void 839 shmexit(struct vmspace *vm) 840 #else 841 static void 842 shmexit_myhook(struct vmspace *vm) 843 #endif 844 { 845 struct shmmap_state *base, *shm; 846 int i; 847 848 base = vm->vm_shm; 849 if (base != NULL) { 850 vm->vm_shm = NULL; 851 SYSVSHM_LOCK(); 852 for (i = 0, shm = base; i < shminfo.shmseg; i++, shm++) { 853 if (shm->shmid != -1) 854 shm_delete_mapping(vm, shm); 855 } 856 SYSVSHM_UNLOCK(); 857 free(base, M_SHM); 858 } 859 } 860 861 #ifdef SYSVSHM 862 void 863 shmobjinfo(vm_object_t obj, key_t *key, unsigned short *seq) 864 #else 865 static void 866 shmobjinfo_myhook(vm_object_t obj, key_t *key, unsigned short *seq) 867 #endif 868 { 869 int i; 870 871 *key = 0; /* For statically compiled-in sysv_shm.c */ 872 *seq = 0; 873 SYSVSHM_LOCK(); 874 for (i = 0; i < shmalloced; i++) { 875 if (shmsegs[i].object == obj) { 876 *key = shmsegs[i].u.shm_perm.key; 877 *seq = shmsegs[i].u.shm_perm.seq; 878 break; 879 } 880 } 881 SYSVSHM_UNLOCK(); 882 } 883 884 static void 885 shmrealloc(void) 886 { 887 struct shmid_kernel *newsegs; 888 int i; 889 890 SYSVSHM_ASSERT_LOCKED(); 891 892 if (shmalloced >= shminfo.shmmni) 893 return; 894 895 newsegs = malloc(shminfo.shmmni * sizeof(*newsegs), M_SHM, 896 M_WAITOK | M_ZERO); 897 for (i = 0; i < shmalloced; i++) 898 bcopy(&shmsegs[i], &newsegs[i], sizeof(newsegs[0])); 899 for (; i < shminfo.shmmni; i++) { 900 newsegs[i].u.shm_perm.mode = SHMSEG_FREE; 901 newsegs[i].u.shm_perm.seq = 0; 902 #ifdef MAC 903 mac_sysvshm_init(&newsegs[i]); 904 #endif 905 } 906 free(shmsegs, M_SHM); 907 shmsegs = newsegs; 908 shmalloced = shminfo.shmmni; 909 } 910 911 static struct syscall_helper_data shm_syscalls[] = { 912 SYSCALL_INIT_HELPER(shmat), 913 SYSCALL_INIT_HELPER(shmctl), 914 SYSCALL_INIT_HELPER(shmdt), 915 SYSCALL_INIT_HELPER(shmget), 916 #if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \ 917 defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD7) 918 SYSCALL_INIT_HELPER_COMPAT(freebsd7_shmctl), 919 #endif 920 #if defined(__i386__) && (defined(COMPAT_FREEBSD4) || defined(COMPAT_43)) 921 SYSCALL_INIT_HELPER(shmsys), 922 #endif 923 SYSCALL_INIT_LAST 924 }; 925 926 #ifdef COMPAT_FREEBSD32 927 #include <compat/freebsd32/freebsd32.h> 928 #include <compat/freebsd32/freebsd32_ipc.h> 929 #include <compat/freebsd32/freebsd32_proto.h> 930 #include <compat/freebsd32/freebsd32_signal.h> 931 #include <compat/freebsd32/freebsd32_syscall.h> 932 #include <compat/freebsd32/freebsd32_util.h> 933 934 static struct syscall_helper_data shm32_syscalls[] = { 935 SYSCALL32_INIT_HELPER_COMPAT(shmat), 936 SYSCALL32_INIT_HELPER_COMPAT(shmdt), 937 SYSCALL32_INIT_HELPER_COMPAT(shmget), 938 SYSCALL32_INIT_HELPER(freebsd32_shmsys), 939 SYSCALL32_INIT_HELPER(freebsd32_shmctl), 940 #if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \ 941 defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD7) 942 SYSCALL32_INIT_HELPER(freebsd7_freebsd32_shmctl), 943 #endif 944 SYSCALL_INIT_LAST 945 }; 946 #endif 947 948 static int 949 shminit(void) 950 { 951 struct prison *pr; 952 void **rsv; 953 int i, error; 954 osd_method_t methods[PR_MAXMETHOD] = { 955 [PR_METHOD_CHECK] = shm_prison_check, 956 [PR_METHOD_SET] = shm_prison_set, 957 [PR_METHOD_GET] = shm_prison_get, 958 [PR_METHOD_REMOVE] = shm_prison_remove, 959 }; 960 961 #ifndef BURN_BRIDGES 962 if (TUNABLE_ULONG_FETCH("kern.ipc.shmmaxpgs", &shminfo.shmall) != 0) 963 printf("kern.ipc.shmmaxpgs is now called kern.ipc.shmall!\n"); 964 #endif 965 if (shminfo.shmmax == SHMMAX) { 966 /* Initialize shmmax dealing with possible overflow. */ 967 for (i = PAGE_SIZE; i != 0; i--) { 968 shminfo.shmmax = shminfo.shmall * i; 969 if ((shminfo.shmmax / shminfo.shmall) == (u_long)i) 970 break; 971 } 972 } 973 shmalloced = shminfo.shmmni; 974 shmsegs = malloc(shmalloced * sizeof(shmsegs[0]), M_SHM, 975 M_WAITOK|M_ZERO); 976 for (i = 0; i < shmalloced; i++) { 977 shmsegs[i].u.shm_perm.mode = SHMSEG_FREE; 978 shmsegs[i].u.shm_perm.seq = 0; 979 #ifdef MAC 980 mac_sysvshm_init(&shmsegs[i]); 981 #endif 982 } 983 shm_last_free = 0; 984 shm_nused = 0; 985 shm_committed = 0; 986 sx_init(&sysvshmsx, "sysvshmsx"); 987 #ifndef SYSVSHM 988 shmexit_hook = &shmexit_myhook; 989 shmfork_hook = &shmfork_myhook; 990 shmobjinfo_hook = &shmobjinfo_myhook; 991 #endif 992 993 /* Set current prisons according to their allow.sysvipc. */ 994 shm_prison_slot = osd_jail_register(NULL, methods); 995 rsv = osd_reserve(shm_prison_slot); 996 prison_lock(&prison0); 997 (void)osd_jail_set_reserved(&prison0, shm_prison_slot, rsv, &prison0); 998 prison_unlock(&prison0); 999 rsv = NULL; 1000 sx_slock(&allprison_lock); 1001 TAILQ_FOREACH(pr, &allprison, pr_list) { 1002 if (rsv == NULL) 1003 rsv = osd_reserve(shm_prison_slot); 1004 prison_lock(pr); 1005 if (pr->pr_allow & PR_ALLOW_SYSVIPC) { 1006 (void)osd_jail_set_reserved(pr, shm_prison_slot, rsv, 1007 &prison0); 1008 rsv = NULL; 1009 } 1010 prison_unlock(pr); 1011 } 1012 if (rsv != NULL) 1013 osd_free_reserved(rsv); 1014 sx_sunlock(&allprison_lock); 1015 1016 error = syscall_helper_register(shm_syscalls, SY_THR_STATIC_KLD); 1017 if (error != 0) 1018 return (error); 1019 #ifdef COMPAT_FREEBSD32 1020 error = syscall32_helper_register(shm32_syscalls, SY_THR_STATIC_KLD); 1021 if (error != 0) 1022 return (error); 1023 #endif 1024 return (0); 1025 } 1026 1027 static int 1028 shmunload(void) 1029 { 1030 int i; 1031 1032 if (shm_nused > 0) 1033 return (EBUSY); 1034 1035 #ifdef COMPAT_FREEBSD32 1036 syscall32_helper_unregister(shm32_syscalls); 1037 #endif 1038 syscall_helper_unregister(shm_syscalls); 1039 if (shm_prison_slot != 0) 1040 osd_jail_deregister(shm_prison_slot); 1041 1042 for (i = 0; i < shmalloced; i++) { 1043 #ifdef MAC 1044 mac_sysvshm_destroy(&shmsegs[i]); 1045 #endif 1046 /* 1047 * Objects might be still mapped into the processes 1048 * address spaces. Actual free would happen on the 1049 * last mapping destruction. 1050 */ 1051 if (shmsegs[i].u.shm_perm.mode != SHMSEG_FREE) 1052 vm_object_deallocate(shmsegs[i].object); 1053 } 1054 free(shmsegs, M_SHM); 1055 #ifndef SYSVSHM 1056 shmexit_hook = NULL; 1057 shmfork_hook = NULL; 1058 shmobjinfo_hook = NULL; 1059 #endif 1060 sx_destroy(&sysvshmsx); 1061 return (0); 1062 } 1063 1064 static int 1065 sysctl_shmsegs(SYSCTL_HANDLER_ARGS) 1066 { 1067 struct shmid_kernel tshmseg; 1068 #ifdef COMPAT_FREEBSD32 1069 struct shmid_kernel32 tshmseg32; 1070 #endif 1071 struct prison *pr, *rpr; 1072 void *outaddr; 1073 size_t outsize; 1074 int error, i; 1075 1076 SYSVSHM_LOCK(); 1077 pr = req->td->td_ucred->cr_prison; 1078 rpr = shm_find_prison(req->td->td_ucred); 1079 error = 0; 1080 for (i = 0; i < shmalloced; i++) { 1081 if ((shmsegs[i].u.shm_perm.mode & SHMSEG_ALLOCATED) == 0 || 1082 rpr == NULL || shm_prison_cansee(rpr, &shmsegs[i]) != 0) { 1083 bzero(&tshmseg, sizeof(tshmseg)); 1084 tshmseg.u.shm_perm.mode = SHMSEG_FREE; 1085 } else { 1086 tshmseg = shmsegs[i]; 1087 if (tshmseg.cred->cr_prison != pr) 1088 tshmseg.u.shm_perm.key = IPC_PRIVATE; 1089 } 1090 #ifdef COMPAT_FREEBSD32 1091 if (SV_CURPROC_FLAG(SV_ILP32)) { 1092 bzero(&tshmseg32, sizeof(tshmseg32)); 1093 freebsd32_ipcperm_out(&tshmseg.u.shm_perm, 1094 &tshmseg32.u.shm_perm); 1095 CP(tshmseg, tshmseg32, u.shm_segsz); 1096 CP(tshmseg, tshmseg32, u.shm_lpid); 1097 CP(tshmseg, tshmseg32, u.shm_cpid); 1098 CP(tshmseg, tshmseg32, u.shm_nattch); 1099 CP(tshmseg, tshmseg32, u.shm_atime); 1100 CP(tshmseg, tshmseg32, u.shm_dtime); 1101 CP(tshmseg, tshmseg32, u.shm_ctime); 1102 /* Don't copy object, label, or cred */ 1103 outaddr = &tshmseg32; 1104 outsize = sizeof(tshmseg32); 1105 } else 1106 #endif 1107 { 1108 tshmseg.object = NULL; 1109 tshmseg.label = NULL; 1110 tshmseg.cred = NULL; 1111 outaddr = &tshmseg; 1112 outsize = sizeof(tshmseg); 1113 } 1114 error = SYSCTL_OUT(req, outaddr, outsize); 1115 if (error != 0) 1116 break; 1117 } 1118 SYSVSHM_UNLOCK(); 1119 return (error); 1120 } 1121 1122 int 1123 kern_get_shmsegs(struct thread *td, struct shmid_kernel **res, size_t *sz) 1124 { 1125 struct shmid_kernel *pshmseg; 1126 struct prison *pr, *rpr; 1127 int i; 1128 1129 SYSVSHM_LOCK(); 1130 *sz = shmalloced; 1131 if (res == NULL) 1132 goto out; 1133 1134 pr = td->td_ucred->cr_prison; 1135 rpr = shm_find_prison(td->td_ucred); 1136 *res = malloc(sizeof(struct shmid_kernel) * shmalloced, M_TEMP, 1137 M_WAITOK); 1138 for (i = 0; i < shmalloced; i++) { 1139 pshmseg = &(*res)[i]; 1140 if ((shmsegs[i].u.shm_perm.mode & SHMSEG_ALLOCATED) == 0 || 1141 rpr == NULL || shm_prison_cansee(rpr, &shmsegs[i]) != 0) { 1142 bzero(pshmseg, sizeof(*pshmseg)); 1143 pshmseg->u.shm_perm.mode = SHMSEG_FREE; 1144 } else { 1145 *pshmseg = shmsegs[i]; 1146 if (pshmseg->cred->cr_prison != pr) 1147 pshmseg->u.shm_perm.key = IPC_PRIVATE; 1148 } 1149 pshmseg->object = NULL; 1150 pshmseg->label = NULL; 1151 pshmseg->cred = NULL; 1152 } 1153 out: 1154 SYSVSHM_UNLOCK(); 1155 return (0); 1156 } 1157 1158 static int 1159 shm_prison_check(void *obj, void *data) 1160 { 1161 struct prison *pr = obj; 1162 struct prison *prpr; 1163 struct vfsoptlist *opts = data; 1164 int error, jsys; 1165 1166 /* 1167 * sysvshm is a jailsys integer. 1168 * It must be "disable" if the parent jail is disabled. 1169 */ 1170 error = vfs_copyopt(opts, "sysvshm", &jsys, sizeof(jsys)); 1171 if (error != ENOENT) { 1172 if (error != 0) 1173 return (error); 1174 switch (jsys) { 1175 case JAIL_SYS_DISABLE: 1176 break; 1177 case JAIL_SYS_NEW: 1178 case JAIL_SYS_INHERIT: 1179 prison_lock(pr->pr_parent); 1180 prpr = osd_jail_get(pr->pr_parent, shm_prison_slot); 1181 prison_unlock(pr->pr_parent); 1182 if (prpr == NULL) 1183 return (EPERM); 1184 break; 1185 default: 1186 return (EINVAL); 1187 } 1188 } 1189 1190 return (0); 1191 } 1192 1193 static int 1194 shm_prison_set(void *obj, void *data) 1195 { 1196 struct prison *pr = obj; 1197 struct prison *tpr, *orpr, *nrpr, *trpr; 1198 struct vfsoptlist *opts = data; 1199 void *rsv; 1200 int jsys, descend; 1201 1202 /* 1203 * sysvshm controls which jail is the root of the associated segments 1204 * (this jail or same as the parent), or if the feature is available 1205 * at all. 1206 */ 1207 if (vfs_copyopt(opts, "sysvshm", &jsys, sizeof(jsys)) == ENOENT) 1208 jsys = vfs_flagopt(opts, "allow.sysvipc", NULL, 0) 1209 ? JAIL_SYS_INHERIT 1210 : vfs_flagopt(opts, "allow.nosysvipc", NULL, 0) 1211 ? JAIL_SYS_DISABLE 1212 : -1; 1213 if (jsys == JAIL_SYS_DISABLE) { 1214 prison_lock(pr); 1215 orpr = osd_jail_get(pr, shm_prison_slot); 1216 if (orpr != NULL) 1217 osd_jail_del(pr, shm_prison_slot); 1218 prison_unlock(pr); 1219 if (orpr != NULL) { 1220 if (orpr == pr) 1221 shm_prison_cleanup(pr); 1222 /* Disable all child jails as well. */ 1223 FOREACH_PRISON_DESCENDANT(pr, tpr, descend) { 1224 prison_lock(tpr); 1225 trpr = osd_jail_get(tpr, shm_prison_slot); 1226 if (trpr != NULL) { 1227 osd_jail_del(tpr, shm_prison_slot); 1228 prison_unlock(tpr); 1229 if (trpr == tpr) 1230 shm_prison_cleanup(tpr); 1231 } else { 1232 prison_unlock(tpr); 1233 descend = 0; 1234 } 1235 } 1236 } 1237 } else if (jsys != -1) { 1238 if (jsys == JAIL_SYS_NEW) 1239 nrpr = pr; 1240 else { 1241 prison_lock(pr->pr_parent); 1242 nrpr = osd_jail_get(pr->pr_parent, shm_prison_slot); 1243 prison_unlock(pr->pr_parent); 1244 } 1245 rsv = osd_reserve(shm_prison_slot); 1246 prison_lock(pr); 1247 orpr = osd_jail_get(pr, shm_prison_slot); 1248 if (orpr != nrpr) 1249 (void)osd_jail_set_reserved(pr, shm_prison_slot, rsv, 1250 nrpr); 1251 else 1252 osd_free_reserved(rsv); 1253 prison_unlock(pr); 1254 if (orpr != nrpr) { 1255 if (orpr == pr) 1256 shm_prison_cleanup(pr); 1257 if (orpr != NULL) { 1258 /* Change child jails matching the old root, */ 1259 FOREACH_PRISON_DESCENDANT(pr, tpr, descend) { 1260 prison_lock(tpr); 1261 trpr = osd_jail_get(tpr, 1262 shm_prison_slot); 1263 if (trpr == orpr) { 1264 (void)osd_jail_set(tpr, 1265 shm_prison_slot, nrpr); 1266 prison_unlock(tpr); 1267 if (trpr == tpr) 1268 shm_prison_cleanup(tpr); 1269 } else { 1270 prison_unlock(tpr); 1271 descend = 0; 1272 } 1273 } 1274 } 1275 } 1276 } 1277 1278 return (0); 1279 } 1280 1281 static int 1282 shm_prison_get(void *obj, void *data) 1283 { 1284 struct prison *pr = obj; 1285 struct prison *rpr; 1286 struct vfsoptlist *opts = data; 1287 int error, jsys; 1288 1289 /* Set sysvshm based on the jail's root prison. */ 1290 prison_lock(pr); 1291 rpr = osd_jail_get(pr, shm_prison_slot); 1292 prison_unlock(pr); 1293 jsys = rpr == NULL ? JAIL_SYS_DISABLE 1294 : rpr == pr ? JAIL_SYS_NEW : JAIL_SYS_INHERIT; 1295 error = vfs_setopt(opts, "sysvshm", &jsys, sizeof(jsys)); 1296 if (error == ENOENT) 1297 error = 0; 1298 return (error); 1299 } 1300 1301 static int 1302 shm_prison_remove(void *obj, void *data __unused) 1303 { 1304 struct prison *pr = obj; 1305 struct prison *rpr; 1306 1307 SYSVSHM_LOCK(); 1308 prison_lock(pr); 1309 rpr = osd_jail_get(pr, shm_prison_slot); 1310 prison_unlock(pr); 1311 if (rpr == pr) 1312 shm_prison_cleanup(pr); 1313 SYSVSHM_UNLOCK(); 1314 return (0); 1315 } 1316 1317 static void 1318 shm_prison_cleanup(struct prison *pr) 1319 { 1320 struct shmid_kernel *shmseg; 1321 int i; 1322 1323 /* Remove any segments that belong to this jail. */ 1324 for (i = 0; i < shmalloced; i++) { 1325 shmseg = &shmsegs[i]; 1326 if ((shmseg->u.shm_perm.mode & SHMSEG_ALLOCATED) && 1327 shmseg->cred != NULL && shmseg->cred->cr_prison == pr) { 1328 shm_remove(shmseg, i); 1329 } 1330 } 1331 } 1332 1333 SYSCTL_JAIL_PARAM_SYS_NODE(sysvshm, CTLFLAG_RW, "SYSV shared memory"); 1334 1335 #if defined(__i386__) && (defined(COMPAT_FREEBSD4) || defined(COMPAT_43)) 1336 struct oshmid_ds { 1337 struct ipc_perm_old shm_perm; /* operation perms */ 1338 int shm_segsz; /* size of segment (bytes) */ 1339 u_short shm_cpid; /* pid, creator */ 1340 u_short shm_lpid; /* pid, last operation */ 1341 short shm_nattch; /* no. of current attaches */ 1342 time_t shm_atime; /* last attach time */ 1343 time_t shm_dtime; /* last detach time */ 1344 time_t shm_ctime; /* last change time */ 1345 void *shm_handle; /* internal handle for shm segment */ 1346 }; 1347 1348 struct oshmctl_args { 1349 int shmid; 1350 int cmd; 1351 struct oshmid_ds *ubuf; 1352 }; 1353 1354 static int 1355 oshmctl(struct thread *td, struct oshmctl_args *uap) 1356 { 1357 #ifdef COMPAT_43 1358 int error = 0; 1359 struct prison *rpr; 1360 struct shmid_kernel *shmseg; 1361 struct oshmid_ds outbuf; 1362 1363 rpr = shm_find_prison(td->td_ucred); 1364 if (rpr == NULL) 1365 return (ENOSYS); 1366 if (uap->cmd != IPC_STAT) { 1367 return (freebsd7_shmctl(td, 1368 (struct freebsd7_shmctl_args *)uap)); 1369 } 1370 SYSVSHM_LOCK(); 1371 shmseg = shm_find_segment(rpr, uap->shmid, true); 1372 if (shmseg == NULL) { 1373 SYSVSHM_UNLOCK(); 1374 return (EINVAL); 1375 } 1376 error = ipcperm(td, &shmseg->u.shm_perm, IPC_R); 1377 if (error != 0) { 1378 SYSVSHM_UNLOCK(); 1379 return (error); 1380 } 1381 #ifdef MAC 1382 error = mac_sysvshm_check_shmctl(td->td_ucred, shmseg, uap->cmd); 1383 if (error != 0) { 1384 SYSVSHM_UNLOCK(); 1385 return (error); 1386 } 1387 #endif 1388 ipcperm_new2old(&shmseg->u.shm_perm, &outbuf.shm_perm); 1389 outbuf.shm_segsz = shmseg->u.shm_segsz; 1390 outbuf.shm_cpid = shmseg->u.shm_cpid; 1391 outbuf.shm_lpid = shmseg->u.shm_lpid; 1392 outbuf.shm_nattch = shmseg->u.shm_nattch; 1393 outbuf.shm_atime = shmseg->u.shm_atime; 1394 outbuf.shm_dtime = shmseg->u.shm_dtime; 1395 outbuf.shm_ctime = shmseg->u.shm_ctime; 1396 outbuf.shm_handle = shmseg->object; 1397 SYSVSHM_UNLOCK(); 1398 return (copyout(&outbuf, uap->ubuf, sizeof(outbuf))); 1399 #else 1400 return (EINVAL); 1401 #endif 1402 } 1403 1404 /* XXX casting to (sy_call_t *) is bogus, as usual. */ 1405 static sy_call_t *shmcalls[] = { 1406 (sy_call_t *)sys_shmat, (sy_call_t *)oshmctl, 1407 (sy_call_t *)sys_shmdt, (sy_call_t *)sys_shmget, 1408 (sy_call_t *)freebsd7_shmctl 1409 }; 1410 1411 #ifndef _SYS_SYSPROTO_H_ 1412 /* XXX actually varargs. */ 1413 struct shmsys_args { 1414 int which; 1415 int a2; 1416 int a3; 1417 int a4; 1418 }; 1419 #endif 1420 int 1421 sys_shmsys(struct thread *td, struct shmsys_args *uap) 1422 { 1423 1424 AUDIT_ARG_SVIPC_WHICH(uap->which); 1425 if (uap->which < 0 || uap->which >= nitems(shmcalls)) 1426 return (EINVAL); 1427 return ((*shmcalls[uap->which])(td, &uap->a2)); 1428 } 1429 1430 #endif /* i386 && (COMPAT_FREEBSD4 || COMPAT_43) */ 1431 1432 #ifdef COMPAT_FREEBSD32 1433 1434 int 1435 freebsd32_shmsys(struct thread *td, struct freebsd32_shmsys_args *uap) 1436 { 1437 1438 #if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \ 1439 defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD7) 1440 AUDIT_ARG_SVIPC_WHICH(uap->which); 1441 switch (uap->which) { 1442 case 0: { /* shmat */ 1443 struct shmat_args ap; 1444 1445 ap.shmid = uap->a2; 1446 ap.shmaddr = PTRIN(uap->a3); 1447 ap.shmflg = uap->a4; 1448 return (sysent[SYS_shmat].sy_call(td, &ap)); 1449 } 1450 case 2: { /* shmdt */ 1451 struct shmdt_args ap; 1452 1453 ap.shmaddr = PTRIN(uap->a2); 1454 return (sysent[SYS_shmdt].sy_call(td, &ap)); 1455 } 1456 case 3: { /* shmget */ 1457 struct shmget_args ap; 1458 1459 ap.key = uap->a2; 1460 ap.size = uap->a3; 1461 ap.shmflg = uap->a4; 1462 return (sysent[SYS_shmget].sy_call(td, &ap)); 1463 } 1464 case 4: { /* shmctl */ 1465 struct freebsd7_freebsd32_shmctl_args ap; 1466 1467 ap.shmid = uap->a2; 1468 ap.cmd = uap->a3; 1469 ap.buf = PTRIN(uap->a4); 1470 return (freebsd7_freebsd32_shmctl(td, &ap)); 1471 } 1472 case 1: /* oshmctl */ 1473 default: 1474 return (EINVAL); 1475 } 1476 #else 1477 return (nosys(td, NULL)); 1478 #endif 1479 } 1480 1481 #if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \ 1482 defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD7) 1483 int 1484 freebsd7_freebsd32_shmctl(struct thread *td, 1485 struct freebsd7_freebsd32_shmctl_args *uap) 1486 { 1487 int error; 1488 union { 1489 struct shmid_ds shmid_ds; 1490 struct shm_info shm_info; 1491 struct shminfo shminfo; 1492 } u; 1493 union { 1494 struct shmid_ds_old32 shmid_ds32; 1495 struct shm_info32 shm_info32; 1496 struct shminfo32 shminfo32; 1497 } u32; 1498 size_t sz; 1499 1500 if (uap->cmd == IPC_SET) { 1501 if ((error = copyin(uap->buf, &u32.shmid_ds32, 1502 sizeof(u32.shmid_ds32)))) 1503 goto done; 1504 freebsd32_ipcperm_old_in(&u32.shmid_ds32.shm_perm, 1505 &u.shmid_ds.shm_perm); 1506 CP(u32.shmid_ds32, u.shmid_ds, shm_segsz); 1507 CP(u32.shmid_ds32, u.shmid_ds, shm_lpid); 1508 CP(u32.shmid_ds32, u.shmid_ds, shm_cpid); 1509 CP(u32.shmid_ds32, u.shmid_ds, shm_nattch); 1510 CP(u32.shmid_ds32, u.shmid_ds, shm_atime); 1511 CP(u32.shmid_ds32, u.shmid_ds, shm_dtime); 1512 CP(u32.shmid_ds32, u.shmid_ds, shm_ctime); 1513 } 1514 1515 error = kern_shmctl(td, uap->shmid, uap->cmd, (void *)&u, &sz); 1516 if (error) 1517 goto done; 1518 1519 /* Cases in which we need to copyout */ 1520 switch (uap->cmd) { 1521 case IPC_INFO: 1522 CP(u.shminfo, u32.shminfo32, shmmax); 1523 CP(u.shminfo, u32.shminfo32, shmmin); 1524 CP(u.shminfo, u32.shminfo32, shmmni); 1525 CP(u.shminfo, u32.shminfo32, shmseg); 1526 CP(u.shminfo, u32.shminfo32, shmall); 1527 error = copyout(&u32.shminfo32, uap->buf, 1528 sizeof(u32.shminfo32)); 1529 break; 1530 case SHM_INFO: 1531 CP(u.shm_info, u32.shm_info32, used_ids); 1532 CP(u.shm_info, u32.shm_info32, shm_rss); 1533 CP(u.shm_info, u32.shm_info32, shm_tot); 1534 CP(u.shm_info, u32.shm_info32, shm_swp); 1535 CP(u.shm_info, u32.shm_info32, swap_attempts); 1536 CP(u.shm_info, u32.shm_info32, swap_successes); 1537 error = copyout(&u32.shm_info32, uap->buf, 1538 sizeof(u32.shm_info32)); 1539 break; 1540 case SHM_STAT: 1541 case IPC_STAT: 1542 memset(&u32.shmid_ds32, 0, sizeof(u32.shmid_ds32)); 1543 freebsd32_ipcperm_old_out(&u.shmid_ds.shm_perm, 1544 &u32.shmid_ds32.shm_perm); 1545 if (u.shmid_ds.shm_segsz > INT32_MAX) 1546 u32.shmid_ds32.shm_segsz = INT32_MAX; 1547 else 1548 CP(u.shmid_ds, u32.shmid_ds32, shm_segsz); 1549 CP(u.shmid_ds, u32.shmid_ds32, shm_lpid); 1550 CP(u.shmid_ds, u32.shmid_ds32, shm_cpid); 1551 CP(u.shmid_ds, u32.shmid_ds32, shm_nattch); 1552 CP(u.shmid_ds, u32.shmid_ds32, shm_atime); 1553 CP(u.shmid_ds, u32.shmid_ds32, shm_dtime); 1554 CP(u.shmid_ds, u32.shmid_ds32, shm_ctime); 1555 u32.shmid_ds32.shm_internal = 0; 1556 error = copyout(&u32.shmid_ds32, uap->buf, 1557 sizeof(u32.shmid_ds32)); 1558 break; 1559 } 1560 1561 done: 1562 if (error) { 1563 /* Invalidate the return value */ 1564 td->td_retval[0] = -1; 1565 } 1566 return (error); 1567 } 1568 #endif 1569 1570 int 1571 freebsd32_shmctl(struct thread *td, struct freebsd32_shmctl_args *uap) 1572 { 1573 int error; 1574 union { 1575 struct shmid_ds shmid_ds; 1576 struct shm_info shm_info; 1577 struct shminfo shminfo; 1578 } u; 1579 union { 1580 struct shmid_ds32 shmid_ds32; 1581 struct shm_info32 shm_info32; 1582 struct shminfo32 shminfo32; 1583 } u32; 1584 size_t sz; 1585 1586 if (uap->cmd == IPC_SET) { 1587 if ((error = copyin(uap->buf, &u32.shmid_ds32, 1588 sizeof(u32.shmid_ds32)))) 1589 goto done; 1590 freebsd32_ipcperm_in(&u32.shmid_ds32.shm_perm, 1591 &u.shmid_ds.shm_perm); 1592 CP(u32.shmid_ds32, u.shmid_ds, shm_segsz); 1593 CP(u32.shmid_ds32, u.shmid_ds, shm_lpid); 1594 CP(u32.shmid_ds32, u.shmid_ds, shm_cpid); 1595 CP(u32.shmid_ds32, u.shmid_ds, shm_nattch); 1596 CP(u32.shmid_ds32, u.shmid_ds, shm_atime); 1597 CP(u32.shmid_ds32, u.shmid_ds, shm_dtime); 1598 CP(u32.shmid_ds32, u.shmid_ds, shm_ctime); 1599 } 1600 1601 error = kern_shmctl(td, uap->shmid, uap->cmd, (void *)&u, &sz); 1602 if (error) 1603 goto done; 1604 1605 /* Cases in which we need to copyout */ 1606 switch (uap->cmd) { 1607 case IPC_INFO: 1608 CP(u.shminfo, u32.shminfo32, shmmax); 1609 CP(u.shminfo, u32.shminfo32, shmmin); 1610 CP(u.shminfo, u32.shminfo32, shmmni); 1611 CP(u.shminfo, u32.shminfo32, shmseg); 1612 CP(u.shminfo, u32.shminfo32, shmall); 1613 error = copyout(&u32.shminfo32, uap->buf, 1614 sizeof(u32.shminfo32)); 1615 break; 1616 case SHM_INFO: 1617 CP(u.shm_info, u32.shm_info32, used_ids); 1618 CP(u.shm_info, u32.shm_info32, shm_rss); 1619 CP(u.shm_info, u32.shm_info32, shm_tot); 1620 CP(u.shm_info, u32.shm_info32, shm_swp); 1621 CP(u.shm_info, u32.shm_info32, swap_attempts); 1622 CP(u.shm_info, u32.shm_info32, swap_successes); 1623 error = copyout(&u32.shm_info32, uap->buf, 1624 sizeof(u32.shm_info32)); 1625 break; 1626 case SHM_STAT: 1627 case IPC_STAT: 1628 freebsd32_ipcperm_out(&u.shmid_ds.shm_perm, 1629 &u32.shmid_ds32.shm_perm); 1630 if (u.shmid_ds.shm_segsz > INT32_MAX) 1631 u32.shmid_ds32.shm_segsz = INT32_MAX; 1632 else 1633 CP(u.shmid_ds, u32.shmid_ds32, shm_segsz); 1634 CP(u.shmid_ds, u32.shmid_ds32, shm_lpid); 1635 CP(u.shmid_ds, u32.shmid_ds32, shm_cpid); 1636 CP(u.shmid_ds, u32.shmid_ds32, shm_nattch); 1637 CP(u.shmid_ds, u32.shmid_ds32, shm_atime); 1638 CP(u.shmid_ds, u32.shmid_ds32, shm_dtime); 1639 CP(u.shmid_ds, u32.shmid_ds32, shm_ctime); 1640 error = copyout(&u32.shmid_ds32, uap->buf, 1641 sizeof(u32.shmid_ds32)); 1642 break; 1643 } 1644 1645 done: 1646 if (error) { 1647 /* Invalidate the return value */ 1648 td->td_retval[0] = -1; 1649 } 1650 return (error); 1651 } 1652 #endif 1653 1654 #if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \ 1655 defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD7) 1656 1657 #ifndef _SYS_SYSPROTO_H_ 1658 struct freebsd7_shmctl_args { 1659 int shmid; 1660 int cmd; 1661 struct shmid_ds_old *buf; 1662 }; 1663 #endif 1664 int 1665 freebsd7_shmctl(struct thread *td, struct freebsd7_shmctl_args *uap) 1666 { 1667 int error; 1668 struct shmid_ds_old old; 1669 struct shmid_ds buf; 1670 size_t bufsz; 1671 1672 /* 1673 * The only reason IPC_INFO, SHM_INFO, SHM_STAT exists is to support 1674 * Linux binaries. If we see the call come through the FreeBSD ABI, 1675 * return an error back to the user since we do not to support this. 1676 */ 1677 if (uap->cmd == IPC_INFO || uap->cmd == SHM_INFO || 1678 uap->cmd == SHM_STAT) 1679 return (EINVAL); 1680 1681 /* IPC_SET needs to copyin the buffer before calling kern_shmctl */ 1682 if (uap->cmd == IPC_SET) { 1683 if ((error = copyin(uap->buf, &old, sizeof(old)))) 1684 goto done; 1685 ipcperm_old2new(&old.shm_perm, &buf.shm_perm); 1686 CP(old, buf, shm_segsz); 1687 CP(old, buf, shm_lpid); 1688 CP(old, buf, shm_cpid); 1689 CP(old, buf, shm_nattch); 1690 CP(old, buf, shm_atime); 1691 CP(old, buf, shm_dtime); 1692 CP(old, buf, shm_ctime); 1693 } 1694 1695 error = kern_shmctl(td, uap->shmid, uap->cmd, (void *)&buf, &bufsz); 1696 if (error) 1697 goto done; 1698 1699 /* Cases in which we need to copyout */ 1700 switch (uap->cmd) { 1701 case IPC_STAT: 1702 memset(&old, 0, sizeof(old)); 1703 ipcperm_new2old(&buf.shm_perm, &old.shm_perm); 1704 if (buf.shm_segsz > INT_MAX) 1705 old.shm_segsz = INT_MAX; 1706 else 1707 CP(buf, old, shm_segsz); 1708 CP(buf, old, shm_lpid); 1709 CP(buf, old, shm_cpid); 1710 if (buf.shm_nattch > SHRT_MAX) 1711 old.shm_nattch = SHRT_MAX; 1712 else 1713 CP(buf, old, shm_nattch); 1714 CP(buf, old, shm_atime); 1715 CP(buf, old, shm_dtime); 1716 CP(buf, old, shm_ctime); 1717 old.shm_internal = NULL; 1718 error = copyout(&old, uap->buf, sizeof(old)); 1719 break; 1720 } 1721 1722 done: 1723 if (error) { 1724 /* Invalidate the return value */ 1725 td->td_retval[0] = -1; 1726 } 1727 return (error); 1728 } 1729 1730 #endif /* COMPAT_FREEBSD4 || COMPAT_FREEBSD5 || COMPAT_FREEBSD6 || 1731 COMPAT_FREEBSD7 */ 1732 1733 static int 1734 sysvshm_modload(struct module *module, int cmd, void *arg) 1735 { 1736 int error = 0; 1737 1738 switch (cmd) { 1739 case MOD_LOAD: 1740 error = shminit(); 1741 if (error != 0) 1742 shmunload(); 1743 break; 1744 case MOD_UNLOAD: 1745 error = shmunload(); 1746 break; 1747 case MOD_SHUTDOWN: 1748 break; 1749 default: 1750 error = EINVAL; 1751 break; 1752 } 1753 return (error); 1754 } 1755 1756 static moduledata_t sysvshm_mod = { 1757 "sysvshm", 1758 &sysvshm_modload, 1759 NULL 1760 }; 1761 1762 DECLARE_MODULE(sysvshm, sysvshm_mod, SI_SUB_SYSV_SHM, SI_ORDER_FIRST); 1763 MODULE_VERSION(sysvshm, 1); 1764