1 /*
2 * Copyright 1991-1998 by Open Software Foundation, Inc.
3 * All Rights Reserved
4 *
5 * Permission to use, copy, modify, and distribute this software and
6 * its documentation for any purpose and without fee is hereby granted,
7 * provided that the above copyright notice appears in all copies and
8 * that both the copyright notice and this permission notice appear in
9 * supporting documentation.
10 *
11 * OSF DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
12 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
13 * FOR A PARTICULAR PURPOSE.
14 *
15 * IN NO EVENT SHALL OSF BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
16 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
17 * LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
18 * NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
19 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 */
21 /* CMU_HIST */
22 /*
23 * Revision 2.7 91/10/09 16:08:15 af
24 * Revision 2.6.2.1 91/09/16 10:15:30 rpd
25 * Added <ipc/ipc_hash.h>.
26 * [91/09/02 rpd]
27 *
28 * Revision 2.6.2.1 91/09/16 10:15:30 rpd
29 * Added <ipc/ipc_hash.h>.
30 * [91/09/02 rpd]
31 *
32 * Revision 2.6 91/05/14 16:31:38 mrt
33 * Correcting copyright
34 *
35 * Revision 2.5 91/03/16 14:47:45 rpd
36 * Fixed ipc_entry_grow_table to use it_entries_realloc.
37 * [91/03/05 rpd]
38 *
39 * Revision 2.4 91/02/05 17:21:17 mrt
40 * Changed to new Mach copyright
41 * [91/02/01 15:44:19 mrt]
42 *
43 * Revision 2.3 91/01/08 15:12:58 rpd
44 * Removed MACH_IPC_GENNOS.
45 * [90/11/08 rpd]
46 *
47 * Revision 2.2 90/06/02 14:49:36 rpd
48 * Created for new IPC.
49 * [90/03/26 20:54:27 rpd]
50 *
51 */
52 /* CMU_ENDHIST */
53 /*
54 * Mach Operating System
55 * Copyright (c) 1991,1990,1989 Carnegie Mellon University
56 * All Rights Reserved.
57 *
58 * Permission to use, copy, modify and distribute this software and its
59 * documentation is hereby granted, provided that both the copyright
60 * notice and this permission notice appear in all copies of the
61 * software, derivative works or modified versions, and any portions
62 * thereof, and that both notices appear in supporting documentation.
63 *
64 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
65 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
66 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
67 *
68 * Carnegie Mellon requests users of this software to return to
69 *
70 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
71 * School of Computer Science
72 * Carnegie Mellon University
73 * Pittsburgh PA 15213-3890
74 *
75 * any improvements or extensions that they make and grant Carnegie Mellon
76 * the rights to redistribute these changes.
77 */
78 /*
79 */
80 /*
81 * File: ipc/ipc_entry.c
82 * Author: Rich Draves
83 * Date: 1989
84 *
85 * Primitive functions to manipulate translation entries.
86 */
87
88
89 #include <sys/cdefs.h>
90 __FBSDID("$FreeBSD$");
91
92 #include "opt_capsicum.h"
93
94 #include <sys/types.h>
95 #include <sys/param.h>
96 #include <sys/eventhandler.h>
97 #include <sys/capsicum.h>
98 #include <sys/file.h>
99 #include <sys/filedesc.h>
100 #include <sys/fcntl.h>
101 #include <sys/kernel.h>
102 #include <sys/syscallsubr.h>
103 #include <sys/stat.h>
104 #include <sys/syslog.h>
105 #include <sys/user.h>
106
107 #include <sys/mach/mach_types.h>
108 #include <sys/mach/kern_return.h>
109 #include <sys/mach/port.h>
110 #include <sys/mach/message.h>
111 #include <sys/mach/mach_port_server.h>
112 #include <vm/uma.h>
113 #include <sys/mach/ipc/port.h>
114 #include <sys/mach/ipc/ipc_entry.h>
115 #include <sys/mach/ipc/ipc_space.h>
116 #include <sys/mach/ipc/ipc_object.h>
117 #include <sys/mach/ipc/ipc_hash.h>
118 #include <sys/mach/ipc/ipc_table.h>
119 #include <sys/mach/ipc/ipc_port.h>
120 #include <sys/mach/ipc/ipc_pset.h>
121 #include <sys/mach/thread.h>
122
123 #include <security/audit/audit.h>
124
125 static void fdunused(struct filedesc *fdp, int fd);
126 static int kern_fdalloc(struct thread *td, int minfd, int *result);
127 static void kern_fddealloc(struct thread *td, int fd);
128 static inline void kern_fdfree(struct filedesc *fdp, int fd);
129 static int kern_finstall(struct thread *td, struct file *fp, int *fd, int flags,
130 struct filecaps *fcaps);
131
132 #define MODERN (__FreeBSD_version >= 1100000)
133 #define FNOFDALLOC 0x80000000
134
135 static void
ipc_entry_hash_delete(ipc_space_t space,ipc_entry_t entry)136 ipc_entry_hash_delete(
137 ipc_space_t space,
138 ipc_entry_t entry)
139 {
140 mach_port_index_t idx;
141 ipc_entry_t entryp;
142
143 if ((idx = entry->ie_index) == UINT_MAX)
144 return;
145 if ((entry->ie_bits & (MACH_PORT_TYPE_DEAD_NAME | MACH_PORT_TYPE_SEND)) !=
146 MACH_PORT_TYPE_SEND)
147 return;
148 if (idx >= space->is_table_size)
149 return;
150
151 entryp = space->is_table[idx];
152 assert(entryp);
153
154 if (entryp == entry) {
155 space->is_table[idx] = entry->ie_link;
156 entry->ie_link = NULL;
157 entry->ie_index = UINT_MAX;
158 } else {
159 while (entryp->ie_link != NULL) {
160 if (entryp->ie_link == entry) {
161 entryp->ie_link = entry->ie_link;
162 entry->ie_link = NULL;
163 entry->ie_index = UINT_MAX;
164 break;
165 }
166 entryp = entryp->ie_link;
167 }
168 }
169 /* assert that it was found */
170 MPASS(entry->ie_index == UINT_MAX);
171 }
172
173 static fo_close_t mach_port_close;
174 static fo_stat_t mach_port_stat;
175 #if MODERN
176 static fo_fill_kinfo_t mach_port_fill_kinfo;
177 #endif
178
179 struct fileops mach_fileops = {
180 .fo_close = mach_port_close,
181 .fo_stat = mach_port_stat,
182 #if MODERN
183 .fo_fill_kinfo = mach_port_fill_kinfo,
184 #endif
185 .fo_flags = 0,
186 };
187
188 static int
mach_port_close(struct file * fp,struct thread * td)189 mach_port_close(struct file *fp, struct thread *td)
190 {
191 ipc_entry_t entry;
192 ipc_object_t object;
193 ipc_pset_t pset;
194 ipc_port_t port;
195
196 MACH_VERIFY(fp->f_data != NULL, ("expected fp->f_data != NULL - got NULL\n"));
197 if ((entry = fp->f_data) == NULL)
198 return (0);
199 if ((entry->ie_bits & MACH_PORT_TYPE_PORT_SET) == 0)
200 ipc_entry_hash_delete(entry->ie_space, entry);
201 MPASS(entry->ie_link == NULL);
202 PROC_LOCK(td->td_proc);
203 LIST_REMOVE(entry, ie_space_link);
204 PROC_UNLOCK(td->td_proc);
205 if ((object = entry->ie_object) != NULL) {
206 if (entry->ie_bits & MACH_PORT_TYPE_PORT_SET) {
207 pset = (ipc_pset_t)object;
208 ips_lock(pset);
209 ipc_pset_destroy(pset);
210 } else {
211 port = (ipc_port_t)object;
212 if (port->ip_receiver == current_space()) {
213 ip_lock(port);
214 ipc_port_clear_receiver(port);
215 ipc_port_destroy(port);
216 } else {
217 ip_release(port);
218 }
219 }
220 entry->ie_object = NULL;
221 }
222 free(entry, M_MACH_IPC_ENTRY);
223 fp->f_data = NULL;
224
225 return (0);
226 }
227
228 static int
mach_port_stat(struct file * fp __unused,struct stat * sb,struct ucred * active_cred __unused,struct thread * td __unused)229 mach_port_stat(struct file *fp __unused, struct stat *sb,
230 struct ucred *active_cred __unused, struct thread *td __unused)
231 {
232 ipc_entry_t entry;
233
234 bzero((caddr_t)sb, sizeof(*sb));
235
236 entry = fp->f_data;
237 if (entry->ie_bits & MACH_PORT_TYPE_PORT_SET) {
238 sb->st_mode = S_IFPSET;
239 } else {
240 sb->st_mode = S_IFPORT;
241 }
242 return (0);
243 }
244
245 #if MODERN
246 static int
mach_port_fill_kinfo(struct file * fp,struct kinfo_file * kif,struct filedesc * fdp __unused)247 mach_port_fill_kinfo(struct file *fp, struct kinfo_file *kif,
248 struct filedesc *fdp __unused)
249 {
250 ipc_entry_t entry;
251
252 /* assume it's a port first */
253 kif->kf_type = KF_TYPE_PORT;
254
255 if ((entry = fp->f_data) == NULL)
256 return (0);
257 /* What else do we want from it? */
258 if (entry->ie_bits & MACH_PORT_TYPE_PORT_SET) {
259 kif->kf_type = KF_TYPE_PORTSET;
260 }
261
262 return (0);
263 }
264 #endif
265
266 /*
267 * Routine: ipc_entry_release
268 * Purpose:
269 * Drops a reference to an entry.
270 * Conditions:
271 * The space must be locked.
272 */
273
274 void
ipc_entry_release(ipc_entry_t entry)275 ipc_entry_release(ipc_entry_t entry)
276 {
277
278 fdrop(entry->ie_fp, curthread);
279 }
280
281 /*
282 * Routine: ipc_entry_lookup
283 * Purpose:
284 * Searches for an entry, given its name.
285 * Conditions:
286 * The space must be active.
287 */
288
289 extern void kdb_backtrace(void);
290 ipc_entry_t
ipc_entry_lookup(ipc_space_t space,mach_port_name_t name)291 ipc_entry_lookup(ipc_space_t space, mach_port_name_t name)
292 {
293 struct file *fp;
294 ipc_entry_t entry;
295
296 assert(space->is_active);
297
298 if (curthread->td_proc->p_fd == NULL)
299 return (NULL);
300
301 if (fget(curthread, name, NULL, &fp) != 0) {
302 log(LOG_DEBUG, "%s:%d entry for port name: %d not found\n", curproc->p_comm, curproc->p_pid, name);
303 return (NULL);
304 }
305 if (fp->f_type != DTYPE_MACH_IPC) {
306 kdb_backtrace();
307 log(LOG_DEBUG, "%s:%d port name: %d is not MACH\n", curproc->p_comm, curproc->p_pid, name);
308 fdrop(fp, curthread);
309 return (NULL);
310 }
311 entry = fp->f_data;
312 fdrop(fp, curthread);
313 return (entry);
314 }
315
316 kern_return_t
ipc_entry_file_to_port(ipc_space_t space,mach_port_name_t name,ipc_object_t * objectp)317 ipc_entry_file_to_port(ipc_space_t space, mach_port_name_t name, ipc_object_t *objectp)
318 {
319 struct file *fp;
320 ipc_port_t port;
321
322 assert(space->is_active);
323
324 if (curthread->td_proc->p_fd == NULL)
325 return (KERN_INVALID_ARGUMENT);
326
327 if (fget(curthread, name, NULL, &fp) != 0) {
328 log(LOG_DEBUG, "%s:%d entry for port name: %d not found\n", curproc->p_comm, curproc->p_pid, name);
329 return (KERN_INVALID_ARGUMENT);
330 }
331 if (fp->f_type == DTYPE_MACH_IPC) {
332 fdrop(fp, curthread);
333 return (KERN_INVALID_ARGUMENT);
334 }
335 if ((port = ipc_port_alloc_special(space)) == NULL)
336 return (KERN_RESOURCE_SHORTAGE);
337
338 port->ip_context = (mach_vm_address_t) fp;
339 port->ip_flags = IP_CONTEXT_FILE;
340 port->ip_receiver = space;
341 port->ip_receiver_name = name;
342
343 *objectp = (ipc_object_t)port;
344 return (KERN_SUCCESS);
345 }
346
347 kern_return_t
ipc_entry_port_to_file(ipc_space_t space,mach_port_name_t * namep,ipc_object_t object)348 ipc_entry_port_to_file(ipc_space_t space, mach_port_name_t *namep, ipc_object_t object)
349 {
350 ipc_port_t port;
351 struct file *fp;
352
353 port = (ipc_port_t)object;
354 MPASS(object != NULL);
355 MPASS(port->ip_flags & IP_CONTEXT_FILE);
356 fp = (void *)port->ip_context;
357 ipc_port_dealloc_special(port, space);
358
359 /* Are sent file O_CLOEXEC? */
360 if (kern_finstall(curthread, fp, namep, 0, NULL) != 0) {
361 fdrop(fp, curthread);
362 printf("finstall failed\n");
363 return (KERN_RESOURCE_SHORTAGE);
364 }
365 printf(" installing received file *fp=%p at %d\n", fp, *namep);
366 return (KERN_SUCCESS);
367 }
368
369 /*
370 * Routine: ipc_entry_get
371 * Purpose:
372 * Tries to allocate an entry out of the space.
373 * Conditions:
374 * The space is active throughout.
375 * An object may be locked. Will try to allocate memory.
376 * Returns:
377 * KERN_SUCCESS A free entry was found.
378 * KERN_NO_SPACE No entry allocated.
379 */
380
381 kern_return_t
ipc_entry_get(ipc_space_t space,boolean_t is_send_once,mach_port_name_t * namep,ipc_entry_t * entryp)382 ipc_entry_get(
383 ipc_space_t space,
384 boolean_t is_send_once,
385 mach_port_name_t *namep,
386 ipc_entry_t *entryp)
387 {
388 ipc_entry_t free_entry;
389 int fd;
390 struct file *fp;
391 struct thread *td;
392
393 assert(space->is_active);
394
395 td = curthread;
396 if ((free_entry = malloc(sizeof(*free_entry), M_MACH_IPC_ENTRY, M_WAITOK|M_ZERO)) == NULL)
397 return KERN_RESOURCE_SHORTAGE;
398
399 if (kern_fdalloc(td, 16, &fd)) {
400 log(LOG_WARNING, "%s:%d failed to allocate fd\n", __FILE__, __LINE__);
401 return (KERN_RESOURCE_SHORTAGE);
402 }
403 if (falloc_noinstall(td, &fp)) {
404 kern_fddealloc(td, fd);
405 log(LOG_WARNING, "%s:%d failed to allocate fp\n", __FILE__, __LINE__);
406 return (KERN_RESOURCE_SHORTAGE);
407 }
408 if (kern_finstall(td, fp, &fd, FNOFDALLOC, NULL)) {
409 log(LOG_WARNING, "%s:%d failed to allocate fp:%p at fd:%d \n", __FILE__, __LINE__, fp, fd);
410 kern_fddealloc(td, fd);
411 fdrop(fp, td);
412 return (KERN_RESOURCE_SHORTAGE);
413 }
414
415 free_entry->ie_bits = 0;
416 free_entry->ie_request = 0;
417 free_entry->ie_name = fd;
418 free_entry->ie_fp = fp;
419 free_entry->ie_index = UINT_MAX;
420 free_entry->ie_link = NULL;
421 free_entry->ie_space = space;
422 PROC_LOCK(curproc);
423 LIST_INSERT_HEAD(&space->is_entry_list, free_entry, ie_space_link);
424 PROC_UNLOCK(curproc);
425 finit(fp, 0, DTYPE_MACH_IPC, free_entry, &mach_fileops);
426 fdrop(fp, td);
427 assert(fp->f_count == 1);
428 *namep = fd;
429 *entryp = free_entry;
430
431 return KERN_SUCCESS;
432 }
433
434 /*
435 * Routine: ipc_entry_alloc
436 * Purpose:
437 * Allocate an entry out of the space.
438 * Conditions:
439 * The space is not locked before, but it is write-locked after
440 * if the call is successful. May allocate memory.
441 * Returns:
442 * KERN_SUCCESS An entry was allocated.
443 * KERN_INVALID_TASK The space is dead.
444 * KERN_NO_SPACE No room for an entry in the space.
445 * KERN_RESOURCE_SHORTAGE Couldn't allocate memory for an entry.
446 */
447
448 kern_return_t
ipc_entry_alloc(ipc_space_t space,boolean_t is_send_once,mach_port_name_t * namep,ipc_entry_t * entryp)449 ipc_entry_alloc(
450 ipc_space_t space,
451 boolean_t is_send_once,
452 mach_port_name_t *namep,
453 ipc_entry_t *entryp)
454 {
455 kern_return_t kr;
456
457 if (!space->is_active)
458 return (KERN_INVALID_TASK);
459
460 *namep = MACH_PORT_NAME_NULL;
461 if ((kr = ipc_entry_get(space, is_send_once, namep, entryp)) != KERN_SUCCESS)
462 return (kr);
463
464 is_write_lock(space);
465 return (0);
466 }
467
468 /*
469 * Routine: ipc_entry_alloc_name
470 * Purpose:
471 * Allocates/finds an entry with a specific name.
472 * If an existing entry is returned, its type will be nonzero.
473 * Conditions:
474 * The space is not locked before, but it is write-locked after
475 * if the call is successful. May allocate memory.
476 * Returns:
477 * KERN_SUCCESS Found existing entry with same name.
478 * KERN_SUCCESS Allocated a new entry.
479 * KERN_INVALID_TASK The space is dead.
480 * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
481 */
482
483 kern_return_t
ipc_entry_alloc_name(ipc_space_t space,mach_port_name_t name,ipc_entry_t * entryp)484 ipc_entry_alloc_name(
485 ipc_space_t space,
486 mach_port_name_t name,
487 ipc_entry_t *entryp)
488 {
489 mach_port_name_t newname;
490 struct file *fp;
491 kern_return_t kr;
492 struct thread *td = curthread;
493
494 if (!space->is_active) {
495 return (KERN_INVALID_TASK);
496 }
497 assert(MACH_PORT_NAME_VALID(name));
498 is_write_lock(space);
499 if ((*entryp = ipc_entry_lookup(space, name)) != NULL)
500 return (KERN_SUCCESS);
501
502 is_write_unlock(space);
503
504 /* name could technically be a ridiculously large value */
505 if (kern_fdalloc(td, name, &newname)) {
506 log(LOG_WARNING, "%s:%d failed to allocate %d\n", __FILE__, __LINE__, name);
507 return (KERN_RESOURCE_SHORTAGE);
508 }
509 if (newname != name) {
510 kern_fddealloc(td, newname);
511 return (KERN_NAME_EXISTS);
512 }
513 if (falloc_noinstall(td, &fp)) {
514 kern_fddealloc(td, newname);
515 return (KERN_RESOURCE_SHORTAGE);
516 }
517 if (kern_finstall(td, fp, &name, FNOFDALLOC, NULL)) {
518 kern_fddealloc(td, newname);
519 fdrop(fp, td);
520 return (KERN_RESOURCE_SHORTAGE);
521 }
522 kr = ipc_entry_get(space, 0, &name, entryp);
523 if (kr != KERN_SUCCESS) {
524 kern_fddealloc(td, newname);
525 return (KERN_INVALID_TASK);
526 }
527 is_write_lock(space);
528 return (kr);
529 }
530
531 void
ipc_entry_close(ipc_space_t space,mach_port_name_t fd)532 ipc_entry_close(
533 ipc_space_t space,
534 mach_port_name_t fd)
535 {
536 struct filedesc *fdp;
537 struct file *fp;
538 struct thread *td;
539
540 td = curthread;
541 fdp = td->td_proc->p_fd;
542
543 AUDIT_SYSCLOSE(td, fd);
544
545 FILEDESC_XLOCK(fdp);
546 if ((fp = fget_locked(fdp, fd)) == NULL) {
547 FILEDESC_XUNLOCK(fdp);
548 return;
549 }
550 /* we deliberately skip closing the knote so that it will
551 * have the last reference to the fp
552 */
553 kern_fdfree(fdp, fd);
554 FILEDESC_XUNLOCK(fdp);
555 fdrop(fp, td);
556 }
557
558 int
ipc_entry_refs(ipc_entry_t entry)559 ipc_entry_refs(
560 ipc_entry_t entry)
561 {
562
563 return (entry->ie_fp->f_count);
564 }
565
566 void
ipc_entry_add_refs(ipc_entry_t entry,int delta)567 ipc_entry_add_refs(
568 ipc_entry_t entry,
569 int delta)
570 {
571
572 atomic_add_acq_int(&entry->ie_fp->f_count, delta);
573 }
574
575 void
ipc_entry_hold(ipc_entry_t entry)576 ipc_entry_hold(ipc_entry_t entry)
577 {
578
579 fhold(entry->ie_fp);
580 }
581
582 /*
583 * Routine: ipc_entry_dealloc
584 * Purpose:
585 * Deallocates an entry from a space.
586 * Conditions:
587 * The space must be write-locked.
588 * The space is unlocked on return.
589 * The space must be active.
590 */
591
592 void
ipc_entry_dealloc(ipc_space_t space,mach_port_name_t name,ipc_entry_t entry)593 ipc_entry_dealloc(
594 ipc_space_t space,
595 mach_port_name_t name,
596 ipc_entry_t entry)
597 {
598 assert(space->is_active);
599 assert(entry->ie_object == IO_NULL);
600 assert(entry->ie_request == 0);
601
602 if (space != entry->ie_space) {
603 is_write_unlock(space);
604 is_write_lock(entry->ie_space);
605 }
606 ipc_entry_hash_delete(space, entry);
607 if (space != entry->ie_space) {
608 is_write_unlock(entry->ie_space);
609 } else {
610 is_write_unlock(space);
611 }
612 MPASS(entry->ie_link == NULL);
613
614 ipc_entry_close(space, name);
615 }
616
617 static void
kern_last_close(struct thread * td,struct file * fp,struct filedesc * fdp,int fd)618 kern_last_close(struct thread *td, struct file *fp, struct filedesc *fdp, int fd)
619 {
620
621 FILEDESC_XLOCK(fdp);
622 knote_fdclose(td, fd);
623 kern_fdfree(fdp, fd);
624 FILEDESC_XUNLOCK(fdp);
625 fdrop(fp, td);
626 }
627
628 static void
ipc_entry_list_close(void * arg __unused,struct proc * p)629 ipc_entry_list_close(void *arg __unused, struct proc *p)
630 {
631 struct filedesc *fdp;
632 struct filedescent *fde;
633 struct file *fp;
634 struct thread *td;
635 #if 0
636 ipc_port_t port;
637 ipc_pset_t pset;
638 ipc_entry_t entry_tmp;
639 #endif
640 ipc_entry_t entry;
641 ipc_space_t space;
642 int i;
643
644 fdp = p->p_fd;
645 td = curthread;
646 space = current_space();
647
648 /* do we want to just return if the refcount is > 1 or should we
649 * bar this from happening in the first place?
650 **/
651 KASSERT(fdp->fd_refcnt == 1, ("the fdtable should not be shared"));
652
653 for (i = 0; i <= fdp->fd_lastfile; i++) {
654 fde = &fdp->fd_ofiles[i];
655 fp = fde->fde_file;
656 if (fp == NULL || (fp->f_type != DTYPE_MACH_IPC))
657 continue;
658 MPASS(fp->f_count > 0);
659
660 if (fp->f_data == NULL) {
661 log(LOG_WARNING, "%s:%d fd: %d has NULL f_data\n", p->p_comm, p->p_pid, i);
662 kern_last_close(td, fp, fdp, i);
663 continue;
664 }
665 entry = fp->f_data;
666 MPASS(entry->ie_bits != 0xdeadc0de);
667 if ((entry->ie_bits & MACH_PORT_TYPE_PORT_SET) == 0)
668 continue;
669 kern_last_close(td, fp, fdp, i);
670 }
671
672 for (i = 0; i <= fdp->fd_lastfile; i++) {
673
674 fde = &fdp->fd_ofiles[i];
675 fp = fde->fde_file;
676 if (fp == NULL || (fp->f_type != DTYPE_MACH_IPC))
677 continue;
678 MPASS(fp->f_count > 0);
679
680 entry = fp->f_data;
681 #if 0
682 if (fp->f_count > 1) {
683 int ispset = (entry->ie_bits & MACH_PORT_TYPE_PORT_SET);
684 log(LOG_WARNING, "%s:%d fd: %d %s refcount: %d\n", p->p_comm, p->p_pid, i,
685 ispset ? "pset" : "port", fp->f_count);
686 }
687 #endif
688 kern_last_close(td, fp, fdp, i);
689 }
690
691 #ifdef INVARIANTS
692 for (i = 0; i <= fdp->fd_lastfile; i++) {
693 fde = &fdp->fd_ofiles[i];
694 fp = fde->fde_file;
695 if (fp != NULL)
696 MPASS(fp->f_type != DTYPE_MACH_IPC);
697 }
698 #endif
699 /* free unreferenced ipc_entrys */
700 i = 0;
701 while(!LIST_EMPTY(&space->is_entry_list)) {
702 entry = LIST_FIRST(&space->is_entry_list);
703 /* mach_port_close removes the entry */
704 fp = entry->ie_fp;
705 MPASS(fp->f_count > 0);
706 fp->f_count = 1;
707 fdrop(fp, td);
708 /* ensure no infinite loop */
709 MPASS(i++ < 10000);
710 }
711 }
712
713
714 static void
ipc_entry_sysinit(void * arg __unused)715 ipc_entry_sysinit(void *arg __unused)
716 {
717
718 EVENTHANDLER_REGISTER(process_exit, ipc_entry_list_close, NULL, EVENTHANDLER_PRI_ANY);
719 EVENTHANDLER_REGISTER(process_exec, ipc_entry_list_close, NULL, EVENTHANDLER_PRI_ANY);
720 }
721
722 SYSINIT(ipc_entry, SI_SUB_KLD, SI_ORDER_ANY, ipc_entry_sysinit, NULL);
723
724
725 #define NDFILE 20
726 #define NDSLOTSIZE sizeof(NDSLOTTYPE)
727 #define NDENTRIES (NDSLOTSIZE * __CHAR_BIT)
728 #define NDSLOT(x) ((x) / NDENTRIES)
729 #define NDBIT(x) ((NDSLOTTYPE)1 << ((x) % NDENTRIES))
730 #define NDSLOTS(x) (((x) + NDENTRIES - 1) / NDENTRIES)
731
732
733 /*
734 * Find the highest non-zero bit in the given bitmap, starting at 0 and
735 * not exceeding size - 1. Return -1 if not found.
736 */
737 static int
fd_last_used(struct filedesc * fdp,int size)738 fd_last_used(struct filedesc *fdp, int size)
739 {
740 NDSLOTTYPE *map = fdp->fd_map;
741 NDSLOTTYPE mask;
742 int off, minoff;
743
744 off = NDSLOT(size);
745 if (size % NDENTRIES) {
746 mask = ~(~(NDSLOTTYPE)0 << (size % NDENTRIES));
747 if ((mask &= map[off]) != 0)
748 return (off * NDENTRIES + flsl(mask) - 1);
749 --off;
750 }
751 for (minoff = NDSLOT(0); off >= minoff; --off)
752 if (map[off] != 0)
753 return (off * NDENTRIES + flsl(map[off]) - 1);
754 return (-1);
755 }
756
757 static int
fdisused(struct filedesc * fdp,int fd)758 fdisused(struct filedesc *fdp, int fd)
759 {
760
761 FILEDESC_LOCK_ASSERT(fdp);
762
763 KASSERT(fd >= 0 && fd < fdp->fd_nfiles,
764 ("file descriptor %d out of range (0, %d)", fd, fdp->fd_nfiles));
765
766 return ((fdp->fd_map[NDSLOT(fd)] & NDBIT(fd)) != 0);
767 }
768
769 /*
770 * Mark a file descriptor as unused.
771 */
772 static void
fdunused(struct filedesc * fdp,int fd)773 fdunused(struct filedesc *fdp, int fd)
774 {
775
776 FILEDESC_XLOCK_ASSERT(fdp);
777
778 KASSERT(fdisused(fdp, fd), ("fd=%d is already unused", fd));
779 KASSERT(fdp->fd_ofiles[fd].fde_file == NULL,
780 ("fd=%d is still in use", fd));
781
782 fdp->fd_map[NDSLOT(fd)] &= ~NDBIT(fd);
783 if (fd < fdp->fd_freefile)
784 fdp->fd_freefile = fd;
785 if (fd == fdp->fd_lastfile)
786 fdp->fd_lastfile = fd_last_used(fdp, fd);
787 }
788
789 static int
kern_fdalloc(struct thread * td,int minfd,int * result)790 kern_fdalloc(struct thread *td, int minfd, int *result)
791 {
792 struct proc *p = td->td_proc;
793 struct filedesc *fdp = p->p_fd;
794 int rc;
795 FILEDESC_XLOCK(fdp);
796 rc = fdalloc(td, minfd, result);
797 FILEDESC_XUNLOCK(fdp);
798 return (rc);
799 }
800
801 static void
kern_fddealloc(struct thread * td,int fd)802 kern_fddealloc(struct thread *td, int fd)
803 {
804 struct proc *p = td->td_proc;
805 struct filedesc *fdp = p->p_fd;
806 FILEDESC_XLOCK(fdp);
807 fdunused(fdp, fd);
808 FILEDESC_XUNLOCK(fdp);
809 }
810
811 static inline void
kern_fdfree(struct filedesc * fdp,int fd)812 kern_fdfree(struct filedesc *fdp, int fd)
813 {
814 struct filedescent *fde;
815
816 fde = &fdp->fd_ofiles[fd];
817 #ifdef CAPABILITIES
818 seq_write_begin(&fde->fde_seq);
819 #endif
820 bzero(fde, fde_change_size);
821 fdunused(fdp, fd);
822 #ifdef CAPABILITIES
823 seq_write_end(&fde->fde_seq);
824 #endif
825 }
826
827 static void
filecaps_fill(struct filecaps * fcaps)828 filecaps_fill(struct filecaps *fcaps)
829 {
830
831 CAP_ALL(&fcaps->fc_rights);
832 fcaps->fc_ioctls = NULL;
833 fcaps->fc_nioctls = -1;
834 fcaps->fc_fcntls = CAP_FCNTL_ALL;
835 }
836
837 /*
838 * Install a file in a file descriptor table.
839 */
840 static int
kern_finstall(struct thread * td,struct file * fp,int * fd,int flags,struct filecaps * fcaps)841 kern_finstall(struct thread *td, struct file *fp, int *fd, int flags,
842 struct filecaps *fcaps)
843 {
844 struct filedesc *fdp = td->td_proc->p_fd;
845 struct filedescent *fde;
846 int error, min;
847
848 KASSERT(fd != NULL, ("%s: fd == NULL", __func__));
849 KASSERT(fp != NULL, ("%s: fp == NULL", __func__));
850
851 min = 16;
852
853 FILEDESC_XLOCK(fdp);
854 if (!(flags & FNOFDALLOC)) {
855 if ((error = fdalloc(td, min, fd))) {
856 FILEDESC_XUNLOCK(fdp);
857 return (error);
858 }
859 }
860 fhold(fp);
861 fde = &fdp->fd_ofiles[*fd];
862 #ifdef CAPABILITIES
863 seq_write_begin(&fde->fde_seq);
864 #endif
865 fde->fde_file = fp;
866 if ((flags & O_CLOEXEC) != 0)
867 fde->fde_flags |= UF_EXCLOSE;
868 filecaps_fill(&fde->fde_caps);
869 #ifdef CAPABILITIES
870 seq_write_end(&fde->fde_seq);
871 #endif
872 FILEDESC_XUNLOCK(fdp);
873 return (0);
874 }
875
876 #if MACH_KDB
877 #include <ddb/db_output.h>
878 #define printf kdbprintf
879
880 ipc_entry_t db_ipc_object_by_name(
881 task_t task,
882 mach_port_name_t name);
883
884
885 ipc_entry_t
db_ipc_object_by_name(task_t task,mach_port_name_t name)886 db_ipc_object_by_name(
887 task_t task,
888 mach_port_name_t name)
889 {
890 ipc_space_t space = task->itk_space;
891 ipc_entry_t entry;
892
893
894 entry = ipc_entry_lookup(space, name);
895 if(entry != IE_NULL) {
896 iprintf("(task 0x%x, name 0x%x) ==> object 0x%x\n",
897 task, name, entry->ie_object);
898 return (ipc_entry_t) entry->ie_object;
899 }
900 return entry;
901 }
902 #endif /* MACH_KDB */
903