]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_utilities/lib/machserver.cpp
Security-59754.41.1.tar.gz
[apple/security.git] / OSX / libsecurity_utilities / lib / machserver.cpp
1 /*
2 * Copyright (c) 2000-2007,2011-2013 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_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. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24
25 //
26 // machserver - C++ shell for writing Mach 3 servers
27 //
28 #include "machserver.h"
29 #include <servers/bootstrap.h>
30 #include <mach/kern_return.h>
31 #include <mach/message.h>
32 #include <mach/mig_errors.h>
33 #include "mach_notifyServer.h"
34 #include <security_utilities/debugging.h>
35 #include <malloc/malloc.h>
36
37 #if defined(USECFCURRENTTIME)
38 # include <CoreFoundation/CFDate.h>
39 #else
40 # include <sys/time.h>
41 #endif
42
43 #define SEC_MACH_AUDIT_TOKEN_PID (5)
44
45 namespace Security {
46 namespace MachPlusPlus {
47
48
49 //
50 // Global per-thread information
51 //
52 ModuleNexus< ThreadNexus<MachServer::PerThread> > MachServer::thread;
53
54
55 //
56 // Create a server object.
57 // The resulting object is not "active", and any number of server objects
58 // can be in this "prepared" state at the same time.
59 //
60 MachServer::MachServer()
61 { setup("(anonymous)"); }
62
63 MachServer::MachServer(const char *name)
64 : mServerPort(name, bootstrap)
65 { setup(name); }
66
67 MachServer::MachServer(const char *name, const Bootstrap &boot)
68 : bootstrap(boot), mServerPort(name, bootstrap)
69 { setup(name); }
70
71 void MachServer::setup(const char *name)
72 {
73 workerTimeout = 60 * 2; // 2 minutes default timeout
74 maxWorkerCount = 100; // sanity check limit
75 useFloatingThread = false; // tight thread management
76
77 mPortSet += mServerPort;
78 }
79
80 MachServer::~MachServer()
81 {
82 // The ReceivePort members will clean themselves up.
83 // The bootstrap server will clear us from its map when our receive port dies.
84 }
85
86
87 //
88 // Add and remove extra listening ports.
89 // Messages directed to those ports are dispatched through the main handler.
90 // To get automatic call-out to another handler, use the Handler class.
91 //
92 void MachServer::add(Port receiver)
93 {
94 secinfo("machserver", "port add: %d", receiver.port());
95 mPortSet += receiver;
96 }
97
98 void MachServer::remove(Port receiver)
99 {
100 secinfo("machserver", "port remove: %d", receiver.port());
101 mPortSet -= receiver;
102 }
103
104
105 //
106 // Register for mach port notifications
107 //
108 void MachServer::notifyIfDead(Port port, bool doNotify) const
109 {
110 if (doNotify)
111 port.requestNotify(mServerPort);
112 else
113 port.cancelNotify();
114 }
115
116 void MachServer::notifyIfUnused(Port port, bool doNotify) const
117 {
118 if (doNotify)
119 port.requestNotify(port, MACH_NOTIFY_NO_SENDERS, true);
120 else
121 port.cancelNotify(MACH_NOTIFY_NO_SENDERS);
122 }
123
124
125 //
126 // Initiate service.
127 // This call will take control of the current thread and use it to service
128 // incoming requests. The thread will not be released until an error happens, which
129 // will cause an exception to be thrown. In other words, this never returns normally.
130 // We may also be creating additional threads to service concurrent requests
131 // as appropriate.
132 // @@@ Msg-errors in additional threads are not acted upon.
133 //
134 void MachServer::run(mach_msg_size_t maxSize, mach_msg_options_t options)
135 {
136 // establish server-global (thread-shared) parameters
137 mMaxSize = maxSize;
138 mMsgOptions = options;
139
140 // establish the thread pool state
141 // (don't need managerLock since we're the only thread as of yet)
142 idleCount = workerCount = 1;
143 nextCheckTime = Time::now() + workerTimeout;
144 leastIdleWorkers = 1;
145 highestWorkerCount = 1;
146
147 // run server loop in initial (immortal) thread
148 secinfo("machserver", "start thread");
149 runServerThread(false);
150 secinfo("machserver", "end thread");
151
152 // primary server thread exited somehow (not currently possible)
153 assert(false);
154 }
155
156
157 //
158 // This is the core of a server thread at work. It takes over the thread until
159 // (a) an error occurs, throwing an exception
160 // (b) low-load timeout happens, causing a normal return (doTimeout only)
161 // This code was once based on mach_msg_server.c, but it is getting harder to notice
162 // the lingering resemblance.
163 //
164 extern "C" boolean_t cdsa_notify_server(mach_msg_header_t *in, mach_msg_header_t *out);
165
166 void MachServer::runServerThread(bool doTimeout)
167 {
168 // allocate request/reply buffers
169 Message bufRequest(mMaxSize);
170 Message bufReply(mMaxSize);
171
172 // all exits from runServerThread are through exceptions
173 try {
174 // register as a worker thread
175 perThread().server = this;
176
177 for (;;) {
178 // progress hook
179 eventDone();
180
181 // process all pending timers
182 while (processTimer()) {}
183
184 // check for worker idle timeout
185 { StLock<Mutex> _(managerLock);
186 // record idle thread low-water mark in scan interval
187 if (idleCount < leastIdleWorkers)
188 leastIdleWorkers = idleCount;
189
190 // perform self-timeout processing
191 if (doTimeout) {
192 if (workerCount > maxWorkerCount) // someone reduced maxWorkerCount recently...
193 break; // ... so release this thread immediately
194 Time::Absolute rightNow = Time::now();
195 if (rightNow >= nextCheckTime) { // reaping period complete; process
196 UInt32 idlers = leastIdleWorkers;
197 secinfo("machserver", "reaping workers: %d %d", (uint32_t) workerCount, (uint32_t) idlers);
198 nextCheckTime = rightNow + workerTimeout;
199 leastIdleWorkers = INT_MAX;
200 if (idlers > 1) // multiple idle threads throughout measuring interval...
201 break; // ... so release this thread now
202 }
203 }
204 }
205
206 // determine next timeout (if any)
207 bool indefinite = false;
208 Time::Interval timeout = workerTimeout;
209 { StLock<Mutex> _(managerLock);
210 if (timers.empty()) {
211 indefinite = !doTimeout;
212 } else {
213 timeout = max(Time::Interval(0), timers.next() - Time::now());
214 if (doTimeout && workerTimeout < timeout)
215 timeout = workerTimeout;
216 }
217 }
218
219 // receive next IPC request (or wait for timeout)
220 mach_msg_return_t mr = indefinite ?
221 mach_msg_overwrite(bufRequest,
222 MACH_RCV_MSG | mMsgOptions,
223 0, mMaxSize, mPortSet,
224 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL,
225 (mach_msg_header_t *) 0, 0)
226 :
227 mach_msg_overwrite(bufRequest,
228 MACH_RCV_MSG | MACH_RCV_TIMEOUT | MACH_RCV_INTERRUPT | mMsgOptions,
229 0, mMaxSize, mPortSet,
230 mach_msg_timeout_t(timeout.mSeconds()), MACH_PORT_NULL,
231 (mach_msg_header_t *) 0, 0);
232
233 switch (mr) {
234 case MACH_MSG_SUCCESS:
235 // process received request message below
236 break;
237 default:
238 secinfo("machserver", "received error: %d", mr);
239 continue;
240 }
241
242 // reset the buffer each time, handlers don't consistently set out params
243 bufReply.clearBuffer();
244
245 // process received message
246 if (bufRequest.msgId() >= MACH_NOTIFY_FIRST &&
247 bufRequest.msgId() <= MACH_NOTIFY_LAST) {
248 // mach kernel notification message
249 // we assume this is quick, so no thread arbitration here
250 mach_msg_audit_trailer_t *tlr = bufRequest.auditTrailer();
251 if (tlr == NULL || tlr->msgh_audit.val[SEC_MACH_AUDIT_TOKEN_PID] != 0) {
252 secnotice("machserver", "ignoring invalid notify message");
253 continue;
254 }
255 cdsa_notify_server(bufRequest, bufReply);
256 } else {
257 // normal request message
258 StLock<MachServer, &MachServer::busy, &MachServer::idle> _(*this);
259 secinfo("machserver", "begin request: %d, %d", bufRequest.localPort().port(), bufRequest.msgId());
260
261 // try subsidiary handlers first
262 bool handled = false;
263 for (HandlerSet::const_iterator it = mHandlers.begin();
264 it != mHandlers.end(); it++)
265 if (bufRequest.localPort() == (*it)->port()) {
266 (*it)->handle(bufRequest, bufReply);
267 handled = true;
268 }
269 if (!handled) {
270 // unclaimed, send to main handler
271 handle(bufRequest, bufReply);
272 }
273
274 secinfo("machserver", "end request");
275 }
276
277 // process reply generated by handler
278 if (!(bufReply.bits() & MACH_MSGH_BITS_COMPLEX) &&
279 bufReply.returnCode() != KERN_SUCCESS) {
280 if (bufReply.returnCode() == MIG_NO_REPLY)
281 continue;
282 // don't destroy the reply port right, so we can send an error message
283 bufRequest.remotePort(MACH_PORT_NULL);
284 mach_msg_destroy(bufRequest);
285 }
286
287 if (bufReply.remotePort() == MACH_PORT_NULL) {
288 // no reply port, so destroy the reply
289 if (bufReply.bits() & MACH_MSGH_BITS_COMPLEX)
290 bufReply.destroy();
291 continue;
292 }
293
294 /*
295 * We don't want to block indefinitely because the client
296 * isn't receiving messages from the reply port.
297 * If we have a send-once right for the reply port, then
298 * this isn't a concern because the send won't block.
299 * If we have a send right, we need to use MACH_SEND_TIMEOUT.
300 * To avoid falling off the kernel's fast RPC path unnecessarily,
301 * we only supply MACH_SEND_TIMEOUT when absolutely necessary.
302 */
303 mr = mach_msg_overwrite(bufReply,
304 (MACH_MSGH_BITS_REMOTE(bufReply.bits()) ==
305 MACH_MSG_TYPE_MOVE_SEND_ONCE) ?
306 MACH_SEND_MSG | mMsgOptions :
307 MACH_SEND_MSG | MACH_SEND_TIMEOUT | mMsgOptions,
308 bufReply.length(), 0, MACH_PORT_NULL,
309 0, MACH_PORT_NULL, NULL, 0);
310 switch (mr) {
311 case MACH_MSG_SUCCESS:
312 break;
313 case MACH_SEND_INVALID_DEST:
314 case MACH_SEND_TIMED_OUT:
315 secinfo("machserver", "send error: %d %d", mr, bufReply.remotePort().port());
316 bufReply.destroy();
317 break;
318 default:
319 secinfo("machserver", "send error: %d %d", mr, bufReply.remotePort().port());
320 break;
321 }
322
323
324 // clean up after the transaction
325 releaseDeferredAllocations();
326 }
327 perThread().server = NULL;
328
329 } catch (...) {
330 perThread().server = NULL;
331 throw;
332 }
333 }
334
335
336 //
337 // Manage subsidiary port handlers
338 //
339 void MachServer::add(Handler &handler)
340 {
341 assert(mHandlers.find(&handler) == mHandlers.end());
342 assert(handler.port() != MACH_PORT_NULL);
343 mHandlers.insert(&handler);
344 mPortSet += handler.port();
345 }
346
347 void MachServer::remove(Handler &handler)
348 {
349 assert(mHandlers.find(&handler) != mHandlers.end());
350 mHandlers.erase(&handler);
351 mPortSet -= handler.port();
352 }
353
354
355 //
356 // Abstract auxiliary message handlers
357 //
358 MachServer::Handler::~Handler()
359 { /* virtual */ }
360
361
362 //
363 // Implement a Handler that sends no reply
364 //
365 boolean_t MachServer::NoReplyHandler::handle(mach_msg_header_t *in, mach_msg_header_t *out)
366 {
367 // set up reply message to be valid (enough) and read "do not send reply"
368 out->msgh_bits = 0;
369 out->msgh_remote_port = MACH_PORT_NULL;
370 out->msgh_size = sizeof(mig_reply_error_t);
371 ((mig_reply_error_t *)out)->RetCode = MIG_NO_REPLY;
372
373 // call input-only handler
374 return handle(in);
375 }
376
377
378 //
379 // Register a memory block for deferred release.
380 //
381 void MachServer::releaseWhenDone(Allocator &alloc, void *memory)
382 {
383 if (memory) {
384 set<Allocation> &releaseSet = perThread().deferredAllocations;
385 assert(releaseSet.find(Allocation(memory, alloc)) == releaseSet.end());
386 secinfo("machserver", "allocing register %p with alloc %p", memory, &alloc);
387 releaseSet.insert(Allocation(memory, alloc));
388 }
389 }
390
391
392 //
393 // Run through the accumulated deferred allocations and release them.
394 // This is done automatically on every pass through the server loop;
395 // it must be called by subclasses that implement their loop in some
396 // other way.
397 // @@@X Needs to be thread local
398 //
399 void MachServer::releaseDeferredAllocations()
400 {
401 set<Allocation> &releaseSet = perThread().deferredAllocations;
402 for (set<Allocation>::iterator it = releaseSet.begin(); it != releaseSet.end(); it++) {
403 secinfo("machserver", "releasing alloc at %p with %p", it->addr, it->allocator);
404
405 // before we release the deferred allocation, zap it so that secrets aren't left in memory
406 size_t memSize = malloc_size(it->addr);
407 bzero(it->addr, memSize);
408 it->allocator->free(it->addr);
409 }
410 releaseSet.erase(releaseSet.begin(), releaseSet.end());
411 }
412
413
414 //
415 // The handler function calls this if it realizes that it might be blocked
416 // (or doing something that takes a long time). We respond by ensuring that
417 // at least one more thread is ready to serve requests.
418 // Calls the threadLimitReached callback in the server object if the thread
419 // limit has been exceeded and a needed new thread was not created.
420 //
421 void MachServer::longTermActivity()
422 {
423 if (!useFloatingThread) {
424 StLock<Mutex> _(managerLock);
425 ensureReadyThread();
426 }
427 }
428
429 void MachServer::busy()
430 {
431 StLock<Mutex> _(managerLock);
432 idleCount--;
433 if (useFloatingThread)
434 ensureReadyThread();
435 }
436
437 void MachServer::idle()
438 {
439 StLock<Mutex> _(managerLock);
440 idleCount++;
441 }
442
443
444 void MachServer::ensureReadyThread()
445 {
446 if (idleCount == 0) {
447 if (workerCount >= maxWorkerCount) {
448 this->threadLimitReached(workerCount); // call remedial handler
449 }
450 if (workerCount < maxWorkerCount) { // threadLimit() may have raised maxWorkerCount
451 (new LoadThread(*this))->run();
452 }
453 }
454 }
455
456
457 //
458 // The callback hook for our subclasses.
459 // The default does nothing, thereby denying further thread creation.
460 // You could do something like maxThreads(limit+1) here to grant an variance;
461 // or throw an exception to avoid possible deadlocks (this would abort the current
462 // request but not otherwise impact the server's operation).
463 //
464 void MachServer::threadLimitReached(UInt32 limit)
465 {
466 }
467
468
469 //
470 // What our (non-primary) load threads do
471 //
472 void MachServer::LoadThread::action()
473 {
474 //@@@ race condition?! can server exit before helpers thread gets here?
475
476 // register the worker thread and go
477 server.addThread(this);
478 try {
479 secinfo("machserver", "start thread");
480 server.runServerThread(true);
481 secinfo("machserver", "end thread");
482 } catch (...) {
483 // fell out of server loop by error. Let the thread go quietly
484 secinfo("machserver", "end thread (due to error)");
485 }
486 server.removeThread(this);
487 }
488
489
490 //
491 // Thread accounting
492 //
493 void MachServer::addThread(Thread *thread)
494 {
495 StLock<Mutex> _(managerLock);
496 workerCount++;
497 idleCount++;
498 workers.insert(thread);
499 }
500
501 void MachServer::removeThread(Thread *thread)
502 {
503 StLock<Mutex> _(managerLock);
504 workerCount--;
505 idleCount--;
506 workers.erase(thread);
507 }
508
509
510 //
511 // Timer management
512 //
513 MachServer::Timer::~Timer()
514 { }
515
516 void MachServer::Timer::select()
517 { }
518
519 void MachServer::Timer::unselect()
520 { }
521
522 bool MachServer::processTimer()
523 {
524 Timer *top;
525 { StLock<Mutex> _(managerLock); // could have multiple threads trying this
526 if (!(top = static_cast<Timer *>(timers.pop(Time::now()))))
527 return false; // nothing (more) to be done now
528 } // drop lock; work has been retrieved
529 try {
530 secinfo("machserver", "timer start: %p, %d, %f", top, top->longTerm(), Time::now().internalForm());
531 StLock<MachServer::Timer,
532 &MachServer::Timer::select, &MachServer::Timer::unselect> _t(*top);
533 if (top->longTerm()) {
534 StLock<MachServer, &MachServer::busy, &MachServer::idle> _(*this);
535 top->action();
536 } else {
537 top->action();
538 }
539 secinfo("machserver", "timer end (false)");
540 } catch (...) {
541 secinfo("machserver", "timer end (true)");
542 }
543 return true;
544 }
545
546 void MachServer::setTimer(Timer *timer, Time::Absolute when)
547 {
548 StLock<Mutex> _(managerLock);
549 timers.schedule(timer, when);
550 }
551
552 void MachServer::clearTimer(Timer *timer)
553 {
554 StLock<Mutex> _(managerLock);
555 if (timer->scheduled())
556 timers.unschedule(timer);
557 }
558
559
560 //
561 // Notification hooks and shims. Defaults do nothing.
562 //
563 kern_return_t cdsa_mach_notify_dead_name(mach_port_t, mach_port_name_t port)
564 {
565 try {
566 MachServer::active().notifyDeadName(port);
567 } catch (...) {
568 }
569 // the act of receiving a dead name notification allocates a dead-name
570 // right that must be deallocated
571 mach_port_deallocate(mach_task_self(), port);
572 return KERN_SUCCESS;
573 }
574
575 void MachServer::notifyDeadName(Port) { }
576
577 kern_return_t cdsa_mach_notify_port_deleted(mach_port_t, mach_port_name_t port)
578 {
579 try {
580 MachServer::active().notifyPortDeleted(port);
581 } catch (...) {
582 }
583 return KERN_SUCCESS;
584 }
585
586 void MachServer::notifyPortDeleted(Port) { }
587
588 kern_return_t cdsa_mach_notify_port_destroyed(mach_port_t, mach_port_name_t port)
589 {
590 try {
591 MachServer::active().notifyPortDestroyed(port);
592 } catch (...) {
593 }
594 return KERN_SUCCESS;
595 }
596
597 void MachServer::notifyPortDestroyed(Port) { }
598
599 kern_return_t cdsa_mach_notify_send_once(mach_port_t port)
600 {
601 try {
602 MachServer::active().notifySendOnce(port);
603 } catch (...) {
604 }
605 return KERN_SUCCESS;
606 }
607
608 void MachServer::notifySendOnce(Port) { }
609
610 kern_return_t cdsa_mach_notify_no_senders(mach_port_t port, mach_port_mscount_t count)
611 {
612 try {
613 MachServer::active().notifyNoSenders(port, count);
614 } catch (...) {
615 }
616 return KERN_SUCCESS;
617 }
618
619 void MachServer::notifyNoSenders(Port, mach_port_mscount_t) { }
620
621 void MachServer::eventDone() { }
622
623
624 } // end namespace MachPlusPlus
625
626 } // end namespace Security