1 /* 2 * Copyright (c) 1988 University of Utah. 3 * Copyright (c) 1990 The Regents of the University of California. 4 * All rights reserved. 5 * 6 * This code is derived from software contributed to Berkeley by 7 * the Systems Programming Group of the University of Utah Computer 8 * Science Department. Originally from University of Wisconsin. 9 * 10 * %sccs.include.redist.c% 11 * 12 * from: Utah $Hdr: uipc_shm.c 1.9 89/08/14$ 13 * 14 * @(#)sysv_shm.c 7.3 (Berkeley) 05/25/90 15 */ 16 17 /* 18 * System V shared memory routines. 19 */ 20 21 #ifdef SYSVSHM 22 23 #include "machine/pte.h" 24 25 #include "param.h" 26 #include "systm.h" 27 #include "user.h" 28 #include "kernel.h" 29 #include "proc.h" 30 #include "vm.h" 31 #include "shm.h" 32 #include "mapmem.h" 33 #include "malloc.h" 34 35 #ifdef HPUXCOMPAT 36 #include "../hpux/hpux.h" 37 #endif 38 39 int shmat(), shmctl(), shmdt(), shmget(); 40 int (*shmcalls[])() = { shmat, shmctl, shmdt, shmget }; 41 int shmtot = 0; 42 43 int shmfork(), shmexit(); 44 struct mapmemops shmops = { shmfork, (int (*)())0, shmexit, shmexit }; 45 46 shminit() 47 { 48 register int i; 49 50 if (shminfo.shmmni > SHMMMNI) 51 shminfo.shmmni = SHMMMNI; 52 for (i = 0; i < shminfo.shmmni; i++) { 53 shmsegs[i].shm_perm.mode = 0; 54 shmsegs[i].shm_perm.seq = 0; 55 } 56 } 57 58 /* entry point for all SHM calls */ 59 shmsys() 60 { 61 struct a { 62 int which; 63 } *uap = (struct a *)u.u_ap; 64 65 if (uap->which >= sizeof(shmcalls)/sizeof(shmcalls[0])) { 66 u.u_error = EINVAL; 67 return; 68 } 69 (*shmcalls[uap->which])(u.u_ap+1); 70 } 71 72 /* get a shared memory segment */ 73 shmget(ap) 74 int *ap; 75 { 76 register struct a { 77 key_t key; 78 int size; 79 int shmflg; 80 } *uap = (struct a *)ap; 81 register struct shmid_ds *shp; 82 register int i; 83 int rval = 0, size; 84 caddr_t kva; 85 86 /* look up the specified shm_id */ 87 if (uap->key != IPC_PRIVATE) { 88 for (i = 0; i < shminfo.shmmni; i++) 89 if ((shmsegs[i].shm_perm.mode & SHM_ALLOC) && 90 shmsegs[i].shm_perm.key == uap->key) { 91 rval = i; 92 break; 93 } 94 } else 95 i = shminfo.shmmni; 96 97 /* create a new shared segment if necessary */ 98 if (i == shminfo.shmmni) { 99 if ((uap->shmflg & IPC_CREAT) == 0) { 100 u.u_error = ENOENT; 101 return; 102 } 103 if (uap->size < shminfo.shmmin || uap->size > shminfo.shmmax) { 104 u.u_error = EINVAL; 105 return; 106 } 107 for (i = 0; i < shminfo.shmmni; i++) 108 if ((shmsegs[i].shm_perm.mode & SHM_ALLOC) == 0) { 109 rval = i; 110 break; 111 } 112 if (i == shminfo.shmmni) { 113 u.u_error = ENOSPC; 114 return; 115 } 116 size = clrnd(btoc(uap->size)); 117 if (shmtot + size > shminfo.shmall) { 118 u.u_error = ENOMEM; 119 return; 120 } 121 shp = &shmsegs[rval]; 122 /* 123 * We need to do a couple of things to ensure consistency 124 * in case we sleep in malloc(). We mark segment as 125 * allocated so that other shmgets() will not allocate it. 126 * We mark it as "destroyed" to insure that shmvalid() is 127 * false making most operations fail (XXX). We set the key, 128 * so that other shmget()s will fail. 129 */ 130 shp->shm_perm.mode = SHM_ALLOC | SHM_DEST; 131 shp->shm_perm.key = uap->key; 132 kva = (caddr_t) malloc((u_long)ctob(size), M_SHM, M_WAITOK); 133 if (kva == NULL) { 134 shp->shm_perm.mode = 0; 135 u.u_error = ENOMEM; 136 return; 137 } 138 if (!claligned(kva)) 139 panic("shmget: non-aligned memory"); 140 bzero(kva, (u_int)ctob(size)); 141 shmtot += size; 142 shp->shm_perm.cuid = shp->shm_perm.uid = u.u_uid; 143 shp->shm_perm.cgid = shp->shm_perm.gid = u.u_gid; 144 shp->shm_perm.mode = SHM_ALLOC | (uap->shmflg&0777); 145 shp->shm_handle = (void *) kvtopte(kva); 146 shp->shm_segsz = uap->size; 147 shp->shm_cpid = u.u_procp->p_pid; 148 shp->shm_lpid = shp->shm_nattch = 0; 149 shp->shm_atime = shp->shm_dtime = 0; 150 shp->shm_ctime = time.tv_sec; 151 } else { 152 shp = &shmsegs[rval]; 153 /* XXX: probably not the right thing to do */ 154 if (shp->shm_perm.mode & SHM_DEST) { 155 u.u_error = EBUSY; 156 return; 157 } 158 if (!ipcaccess(&shp->shm_perm, uap->shmflg&0777)) 159 return; 160 if (uap->size && uap->size > shp->shm_segsz) { 161 u.u_error = EINVAL; 162 return; 163 } 164 if ((uap->shmflg&IPC_CREAT) && (uap->shmflg&IPC_EXCL)) { 165 u.u_error = EEXIST; 166 return; 167 } 168 } 169 u.u_r.r_val1 = shp->shm_perm.seq * SHMMMNI + rval; 170 } 171 172 /* shared memory control */ 173 shmctl(ap) 174 int *ap; 175 { 176 register struct a { 177 int shmid; 178 int cmd; 179 caddr_t buf; 180 } *uap = (struct a *)ap; 181 register struct shmid_ds *shp; 182 struct shmid_ds sbuf; 183 184 if (!shmvalid(uap->shmid)) 185 return; 186 shp = &shmsegs[uap->shmid % SHMMMNI]; 187 switch (uap->cmd) { 188 case IPC_STAT: 189 if (ipcaccess(&shp->shm_perm, IPC_R)) 190 u.u_error = 191 copyout((caddr_t)shp, uap->buf, sizeof(*shp)); 192 break; 193 194 case IPC_SET: 195 if (u.u_uid && u.u_uid != shp->shm_perm.uid && 196 u.u_uid != shp->shm_perm.cuid) { 197 u.u_error = EPERM; 198 break; 199 } 200 u.u_error = copyin(uap->buf, (caddr_t)&sbuf, sizeof sbuf); 201 if (!u.u_error) { 202 shp->shm_perm.uid = sbuf.shm_perm.uid; 203 shp->shm_perm.gid = sbuf.shm_perm.gid; 204 shp->shm_perm.mode = (shp->shm_perm.mode & ~0777) 205 | (sbuf.shm_perm.mode & 0777); 206 shp->shm_ctime = time.tv_sec; 207 } 208 break; 209 210 case IPC_RMID: 211 if (u.u_uid && u.u_uid != shp->shm_perm.uid && 212 u.u_uid != shp->shm_perm.cuid) { 213 u.u_error = EPERM; 214 break; 215 } 216 /* set ctime? */ 217 shp->shm_perm.key = IPC_PRIVATE; 218 shp->shm_perm.mode |= SHM_DEST; 219 if (shp->shm_nattch <= 0) 220 shmfree(shp); 221 break; 222 223 #ifdef HPUXCOMPAT 224 case SHM_LOCK: 225 case SHM_UNLOCK: 226 /* don't really do anything, but make them think we did */ 227 if ((u.u_procp->p_flag & SHPUX) == 0) 228 u.u_error = EINVAL; 229 else if (u.u_uid && u.u_uid != shp->shm_perm.uid && 230 u.u_uid != shp->shm_perm.cuid) 231 u.u_error = EPERM; 232 break; 233 #endif 234 235 default: 236 u.u_error = EINVAL; 237 break; 238 } 239 } 240 241 shmat(ap) 242 int *ap; 243 { 244 struct a { 245 int shmid; 246 caddr_t shmaddr; 247 int shmflg; 248 } *uap = (struct a *)ap; 249 register struct shmid_ds *shp; 250 register int size; 251 struct mapmem *mp; 252 caddr_t uva; 253 int prot, shmmapin(); 254 255 if (!shmvalid(uap->shmid)) 256 return; 257 shp = &shmsegs[uap->shmid % SHMMMNI]; 258 if (shp->shm_handle == NULL) 259 panic("shmat NULL handle"); 260 if (!ipcaccess(&shp->shm_perm, 261 (uap->shmflg&SHM_RDONLY) ? IPC_R : IPC_R|IPC_W)) 262 return; 263 uva = uap->shmaddr; 264 if (uva && ((int)uva & (SHMLBA-1))) { 265 if (uap->shmflg & SHM_RND) 266 uva = (caddr_t) ((int)uva & ~(SHMLBA-1)); 267 else { 268 u.u_error = EINVAL; 269 return; 270 } 271 } 272 /* 273 * Make sure user doesn't use more than their fair share 274 */ 275 size = 0; 276 for (mp = u.u_mmap; mp; mp = mp->mm_next) 277 if (mp->mm_ops == &shmops) 278 size++; 279 if (size >= shminfo.shmseg) { 280 u.u_error = EMFILE; 281 return; 282 } 283 /* 284 * Allocate a mapped memory region descriptor and 285 * attempt to expand the user page table to allow for region 286 */ 287 prot = (uap->shmflg & SHM_RDONLY) ? MM_RO : MM_RW; 288 #if defined(hp300) 289 prot |= MM_CI; 290 #endif 291 size = ctob(clrnd(btoc(shp->shm_segsz))); 292 mp = mmalloc(uap->shmid, &uva, (segsz_t)size, prot, &shmops); 293 if (mp == MMNIL) 294 return; 295 if (!mmmapin(mp, shmmapin)) { 296 mmfree(mp); 297 return; 298 } 299 /* 300 * Fill in the remaining fields 301 */ 302 shp->shm_lpid = u.u_procp->p_pid; 303 shp->shm_atime = time.tv_sec; 304 shp->shm_nattch++; 305 u.u_r.r_val1 = (int) uva; 306 } 307 308 shmdt(ap) 309 int *ap; 310 { 311 register struct a { 312 caddr_t shmaddr; 313 } *uap = (struct a *)ap; 314 register struct mapmem *mp; 315 316 for (mp = u.u_mmap; mp; mp = mp->mm_next) 317 if (mp->mm_ops == &shmops && mp->mm_uva == uap->shmaddr) 318 break; 319 if (mp == MMNIL) { 320 u.u_error = EINVAL; 321 return; 322 } 323 shmsegs[mp->mm_id % SHMMMNI].shm_lpid = u.u_procp->p_pid; 324 shmufree(mp); 325 } 326 327 shmmapin(mp, off) 328 struct mapmem *mp; 329 { 330 register struct shmid_ds *shp; 331 332 shp = &shmsegs[mp->mm_id % SHMMMNI]; 333 if (off >= ctob(clrnd(btoc(shp->shm_segsz)))) 334 return(-1); 335 return(((struct pte *)shp->shm_handle)[btop(off)].pg_pfnum); 336 } 337 338 /* 339 * Increment attach count on fork 340 */ 341 shmfork(mp, ischild) 342 register struct mapmem *mp; 343 { 344 if (!ischild) 345 shmsegs[mp->mm_id % SHMMMNI].shm_nattch++; 346 } 347 348 /* 349 * Detach from shared memory segment on exit (or exec) 350 */ 351 shmexit(mp) 352 register struct mapmem *mp; 353 { 354 shmufree(mp); 355 } 356 357 shmvalid(id) 358 register int id; 359 { 360 register struct shmid_ds *shp; 361 362 if (id < 0 || (id % SHMMMNI) >= shminfo.shmmni) 363 return(0); 364 shp = &shmsegs[id % SHMMMNI]; 365 if (shp->shm_perm.seq == (id / SHMMMNI) && 366 (shp->shm_perm.mode & (SHM_ALLOC|SHM_DEST)) == SHM_ALLOC) 367 return(1); 368 u.u_error = EINVAL; 369 return(0); 370 } 371 372 /* 373 * Free user resources associated with a shared memory segment 374 */ 375 shmufree(mp) 376 struct mapmem *mp; 377 { 378 register struct shmid_ds *shp; 379 380 shp = &shmsegs[mp->mm_id % SHMMMNI]; 381 mmmapout(mp); 382 mmfree(mp); 383 shp->shm_dtime = time.tv_sec; 384 if (--shp->shm_nattch <= 0 && (shp->shm_perm.mode & SHM_DEST)) 385 shmfree(shp); 386 } 387 388 /* 389 * Deallocate resources associated with a shared memory segment 390 */ 391 shmfree(shp) 392 register struct shmid_ds *shp; 393 { 394 caddr_t kva; 395 396 if (shp->shm_handle == NULL) 397 panic("shmfree"); 398 kva = (caddr_t) ptetokv(shp->shm_handle); 399 free(kva, M_SHM); 400 shp->shm_handle = NULL; 401 shmtot -= clrnd(btoc(shp->shm_segsz)); 402 shp->shm_perm.mode = 0; 403 /* 404 * Increment the sequence number to ensure that outstanding 405 * shmids for this segment will be invalid in the event that 406 * the segment is reallocated. Note that shmids must be 407 * positive as decreed by SVID. 408 */ 409 shp->shm_perm.seq++; 410 if ((int)(shp->shm_perm.seq * SHMMMNI) < 0) 411 shp->shm_perm.seq = 0; 412 } 413 414 /* 415 * XXX This routine would be common to all sysV style IPC 416 * (if the others were implemented). 417 */ 418 ipcaccess(ipc, mode) 419 register struct ipc_perm *ipc; 420 { 421 register int m; 422 423 if (u.u_uid == 0) 424 return(0); 425 /* 426 * Access check is based on only one of owner, group, public. 427 * If not owner, then check group. 428 * If not a member of the group, then check public access. 429 */ 430 mode &= 0700; 431 m = ipc->mode; 432 if (u.u_uid != ipc->uid && u.u_uid != ipc->cuid) { 433 m <<= 3; 434 if (!groupmember(ipc->gid, u.u_cred) && 435 !groupmember(ipc->cgid, u.u_cred)) 436 m <<= 3; 437 } 438 if ((mode&m) == mode) 439 return (1); 440 u.u_error = EACCES; 441 return (0); 442 } 443 444 #endif /* SYSVSHM */ 445