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 /*
22 * MkLinux
23 */
24 /*
25 * Mach Operating System
26 * Copyright (c) 1991,1990,1989 Carnegie Mellon University
27 * All Rights Reserved.
28 *
29 * Permission to use, copy, modify and distribute this software and its
30 * documentation is hereby granted, provided that both the copyright
31 * notice and this permission notice appear in all copies of the
32 * software, derivative works or modified versions, and any portions
33 * thereof, and that both notices appear in supporting documentation.
34 *
35 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
36 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
37 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
38 *
39 * Carnegie Mellon requests users of this software to return to
40 *
41 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
42 * School of Computer Science
43 * Carnegie Mellon University
44 * Pittsburgh PA 15213-3890
45 *
46 * any improvements or extensions that they make and grant Carnegie Mellon
47 * the rights to redistribute these changes.
48 */
49
50 #include <sys/cdefs.h>
51 #include <sys/types.h>
52
53 #include <stdlib.h>
54 #include <sys/queue.h>
55
56 #include <mach/mach.h>
57 #include <mach/boolean.h>
58 #include <mach/kern_return.h>
59 #include <mach/message.h>
60 #include <mach/mig_errors.h>
61 #include <mach/mach_port.h>
62 #include <mach/mach_vm.h>
63
64 #define LIBMACH_OPTIONS (MACH_SEND_INTERRUPT|MACH_RCV_INTERRUPT)
65
66 mach_msg_return_t
mach_msg(msg,option,send_size,rcv_size,rcv_name,timeout,notify)67 mach_msg(msg, option, send_size, rcv_size, rcv_name, timeout, notify)
68 mach_msg_header_t *msg;
69 mach_msg_option_t option;
70 mach_msg_size_t send_size;
71 mach_msg_size_t rcv_size;
72 mach_port_t rcv_name;
73 mach_msg_timeout_t timeout;
74 mach_port_t notify;
75 {
76 mach_msg_return_t mr;
77
78 /*
79 * Consider the following cases:
80 * 1) Errors in pseudo-receive (eg, MACH_SEND_INTERRUPTED
81 * plus special bits).
82 * 2) Use of MACH_SEND_INTERRUPT/MACH_RCV_INTERRUPT options.
83 * 3) RPC calls with interruptions in one/both halves.
84 *
85 * We refrain from passing the option bits that we implement
86 * to the kernel. This prevents their presence from inhibiting
87 * the kernel's fast paths (when it checks the option value).
88 */
89
90 mr = mach_msg_overwrite_trap(msg, option &~ LIBMACH_OPTIONS,
91 send_size, rcv_size, rcv_name,
92 timeout, notify, MACH_MSG_NULL, 0);
93 if (mr == MACH_MSG_SUCCESS)
94 return MACH_MSG_SUCCESS;
95
96 if ((option & MACH_SEND_INTERRUPT) == 0)
97 while (mr == MACH_SEND_INTERRUPTED)
98 mr = mach_msg_overwrite_trap(msg,
99 option &~ LIBMACH_OPTIONS,
100 send_size, rcv_size, rcv_name,
101 timeout, notify, MACH_MSG_NULL, 0);
102
103 if ((option & MACH_RCV_INTERRUPT) == 0)
104 while (mr == MACH_RCV_INTERRUPTED)
105 mr = mach_msg_overwrite_trap(msg,
106 option &~ (LIBMACH_OPTIONS|MACH_SEND_MSG),
107 0, rcv_size, rcv_name,
108 timeout, notify, MACH_MSG_NULL, 0);
109
110 return mr;
111 }
112
113 mach_msg_return_t
mach_msg_overwrite(msg,option,send_size,rcv_limit,rcv_name,timeout,notify,rcv_msg,rcv_msg_size)114 mach_msg_overwrite(msg, option, send_size, rcv_limit, rcv_name, timeout,
115 notify, rcv_msg, rcv_msg_size)
116 mach_msg_header_t *msg;
117 mach_msg_option_t option;
118 mach_msg_size_t send_size;
119 mach_msg_size_t rcv_limit;
120 mach_port_t rcv_name;
121 mach_msg_timeout_t timeout;
122 mach_port_t notify;
123 mach_msg_header_t *rcv_msg;
124 mach_msg_size_t rcv_msg_size;
125 {
126 mach_msg_return_t mr;
127
128 /*
129 * Consider the following cases:
130 * 1) Errors in pseudo-receive (eg, MACH_SEND_INTERRUPTED
131 * plus special bits).
132 * 2) Use of MACH_SEND_INTERRUPT/MACH_RCV_INTERRUPT options.
133 * 3) RPC calls with interruptions in one/both halves.
134 *
135 * We refrain from passing the option bits that we implement
136 * to the kernel. This prevents their presence from inhibiting
137 * the kernel's fast paths (when it checks the option value).
138 */
139
140 mr = mach_msg_overwrite_trap(msg, option &~ LIBMACH_OPTIONS,
141 send_size, rcv_limit, rcv_name,
142 timeout, notify, rcv_msg, rcv_msg_size);
143 if (mr == MACH_MSG_SUCCESS)
144 return MACH_MSG_SUCCESS;
145
146 if ((option & MACH_SEND_INTERRUPT) == 0)
147 while (mr == MACH_SEND_INTERRUPTED)
148 mr = mach_msg_overwrite_trap(msg,
149 option &~ LIBMACH_OPTIONS,
150 send_size, rcv_limit, rcv_name,
151 timeout, notify, rcv_msg, rcv_msg_size);
152
153 if ((option & MACH_RCV_INTERRUPT) == 0)
154 while (mr == MACH_RCV_INTERRUPTED)
155 mr = mach_msg_overwrite_trap(msg,
156 option &~ (LIBMACH_OPTIONS|MACH_SEND_MSG),
157 0, rcv_limit, rcv_name,
158 timeout, notify, rcv_msg, rcv_msg_size);
159
160 return mr;
161 }
162
163 mach_msg_return_t
mach_msg_send(mach_msg_header_t * msg)164 mach_msg_send(mach_msg_header_t *msg)
165 {
166 return mach_msg(msg, MACH_SEND_MSG,
167 msg->msgh_size, 0, MACH_PORT_NULL,
168 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
169 }
170
171 mach_msg_return_t
mach_msg_receive(mach_msg_header_t * msg)172 mach_msg_receive(mach_msg_header_t *msg)
173 {
174 return mach_msg(msg, MACH_RCV_MSG,
175 0, msg->msgh_size, msg->msgh_local_port,
176 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
177 }
178
179 static void
mach_msg_destroy_port(mach_port_t port,mach_msg_type_name_t type)180 mach_msg_destroy_port(mach_port_t port, mach_msg_type_name_t type)
181 {
182 if (MACH_PORT_VALID(port)) switch (type) {
183 case MACH_MSG_TYPE_MOVE_SEND:
184 case MACH_MSG_TYPE_MOVE_SEND_ONCE:
185 /* destroy the send/send-once right */
186 (void) mach_port_deallocate(mach_task_self(), port);
187 break;
188
189 case MACH_MSG_TYPE_MOVE_RECEIVE:
190 /* destroy the receive right */
191 (void) mach_port_mod_refs(mach_task_self(), port,
192 MACH_PORT_RIGHT_RECEIVE, -1);
193 break;
194
195 case MACH_MSG_TYPE_MAKE_SEND:
196 /* create a send right and then destroy it */
197 (void) mach_port_insert_right(mach_task_self(), port,
198 port, MACH_MSG_TYPE_MAKE_SEND);
199 (void) mach_port_deallocate(mach_task_self(), port);
200 break;
201
202 case MACH_MSG_TYPE_MAKE_SEND_ONCE:
203 /* create a send-once right and then destroy it */
204 (void) mach_port_extract_right(mach_task_self(), port,
205 MACH_MSG_TYPE_MAKE_SEND_ONCE,
206 &port, &type);
207 (void) mach_port_deallocate(mach_task_self(), port);
208 break;
209 }
210 }
211
212 static void
mach_msg_destroy_memory(vm_offset_t addr,vm_size_t size)213 mach_msg_destroy_memory(vm_offset_t addr, vm_size_t size)
214 {
215 if (size != 0)
216 (void) mach_vm_deallocate(mach_task_self(), addr, size);
217 }
218
219 /*
220 * Routine: mach_msg_destroy
221 * Purpose:
222 * mach_msg_destroy is useful in two contexts.
223 *
224 * First, it can deallocate all port rights and
225 * out-of-line memory in a received message.
226 * When a server receives a request it doesn't want,
227 * it needs this functionality.
228 *
229 * Second, it can mimic the side-effects of a msg-send
230 * operation. The effect is as if the message were sent
231 * and then destroyed inside the kernel. When a server
232 * can't send a reply (because the client died),
233 * it needs this functionality.
234 */
235 void
mach_msg_destroy(mach_msg_header_t * msg)236 mach_msg_destroy(mach_msg_header_t *msg)
237 {
238 mach_msg_bits_t mbits = msg->msgh_bits;
239
240 /*
241 * The msgh_local_port field doesn't hold a port right.
242 * The receive operation consumes the destination port right.
243 */
244
245 mach_msg_destroy_port(msg->msgh_remote_port, MACH_MSGH_BITS_REMOTE(mbits));
246
247 if (mbits & MACH_MSGH_BITS_COMPLEX) {
248 mach_msg_base_t base __aligned(8);
249 mach_msg_base_t *basep;
250 mach_msg_body_t *body;
251 mach_msg_descriptor_t *saddr, *eaddr;
252
253 basep = &base;
254 memcpy(basep, msg, sizeof(base));
255 body = (mach_msg_body_t *) (msg + 1);
256 saddr = (mach_msg_descriptor_t *) (uintptr_t)(basep + 1);
257 eaddr = saddr + body->msgh_descriptor_count;
258
259 for ( ; saddr < eaddr; saddr++) {
260 switch (saddr->type.type) {
261
262 case MACH_MSG_PORT_DESCRIPTOR: {
263 mach_msg_port_descriptor_t *dsc;
264
265 /*
266 * Destroy port rights carried in the message
267 */
268 dsc = &saddr->port;
269 mach_msg_destroy_port(dsc->name, dsc->disposition);
270 break;
271 }
272
273 case MACH_MSG_OOL_DESCRIPTOR : {
274 mach_msg_ool_descriptor_t *dsc;
275
276 /*
277 * Destroy memory carried in the message
278 */
279 dsc = &saddr->out_of_line;
280 if (dsc->deallocate) {
281 mach_msg_destroy_memory((vm_offset_t)dsc->address,
282 dsc->size);
283 }
284 break;
285 }
286
287 case MACH_MSG_OOL_PORTS_DESCRIPTOR : {
288 mach_port_t *ports;
289 mach_msg_ool_ports_descriptor_t *dsc;
290 mach_msg_type_number_t j;
291
292 /*
293 * Destroy port rights carried in the message
294 */
295 dsc = &saddr->ool_ports;
296 ports = (mach_port_t *) dsc->address;
297 for (j = 0; j < dsc->count; j++, ports++) {
298 mach_msg_destroy_port(*ports, dsc->disposition);
299 }
300
301 /*
302 * Destroy memory carried in the message
303 */
304 if (dsc->deallocate) {
305 mach_msg_destroy_memory((vm_offset_t)dsc->address,
306 dsc->count * sizeof(mach_port_t));
307 }
308 break;
309 }
310 }
311 }
312 }
313 }
314
315 /*
316 * Routine: mach_msg_server_once
317 * Purpose:
318 * A simple generic server function. It allows more flexibility
319 * than mach_msg_server by processing only one message request
320 * and then returning to the user. Note that more in the way
321 * of error codes are returned to the user; specifically, any
322 * failing error from mach_msg_overwrite_trap will be returned
323 * (though errors from the demux routine or the routine it calls
324 * will not be).
325 */
326 mach_msg_return_t
mach_msg_server_once(boolean_t (* demux)(mach_msg_header_t *,mach_msg_header_t *),mach_msg_size_t max_size,mach_port_t rcv_name,mach_msg_options_t options)327 mach_msg_server_once(
328 boolean_t (*demux)(mach_msg_header_t *, mach_msg_header_t *),
329 mach_msg_size_t max_size,
330 mach_port_t rcv_name,
331 mach_msg_options_t options)
332 {
333 mig_reply_error_t *bufRequest = 0, *bufReply = 0, *bufTemp;
334 boolean_t allocatedRequest = FALSE, allocatedReply = FALSE, allocatedTemp;
335 register mach_msg_return_t mr;
336 register kern_return_t kr;
337
338 if ((kr = mach_vm_allocate(mach_task_self(),
339 (vm_address_t *)&bufRequest,
340 max_size + MAX_TRAILER_SIZE,
341 TRUE)) != KERN_SUCCESS)
342 goto cleanup;
343 allocatedRequest = TRUE;
344
345 if ((kr = mach_vm_allocate(mach_task_self(),
346 (vm_address_t *)&bufReply,
347 max_size + MAX_TRAILER_SIZE,
348 TRUE)) != KERN_SUCCESS)
349 goto cleanup;
350 allocatedReply = TRUE;
351
352 mr = mach_msg_overwrite_trap(&bufRequest->Head, MACH_RCV_MSG|options,
353 0, max_size, rcv_name,
354 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL,
355 (mach_msg_header_t *) 0, 0);
356 if (mr == MACH_MSG_SUCCESS) {
357 /* we have a request message */
358
359 (void) (*demux)(&bufRequest->Head, &bufReply->Head);
360
361 if (!(bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) &&
362 bufReply->RetCode != KERN_SUCCESS) {
363 if (bufReply->RetCode == MIG_NO_REPLY) {
364 /*
365 * This return code is a little tricky--
366 * it appears that the demux routine found an
367 * error of some sort, but since that error
368 * would not normally get returned either to
369 * the local user or the remote one, we pretend it's
370 * ok.
371 */
372
373 kr = KERN_SUCCESS;
374 goto cleanup;
375 }
376
377 /* don't destroy the reply port right,
378 so we can send an error message */
379 bufRequest->Head.msgh_remote_port = MACH_PORT_NULL;
380 mach_msg_destroy(&bufRequest->Head);
381 }
382
383 if (bufReply->Head.msgh_remote_port == MACH_PORT_NULL) {
384 /* no reply port, so destroy the reply */
385 if (bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)
386 mach_msg_destroy(&bufReply->Head);
387
388 kr = KERN_SUCCESS;
389 goto cleanup;
390 }
391
392 /* send reply. */
393
394 bufTemp = bufRequest;
395 bufRequest = bufReply;
396 bufReply = bufTemp;
397
398 allocatedTemp = allocatedRequest;
399 allocatedRequest = allocatedReply;
400 allocatedReply = allocatedRequest;
401
402 /*
403 * We don't want to block indefinitely because the client
404 * isn't receiving messages from the reply port.
405 * If we have a send-once right for the reply port, then
406 * this isn't a concern because the send won't block.
407 * If we have a send right, we need to use MACH_SEND_TIMEOUT.
408 * To avoid falling off the kernel's fast RPC path unnecessarily,
409 * we only supply MACH_SEND_TIMEOUT when absolutely necessary.
410 */
411
412 mr = mach_msg_overwrite_trap(&bufRequest->Head,
413 (MACH_MSGH_BITS_REMOTE(bufRequest->Head.msgh_bits) ==
414 MACH_MSG_TYPE_MOVE_SEND_ONCE) ?
415 MACH_SEND_MSG|options :
416 MACH_SEND_MSG|MACH_SEND_TIMEOUT|options,
417 bufRequest->Head.msgh_size, 0, MACH_PORT_NULL,
418 0, MACH_PORT_NULL, (mach_msg_header_t *) 0, 0);
419 }
420 /* Has a message error occurred? */
421
422 switch (mr) {
423 case MACH_SEND_INVALID_DEST:
424 case MACH_SEND_TIMED_OUT:
425 /* the reply can't be delivered, so destroy it */
426 mach_msg_destroy(&bufRequest->Head);
427 kr = KERN_SUCCESS; /* Matches error hiding behavior in
428 mach_msg_server. */
429 goto cleanup;
430
431
432 case MACH_RCV_TOO_LARGE:
433 kr = KERN_SUCCESS; /* Matches error hiding behavior in
434 mach_msg_server. */
435 goto cleanup;
436
437 default:
438 /* Includes success case. */
439 kr = mr;
440 goto cleanup;
441 }
442
443 cleanup:
444 if ( allocatedRequest == TRUE )
445 (void)mach_vm_deallocate(mach_task_self(),
446 (vm_address_t) bufRequest,
447 max_size + MAX_TRAILER_SIZE);
448 if ( allocatedReply == TRUE )
449 (void)mach_vm_deallocate(mach_task_self(),
450 (vm_address_t) bufReply,
451 max_size + MAX_TRAILER_SIZE);
452 return kr;
453
454 }
455
456 /*
457 * Routine: mach_msg_server
458 * Purpose:
459 * A simple generic server function. Note that changes here
460 * should be considered for duplication above.
461 */
462 mach_msg_return_t
mach_msg_server(boolean_t (* demux)(mach_msg_header_t *,mach_msg_header_t *),mach_msg_size_t max_size,mach_port_t rcv_name,mach_msg_options_t options)463 mach_msg_server(
464 boolean_t (*demux)(mach_msg_header_t *, mach_msg_header_t *),
465 mach_msg_size_t max_size,
466 mach_port_t rcv_name,
467 mach_msg_options_t options)
468 {
469 mig_reply_error_t *bufRequest = 0, *bufReply = 0, *bufTemp;
470 boolean_t allocatedRequest = FALSE, allocatedReply = FALSE, allocatedTemp;
471 register mach_msg_return_t mr;
472 register kern_return_t kr;
473
474 if ((kr = mach_vm_allocate(mach_task_self(),
475 (vm_address_t *)&bufRequest,
476 max_size + MAX_TRAILER_SIZE,
477 TRUE)) != KERN_SUCCESS)
478 goto cleanup;
479 allocatedRequest = TRUE;
480
481 if ((kr = mach_vm_allocate(mach_task_self(),
482 (vm_address_t *)&bufReply,
483 max_size + MAX_TRAILER_SIZE,
484 TRUE)) != KERN_SUCCESS)
485 goto cleanup;
486 allocatedReply = TRUE;
487
488 for (;;) {
489 get_request:
490 mr = mach_msg_overwrite_trap(&bufRequest->Head, MACH_RCV_MSG|options,
491 0, max_size, rcv_name,
492 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL,
493 (mach_msg_header_t *) 0, 0);
494 while (mr == MACH_MSG_SUCCESS) {
495 /* we have a request message */
496
497 (void) (*demux)(&bufRequest->Head, &bufReply->Head);
498
499 if (!(bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) &&
500 bufReply->RetCode != KERN_SUCCESS) {
501 if (bufReply->RetCode == MIG_NO_REPLY)
502 goto get_request;
503
504 /* don't destroy the reply port right,
505 so we can send an error message */
506 bufRequest->Head.msgh_remote_port = MACH_PORT_NULL;
507 mach_msg_destroy(&bufRequest->Head);
508 }
509
510 if (bufReply->Head.msgh_remote_port == MACH_PORT_NULL) {
511 /* no reply port, so destroy the reply */
512 if (bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)
513 mach_msg_destroy(&bufReply->Head);
514
515 goto get_request;
516 }
517
518 /* send reply and get next request */
519
520 bufTemp = bufRequest;
521 bufRequest = bufReply;
522 bufReply = bufTemp;
523
524 allocatedTemp = allocatedRequest;
525 allocatedRequest = allocatedReply;
526 allocatedReply = allocatedTemp;
527
528 /*
529 * We don't want to block indefinitely because the client
530 * isn't receiving messages from the reply port.
531 * If we have a send-once right for the reply port, then
532 * this isn't a concern because the send won't block.
533 * If we have a send right, we need to use MACH_SEND_TIMEOUT.
534 * To avoid falling off the kernel's fast RPC path unnecessarily,
535 * we only supply MACH_SEND_TIMEOUT when absolutely necessary.
536 */
537
538 mr = mach_msg_overwrite_trap(&bufRequest->Head,
539 (MACH_MSGH_BITS_REMOTE(bufRequest->Head.msgh_bits) ==
540 MACH_MSG_TYPE_MOVE_SEND_ONCE) ?
541 MACH_SEND_MSG|MACH_RCV_MSG|options :
542 MACH_SEND_MSG|MACH_SEND_TIMEOUT|MACH_RCV_MSG|options,
543 bufRequest->Head.msgh_size, max_size, rcv_name,
544 0, MACH_PORT_NULL, (mach_msg_header_t *) 0, 0);
545 }
546
547 /* a message error occurred */
548
549 switch (mr) {
550 case MACH_SEND_INVALID_DEST:
551 case MACH_SEND_TIMED_OUT:
552 /* the reply can't be delivered, so destroy it */
553 mach_msg_destroy(&bufRequest->Head);
554 goto cleanup;
555
556 case MACH_RCV_TOO_LARGE:
557 /* the kernel destroyed the request */
558 goto cleanup;
559
560 default:
561 /* should only happen if the server is buggy */
562 kr = mr;
563 goto cleanup;
564 }
565
566 cleanup:
567 if ( allocatedRequest == TRUE )
568 (void)mach_vm_deallocate(mach_task_self(),
569 (vm_address_t) bufRequest,
570 max_size + MAX_TRAILER_SIZE);
571 if ( allocatedReply == TRUE )
572 (void)mach_vm_deallocate(mach_task_self(),
573 (vm_address_t) bufReply,
574 max_size + MAX_TRAILER_SIZE);
575 }
576 }
577