1 /*
2 * Copyright (c) 2008, 2010 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28 #include <mach/mach_types.h>
29 #include <mach/notify.h>
30 #include <ipc/ipc_port.h>
31 #include <ipc/ipc_space.h>
32 #include <kern/ipc_kobject.h>
33 #include <kern/ipc_misc.h>
34
35 #include <mach/mach_port.h>
36 #include <mach/vm_map.h>
37 #include <vm/vm_map.h>
38 #include <vm/vm_kern.h>
39
40 extern void fileport_releasefg(struct fileglob *);
41
42 /*
43 * fileport_alloc
44 *
45 * Description: Obtain a send right for the given fileglob, which must be
46 * referenced.
47 *
48 * Parameters: fg A fileglob.
49 *
50 * Returns: Port of type IKOT_FILEPORT with fileglob set as its kobject.
51 * Port is returned with a send right.
52 */
53 ipc_port_t
fileport_alloc(struct fileglob * fg)54 fileport_alloc(struct fileglob *fg)
55 {
56 return ipc_kobject_alloc_port(fg, IKOT_FILEPORT,
57 IPC_KOBJECT_ALLOC_MAKE_SEND);
58 }
59
60
61 /*
62 * fileport_get_fileglob
63 *
64 * Description: Obtain the fileglob associated with a given port.
65 *
66 * Parameters: port A Mach port of type IKOT_FILEPORT.
67 *
68 * Returns: NULL The given Mach port did not reference a
69 * fileglob.
70 * !NULL The fileglob that is associated with the
71 * Mach port.
72 *
73 * Notes: The caller must have a reference on the fileport.
74 */
75 struct fileglob *
fileport_port_to_fileglob(ipc_port_t port)76 fileport_port_to_fileglob(ipc_port_t port)
77 {
78 if (IP_VALID(port)) {
79 return ipc_kobject_get_stable(port, IKOT_FILEPORT);
80 }
81 return NULL;
82 }
83
84
85 /*
86 * fileport_no_senders
87 *
88 * Description: Handle a no-senders notification for a fileport. Unless
89 * the message is spoofed, destroys the port and releases
90 * its reference on the fileglob.
91 *
92 * Parameters: msg A Mach no-senders notification message.
93 */
94 static void
fileport_no_senders(ipc_port_t port,mach_port_mscount_t mscount)95 fileport_no_senders(ipc_port_t port, mach_port_mscount_t mscount)
96 {
97 struct fileglob *fg;
98
99 fg = ipc_kobject_dealloc_port(port, mscount, IKOT_FILEPORT);
100
101 fileport_releasefg(fg);
102 }
103
104 IPC_KOBJECT_DEFINE(IKOT_FILEPORT,
105 .iko_op_movable_send = true,
106 .iko_op_stable = true,
107 .iko_op_no_senders = fileport_no_senders);
108
109 /*
110 * fileport_invoke
111 *
112 * Description: Invoke a function with the fileglob underlying the fileport.
113 * Returns the error code related to the fileglob lookup.
114 *
115 * Parameters: task The target task
116 * action The function to invoke with the fileglob
117 * arg Anonymous pointer to caller state
118 * rval The value returned from calling 'action'
119 */
120 kern_return_t
fileport_invoke(task_t task,mach_port_name_t name,int (* action)(mach_port_name_t,struct fileglob *,void *),void * arg,int * rval)121 fileport_invoke(task_t task, mach_port_name_t name,
122 int (*action)(mach_port_name_t, struct fileglob *, void *),
123 void *arg, int *rval)
124 {
125 kern_return_t kr;
126 ipc_port_t fileport;
127 struct fileglob *fg;
128
129 kr = ipc_typed_port_copyin_send(task->itk_space, name,
130 IKOT_FILEPORT, &fileport);
131 if (kr != KERN_SUCCESS) {
132 return kr;
133 }
134
135 if ((fg = fileport_port_to_fileglob(fileport)) != NULL) {
136 *rval = (*action)(name, fg, arg);
137 } else {
138 kr = KERN_FAILURE;
139 }
140 ipc_typed_port_release_send(fileport, IKOT_FILEPORT);
141 return kr;
142 }
143
144 /*
145 * fileport_walk
146 *
147 * Description: Invoke the action function on every fileport in the task.
148 *
149 * Parameters: task The target task
150 * countp Returns how many ports were found
151 * action The function to invoke on each fileport
152 */
153 kern_return_t
154 fileport_walk(task_t task, size_t *countp,
155 bool (^cb)(size_t i, mach_port_name_t, struct fileglob *))
156 {
157 const uint32_t BATCH_SIZE = 4 << 10;
158 ipc_space_t space = task->itk_space;
159 ipc_entry_table_t table;
160 ipc_entry_num_t index;
161 ipc_entry_t entry;
162 size_t count = 0;
163
164 is_read_lock(space);
165 if (!is_active(space)) {
166 is_read_unlock(space);
167 return KERN_INVALID_TASK;
168 }
169
170 table = is_active_table(space);
171 entry = ipc_entry_table_base(table);
172
173 /* skip the first element which is not a real entry */
174 index = 1;
175 entry = ipc_entry_table_next_elem(table, entry);
176
177 for (;;) {
178 ipc_entry_bits_t bits = entry->ie_bits;
179 mach_port_name_t name;
180 struct fileglob *fg;
181
182 if (IE_BITS_TYPE(bits) & MACH_PORT_TYPE_SEND) {
183 ipc_port_t port = entry->ie_port;
184
185 name = MACH_PORT_MAKE(index, IE_BITS_GEN(bits));
186 fg = fileport_port_to_fileglob(port);
187
188 if (fg) {
189 if (cb && !cb(count, name, fg)) {
190 cb = NULL;
191 if (countp == NULL) {
192 break;
193 }
194 }
195 count++;
196 }
197 }
198
199 index++;
200 entry = ipc_entry_table_next_elem(table, entry);
201 if (!entry) {
202 break;
203 }
204 if (index % BATCH_SIZE == 0) {
205 /*
206 * Give the system some breathing room,
207 * validate that the space is still valid,
208 * and reload the pointer and length.
209 */
210 is_read_unlock(space);
211 is_read_lock(space);
212 if (!is_active(space)) {
213 is_read_unlock(space);
214 return KERN_INVALID_TASK;
215 }
216
217 table = is_active_table(space);
218 entry = ipc_entry_table_get_nocheck(table, index);
219 }
220 }
221
222 is_read_unlock(space);
223
224 if (countp) {
225 *countp = count;
226 }
227
228 return KERN_SUCCESS;
229 }
230