xref: /trueos/sys/compat/mach/ipc/ipc_entry.c (revision 16d1bdc48d4af6c24d88395d77ebe359f763877c)
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