1 #include "inc.h" 2 3 /* Private shm_perm.mode flags, synchronized with NetBSD kernel values */ 4 #define SHM_ALLOC 0x0800 /* slot is in use (SHMSEG_ALLOCATED) */ 5 6 struct shm_struct { 7 struct shmid_ds shmid_ds; 8 vir_bytes page; 9 phys_bytes vm_id; 10 }; 11 static struct shm_struct shm_list[SHMMNI]; 12 static unsigned int shm_list_nr = 0; /* highest in-use slot number plus one */ 13 14 static struct shm_struct * 15 shm_find_key(key_t key) 16 { 17 unsigned int i; 18 19 if (key == IPC_PRIVATE) 20 return NULL; 21 22 for (i = 0; i < shm_list_nr; i++) { 23 if (!(shm_list[i].shmid_ds.shm_perm.mode & SHM_ALLOC)) 24 continue; 25 if (shm_list[i].shmid_ds.shm_perm._key == key) 26 return &shm_list[i]; 27 } 28 29 return NULL; 30 } 31 32 static struct shm_struct * 33 shm_find_id(int id) 34 { 35 struct shm_struct *shm; 36 unsigned int i; 37 38 i = IPCID_TO_IX(id); 39 if (i >= shm_list_nr) 40 return NULL; 41 42 shm = &shm_list[i]; 43 if (!(shm->shmid_ds.shm_perm.mode & SHM_ALLOC)) 44 return NULL; 45 if (shm->shmid_ds.shm_perm._seq != IPCID_TO_SEQ(id)) 46 return NULL; 47 return shm; 48 } 49 50 int 51 do_shmget(message * m) 52 { 53 struct shm_struct *shm; 54 unsigned int i, seq; 55 key_t key; 56 size_t size, old_size; 57 int flag; 58 void *page; 59 60 key = m->m_lc_ipc_shmget.key; 61 old_size = size = m->m_lc_ipc_shmget.size; 62 flag = m->m_lc_ipc_shmget.flag; 63 64 if ((shm = shm_find_key(key)) != NULL) { 65 if (!check_perm(&shm->shmid_ds.shm_perm, m->m_source, flag)) 66 return EACCES; 67 if ((flag & IPC_CREAT) && (flag & IPC_EXCL)) 68 return EEXIST; 69 if (size && shm->shmid_ds.shm_segsz < size) 70 return EINVAL; 71 i = shm - shm_list; 72 } else { /* no key found */ 73 if (!(flag & IPC_CREAT)) 74 return ENOENT; 75 if (size <= 0) 76 return EINVAL; 77 size = roundup(size, PAGE_SIZE); 78 if (size <= 0) 79 return EINVAL; 80 81 /* Find a free entry. */ 82 for (i = 0; i < __arraycount(shm_list); i++) 83 if (!(shm_list[i].shmid_ds.shm_perm.mode & SHM_ALLOC)) 84 break; 85 if (i == __arraycount(shm_list)) 86 return ENOSPC; 87 88 /* 89 * Allocate memory to share. For now, we store the page 90 * reference as a numerical value so as to avoid issues with 91 * live update. TODO: a proper solution. 92 */ 93 page = mmap(0, size, PROT_READ | PROT_WRITE, MAP_ANON, -1, 0); 94 if (page == MAP_FAILED) 95 return ENOMEM; 96 memset(page, 0, size); 97 98 /* Initialize the entry. */ 99 shm = &shm_list[i]; 100 seq = shm->shmid_ds.shm_perm._seq; 101 memset(shm, 0, sizeof(*shm)); 102 103 shm->shmid_ds.shm_perm._key = key; 104 shm->shmid_ds.shm_perm.cuid = 105 shm->shmid_ds.shm_perm.uid = getnuid(m->m_source); 106 shm->shmid_ds.shm_perm.cgid = 107 shm->shmid_ds.shm_perm.gid = getngid(m->m_source); 108 shm->shmid_ds.shm_perm.mode = SHM_ALLOC | (flag & ACCESSPERMS); 109 shm->shmid_ds.shm_perm._seq = (seq + 1) & 0x7fff; 110 shm->shmid_ds.shm_segsz = old_size; 111 shm->shmid_ds.shm_atime = 0; 112 shm->shmid_ds.shm_dtime = 0; 113 shm->shmid_ds.shm_ctime = clock_time(NULL); 114 shm->shmid_ds.shm_cpid = getnpid(m->m_source); 115 shm->shmid_ds.shm_lpid = 0; 116 shm->shmid_ds.shm_nattch = 0; 117 shm->page = (vir_bytes)page; 118 shm->vm_id = vm_getphys(sef_self(), page); 119 120 assert(i <= shm_list_nr); 121 if (i == shm_list_nr) 122 shm_list_nr++; 123 } 124 125 m->m_lc_ipc_shmget.retid = IXSEQ_TO_IPCID(i, shm->shmid_ds.shm_perm); 126 return OK; 127 } 128 129 int 130 do_shmat(message * m) 131 { 132 int id, flag, mask; 133 vir_bytes addr; 134 void *ret; 135 struct shm_struct *shm; 136 137 id = m->m_lc_ipc_shmat.id; 138 addr = (vir_bytes)m->m_lc_ipc_shmat.addr; 139 flag = m->m_lc_ipc_shmat.flag; 140 141 if (addr % PAGE_SIZE) { 142 if (flag & SHM_RND) 143 addr -= addr % PAGE_SIZE; 144 else 145 return EINVAL; 146 } 147 148 if ((shm = shm_find_id(id)) == NULL) 149 return EINVAL; 150 151 mask = 0; 152 if (flag & SHM_RDONLY) 153 mask = IPC_R; 154 else 155 mask = IPC_R | IPC_W; 156 if (!check_perm(&shm->shmid_ds.shm_perm, m->m_source, mask)) 157 return EACCES; 158 159 ret = vm_remap(m->m_source, sef_self(), (void *)addr, 160 (void *)shm->page, shm->shmid_ds.shm_segsz); 161 if (ret == MAP_FAILED) 162 return ENOMEM; 163 164 shm->shmid_ds.shm_atime = clock_time(NULL); 165 shm->shmid_ds.shm_lpid = getnpid(m->m_source); 166 /* nattch is updated lazily */ 167 168 m->m_lc_ipc_shmat.retaddr = ret; 169 return OK; 170 } 171 172 void 173 update_refcount_and_destroy(void) 174 { 175 u8_t rc; 176 unsigned int i; 177 178 for (i = 0; i < shm_list_nr; i++) { 179 if (!(shm_list[i].shmid_ds.shm_perm.mode & SHM_ALLOC)) 180 continue; 181 182 rc = vm_getrefcount(sef_self(), (void *)shm_list[i].page); 183 if (rc == (u8_t)-1) { 184 printf("IPC: can't find physical region.\n"); 185 continue; 186 } 187 shm_list[i].shmid_ds.shm_nattch = rc - 1; 188 189 if (shm_list[i].shmid_ds.shm_nattch == 0 && 190 (shm_list[i].shmid_ds.shm_perm.mode & SHM_DEST)) { 191 munmap((void *)shm_list[i].page, 192 roundup(shm_list[i].shmid_ds.shm_segsz, 193 PAGE_SIZE)); 194 /* Mark the entry as free. */ 195 shm_list[i].shmid_ds.shm_perm.mode &= ~SHM_ALLOC; 196 } 197 } 198 199 /* 200 * Now that we may have removed an arbitrary set of slots, ensure that 201 * shm_list_nr again equals the highest in-use slot number plus one. 202 */ 203 while (shm_list_nr > 0 && 204 !(shm_list[shm_list_nr - 1].shmid_ds.shm_perm.mode & SHM_ALLOC)) 205 shm_list_nr--; 206 } 207 208 int 209 do_shmdt(message * m) 210 { 211 struct shm_struct *shm; 212 vir_bytes addr; 213 phys_bytes vm_id; 214 unsigned int i; 215 216 addr = (vir_bytes)m->m_lc_ipc_shmdt.addr; 217 218 if ((vm_id = vm_getphys(m->m_source, (void *)addr)) == 0) 219 return EINVAL; 220 221 for (i = 0; i < shm_list_nr; i++) { 222 shm = &shm_list[i]; 223 224 if (!(shm->shmid_ds.shm_perm.mode & SHM_ALLOC)) 225 continue; 226 227 if (shm->vm_id == vm_id) { 228 shm->shmid_ds.shm_atime = clock_time(NULL); 229 shm->shmid_ds.shm_lpid = getnpid(m->m_source); 230 /* nattch is updated lazily */ 231 232 vm_unmap(m->m_source, (void *)addr); 233 break; 234 } 235 } 236 if (i == shm_list_nr) 237 printf("IPC: do_shmdt: ID %lu not found\n", vm_id); 238 239 update_refcount_and_destroy(); 240 241 return OK; 242 } 243 244 /* 245 * Fill a shminfo structure with actual information. 246 */ 247 static void 248 fill_shminfo(struct shminfo * sinfo) 249 { 250 251 memset(sinfo, 0, sizeof(*sinfo)); 252 253 sinfo->shmmax = (unsigned long)-1; 254 sinfo->shmmin = 1; 255 sinfo->shmmni = __arraycount(shm_list); 256 sinfo->shmseg = (unsigned long)-1; 257 sinfo->shmall = (unsigned long)-1; 258 } 259 260 int 261 do_shmctl(message * m) 262 { 263 struct shmid_ds tmp_ds; 264 struct shm_struct *shm; 265 struct shminfo sinfo; 266 struct shm_info s_info; 267 vir_bytes buf; 268 unsigned int i; 269 uid_t uid; 270 int r, id, cmd; 271 272 id = m->m_lc_ipc_shmctl.id; 273 cmd = m->m_lc_ipc_shmctl.cmd; 274 buf = (vir_bytes)m->m_lc_ipc_shmctl.buf; 275 276 /* 277 * For stat calls, sure that all information is up-to-date. Since this 278 * may free the slot, do this before mapping from ID to slot below. 279 */ 280 if (cmd == IPC_STAT || cmd == SHM_STAT) 281 update_refcount_and_destroy(); 282 283 switch (cmd) { 284 case IPC_INFO: 285 case SHM_INFO: 286 shm = NULL; 287 break; 288 case SHM_STAT: 289 if (id < 0 || (unsigned int)id >= shm_list_nr) 290 return EINVAL; 291 shm = &shm_list[id]; 292 if (!(shm->shmid_ds.shm_perm.mode & SHM_ALLOC)) 293 return EINVAL; 294 break; 295 default: 296 if ((shm = shm_find_id(id)) == NULL) 297 return EINVAL; 298 break; 299 } 300 301 switch (cmd) { 302 case IPC_STAT: 303 case SHM_STAT: 304 /* Check whether the caller has read permission. */ 305 if (!check_perm(&shm->shmid_ds.shm_perm, m->m_source, IPC_R)) 306 return EACCES; 307 if ((r = sys_datacopy(SELF, (vir_bytes)&shm->shmid_ds, 308 m->m_source, buf, sizeof(shm->shmid_ds))) != OK) 309 return r; 310 if (cmd == SHM_STAT) 311 m->m_lc_ipc_shmctl.ret = 312 IXSEQ_TO_IPCID(id, shm->shmid_ds.shm_perm); 313 break; 314 case IPC_SET: 315 uid = getnuid(m->m_source); 316 if (uid != shm->shmid_ds.shm_perm.cuid && 317 uid != shm->shmid_ds.shm_perm.uid && uid != 0) 318 return EPERM; 319 if ((r = sys_datacopy(m->m_source, buf, SELF, 320 (vir_bytes)&tmp_ds, sizeof(tmp_ds))) != OK) 321 return r; 322 shm->shmid_ds.shm_perm.uid = tmp_ds.shm_perm.uid; 323 shm->shmid_ds.shm_perm.gid = tmp_ds.shm_perm.gid; 324 shm->shmid_ds.shm_perm.mode &= ~ACCESSPERMS; 325 shm->shmid_ds.shm_perm.mode |= 326 tmp_ds.shm_perm.mode & ACCESSPERMS; 327 shm->shmid_ds.shm_ctime = clock_time(NULL); 328 break; 329 case IPC_RMID: 330 uid = getnuid(m->m_source); 331 if (uid != shm->shmid_ds.shm_perm.cuid && 332 uid != shm->shmid_ds.shm_perm.uid && uid != 0) 333 return EPERM; 334 shm->shmid_ds.shm_perm.mode |= SHM_DEST; 335 /* Destroy if possible. */ 336 update_refcount_and_destroy(); 337 break; 338 case IPC_INFO: 339 fill_shminfo(&sinfo); 340 if ((r = sys_datacopy(SELF, (vir_bytes)&sinfo, m->m_source, 341 buf, sizeof(sinfo))) != OK) 342 return r; 343 if (shm_list_nr > 0) 344 m->m_lc_ipc_shmctl.ret = shm_list_nr - 1; 345 else 346 m->m_lc_ipc_shmctl.ret = 0; 347 break; 348 case SHM_INFO: 349 memset(&s_info, 0, sizeof(s_info)); 350 s_info.used_ids = shm_list_nr; 351 s_info.shm_tot = 0; 352 for (i = 0; i < shm_list_nr; i++) 353 s_info.shm_tot += 354 shm_list[i].shmid_ds.shm_segsz / PAGE_SIZE; 355 s_info.shm_rss = s_info.shm_tot; 356 s_info.shm_swp = 0; 357 s_info.swap_attempts = 0; 358 s_info.swap_successes = 0; 359 if ((r = sys_datacopy(SELF, (vir_bytes)&s_info, m->m_source, 360 buf, sizeof(s_info))) != OK) 361 return r; 362 if (shm_list_nr > 0) 363 m->m_lc_ipc_shmctl.ret = shm_list_nr - 1; 364 else 365 m->m_lc_ipc_shmctl.ret = 0; 366 break; 367 default: 368 return EINVAL; 369 } 370 return OK; 371 } 372 373 /* 374 * Return shared memory information for a remote MIB call on the sysvipc_info 375 * node in the kern.ipc subtree. The particular semantics of this call are 376 * tightly coupled to the implementation of the ipcs(1) userland utility. 377 */ 378 ssize_t 379 get_shm_mib_info(struct rmib_oldp * oldp) 380 { 381 struct shm_sysctl_info shmsi; 382 struct shmid_ds *shmds; 383 unsigned int i; 384 ssize_t r, off; 385 386 off = 0; 387 388 fill_shminfo(&shmsi.shminfo); 389 390 /* 391 * As a hackish exception, the requested size may imply that just 392 * general information is to be returned, without throwing an ENOMEM 393 * error because there is no space for full output. 394 */ 395 if (rmib_getoldlen(oldp) == sizeof(shmsi.shminfo)) 396 return rmib_copyout(oldp, 0, &shmsi.shminfo, 397 sizeof(shmsi.shminfo)); 398 399 /* 400 * ipcs(1) blindly expects the returned array to be of size 401 * shminfo.shmmni, using the SHMSEG_ALLOCATED (aka SHM_ALLOC) mode flag 402 * to see whether each entry is valid. If we return a smaller size, 403 * ipcs(1) will access arbitrary memory. 404 */ 405 assert(shmsi.shminfo.shmmni > 0); 406 407 if (oldp == NULL) 408 return sizeof(shmsi) + sizeof(shmsi.shmids[0]) * 409 (shmsi.shminfo.shmmni - 1); 410 411 /* 412 * Copy out entries one by one. For the first entry, copy out the 413 * entire "shmsi" structure. For subsequent entries, reuse the single 414 * embedded 'shmids' element of "shmsi" and copy out only that element. 415 */ 416 for (i = 0; i < shmsi.shminfo.shmmni; i++) { 417 shmds = &shm_list[i].shmid_ds; 418 419 memset(&shmsi.shmids[0], 0, sizeof(shmsi.shmids[0])); 420 if (i < shm_list_nr && (shmds->shm_perm.mode & SHM_ALLOC)) { 421 prepare_mib_perm(&shmsi.shmids[0].shm_perm, 422 &shmds->shm_perm); 423 shmsi.shmids[0].shm_segsz = shmds->shm_segsz; 424 shmsi.shmids[0].shm_lpid = shmds->shm_lpid; 425 shmsi.shmids[0].shm_cpid = shmds->shm_cpid; 426 shmsi.shmids[0].shm_atime = shmds->shm_atime; 427 shmsi.shmids[0].shm_dtime = shmds->shm_dtime; 428 shmsi.shmids[0].shm_ctime = shmds->shm_ctime; 429 shmsi.shmids[0].shm_nattch = shmds->shm_nattch; 430 } 431 432 if (off == 0) 433 r = rmib_copyout(oldp, off, &shmsi, sizeof(shmsi)); 434 else 435 r = rmib_copyout(oldp, off, &shmsi.shmids[0], 436 sizeof(shmsi.shmids[0])); 437 438 if (r < 0) 439 return r; 440 off += r; 441 } 442 443 return off; 444 } 445 446 #if 0 447 static void 448 list_shm_ds(void) 449 { 450 unsigned int i; 451 452 printf("key\tid\tpage\n"); 453 for (i = 0; i < shm_list_nr; i++) { 454 if (!(shm_list[i].shmid_ds.shm_perm.mode & SHM_ALLOC)) 455 continue; 456 printf("%ld\t%d\t%lx\n", 457 shm_list[i].shmid_ds.shm_perm._key, 458 IXSEQ_TO_IPCID(i, shm_list[i].shmid_ds.shm_perm), 459 shm_list[i].page); 460 } 461 } 462 #endif 463 464 int 465 is_shm_nil(void) 466 { 467 468 return (shm_list_nr == 0); 469 } 470