]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_utilities/lib/machserver.cpp
Security-58286.220.15.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_notify.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 default:
314 secinfo("machserver", "send error: %d %d", mr, bufReply.remotePort().port());
315 bufReply.destroy();
316 break;
317 }
318
319
320 // clean up after the transaction
321 releaseDeferredAllocations();
322 }
323 perThread().server = NULL;
324
325 } catch (...) {
326 perThread().server = NULL;
327 throw;
328 }
329 }
330
331
332 //
333 // Manage subsidiary port handlers
334 //
335 void MachServer::add(Handler &handler)
336 {
337 assert(mHandlers.find(&handler) == mHandlers.end());
338 assert(handler.port() != MACH_PORT_NULL);
339 mHandlers.insert(&handler);
340 mPortSet += handler.port();
341 }
342
343 void MachServer::remove(Handler &handler)
344 {
345 assert(mHandlers.find(&handler) != mHandlers.end());
346 mHandlers.erase(&handler);
347 mPortSet -= handler.port();
348 }
349
350
351 //
352 // Abstract auxiliary message handlers
353 //
354 MachServer::Handler::~Handler()
355 { /* virtual */ }
356
357
358 //
359 // Implement a Handler that sends no reply
360 //
361 boolean_t MachServer::NoReplyHandler::handle(mach_msg_header_t *in, mach_msg_header_t *out)
362 {
363 // set up reply message to be valid (enough) and read "do not send reply"
364 out->msgh_bits = 0;
365 out->msgh_remote_port = MACH_PORT_NULL;
366 out->msgh_size = sizeof(mig_reply_error_t);
367 ((mig_reply_error_t *)out)->RetCode = MIG_NO_REPLY;
368
369 // call input-only handler
370 return handle(in);
371 }
372
373
374 //
375 // Register a memory block for deferred release.
376 //
377 void MachServer::releaseWhenDone(Allocator &alloc, void *memory)
378 {
379 if (memory) {
380 set<Allocation> &releaseSet = perThread().deferredAllocations;
381 assert(releaseSet.find(Allocation(memory, alloc)) == releaseSet.end());
382 secinfo("machserver", "allocing register %p with alloc %p", memory, &alloc);
383 releaseSet.insert(Allocation(memory, alloc));
384 }
385 }
386
387
388 //
389 // Run through the accumulated deferred allocations and release them.
390 // This is done automatically on every pass through the server loop;
391 // it must be called by subclasses that implement their loop in some
392 // other way.
393 // @@@X Needs to be thread local
394 //
395 void MachServer::releaseDeferredAllocations()
396 {
397 set<Allocation> &releaseSet = perThread().deferredAllocations;
398 for (set<Allocation>::iterator it = releaseSet.begin(); it != releaseSet.end(); it++) {
399 secinfo("machserver", "releasing alloc at %p with %p", it->addr, it->allocator);
400
401 // before we release the deferred allocation, zap it so that secrets aren't left in memory
402 size_t memSize = malloc_size(it->addr);
403 bzero(it->addr, memSize);
404 it->allocator->free(it->addr);
405 }
406 releaseSet.erase(releaseSet.begin(), releaseSet.end());
407 }
408
409
410 //
411 // The handler function calls this if it realizes that it might be blocked
412 // (or doing something that takes a long time). We respond by ensuring that
413 // at least one more thread is ready to serve requests.
414 // Calls the threadLimitReached callback in the server object if the thread
415 // limit has been exceeded and a needed new thread was not created.
416 //
417 void MachServer::longTermActivity()
418 {
419 if (!useFloatingThread) {
420 StLock<Mutex> _(managerLock);
421 ensureReadyThread();
422 }
423 }
424
425 void MachServer::busy()
426 {
427 StLock<Mutex> _(managerLock);
428 idleCount--;
429 if (useFloatingThread)
430 ensureReadyThread();
431 }
432
433 void MachServer::idle()
434 {
435 StLock<Mutex> _(managerLock);
436 idleCount++;
437 }
438
439
440 void MachServer::ensureReadyThread()
441 {
442 if (idleCount == 0) {
443 if (workerCount >= maxWorkerCount) {
444 this->threadLimitReached(workerCount); // call remedial handler
445 }
446 if (workerCount < maxWorkerCount) { // threadLimit() may have raised maxWorkerCount
447 (new LoadThread(*this))->run();
448 }
449 }
450 }
451
452
453 //
454 // The callback hook for our subclasses.
455 // The default does nothing, thereby denying further thread creation.
456 // You could do something like maxThreads(limit+1) here to grant an variance;
457 // or throw an exception to avoid possible deadlocks (this would abort the current
458 // request but not otherwise impact the server's operation).
459 //
460 void MachServer::threadLimitReached(UInt32 limit)
461 {
462 }
463
464
465 //
466 // What our (non-primary) load threads do
467 //
468 void MachServer::LoadThread::action()
469 {
470 //@@@ race condition?! can server exit before helpers thread gets here?
471
472 // register the worker thread and go
473 server.addThread(this);
474 try {
475 secinfo("machserver", "start thread");
476 server.runServerThread(true);
477 secinfo("machserver", "end thread");
478 } catch (...) {
479 // fell out of server loop by error. Let the thread go quietly
480 secinfo("machserver", "end thread (due to error)");
481 }
482 server.removeThread(this);
483 }
484
485
486 //
487 // Thread accounting
488 //
489 void MachServer::addThread(Thread *thread)
490 {
491 StLock<Mutex> _(managerLock);
492 workerCount++;
493 idleCount++;
494 workers.insert(thread);
495 }
496
497 void MachServer::removeThread(Thread *thread)
498 {
499 StLock<Mutex> _(managerLock);
500 workerCount--;
501 idleCount--;
502 workers.erase(thread);
503 }
504
505
506 //
507 // Timer management
508 //
509 MachServer::Timer::~Timer()
510 { }
511
512 void MachServer::Timer::select()
513 { }
514
515 void MachServer::Timer::unselect()
516 { }
517
518 bool MachServer::processTimer()
519 {
520 Timer *top;
521 { StLock<Mutex> _(managerLock); // could have multiple threads trying this
522 if (!(top = static_cast<Timer *>(timers.pop(Time::now()))))
523 return false; // nothing (more) to be done now
524 } // drop lock; work has been retrieved
525 try {
526 secinfo("machserver", "timer start: %p, %d, %f", top, top->longTerm(), Time::now().internalForm());
527 StLock<MachServer::Timer,
528 &MachServer::Timer::select, &MachServer::Timer::unselect> _t(*top);
529 if (top->longTerm()) {
530 StLock<MachServer, &MachServer::busy, &MachServer::idle> _(*this);
531 top->action();
532 } else {
533 top->action();
534 }
535 secinfo("machserver", "timer end (false)");
536 } catch (...) {
537 secinfo("machserver", "timer end (true)");
538 }
539 return true;
540 }
541
542 void MachServer::setTimer(Timer *timer, Time::Absolute when)
543 {
544 StLock<Mutex> _(managerLock);
545 timers.schedule(timer, when);
546 }
547
548 void MachServer::clearTimer(Timer *timer)
549 {
550 StLock<Mutex> _(managerLock);
551 if (timer->scheduled())
552 timers.unschedule(timer);
553 }
554
555
556 //
557 // Notification hooks and shims. Defaults do nothing.
558 //
559 kern_return_t cdsa_mach_notify_dead_name(mach_port_t, mach_port_name_t port)
560 {
561 try {
562 MachServer::active().notifyDeadName(port);
563 } catch (...) {
564 }
565 return KERN_SUCCESS;
566 }
567
568 void MachServer::notifyDeadName(Port) { }
569
570 kern_return_t cdsa_mach_notify_port_deleted(mach_port_t, mach_port_name_t port)
571 {
572 try {
573 MachServer::active().notifyPortDeleted(port);
574 } catch (...) {
575 }
576 return KERN_SUCCESS;
577 }
578
579 void MachServer::notifyPortDeleted(Port) { }
580
581 kern_return_t cdsa_mach_notify_port_destroyed(mach_port_t, mach_port_name_t port)
582 {
583 try {
584 MachServer::active().notifyPortDestroyed(port);
585 } catch (...) {
586 }
587 return KERN_SUCCESS;
588 }
589
590 void MachServer::notifyPortDestroyed(Port) { }
591
592 kern_return_t cdsa_mach_notify_send_once(mach_port_t port)
593 {
594 try {
595 MachServer::active().notifySendOnce(port);
596 } catch (...) {
597 }
598 return KERN_SUCCESS;
599 }
600
601 void MachServer::notifySendOnce(Port) { }
602
603 kern_return_t cdsa_mach_notify_no_senders(mach_port_t port, mach_port_mscount_t count)
604 {
605 try {
606 MachServer::active().notifyNoSenders(port, count);
607 } catch (...) {
608 }
609 return KERN_SUCCESS;
610 }
611
612 void MachServer::notifyNoSenders(Port, mach_port_mscount_t) { }
613
614 void MachServer::eventDone() { }
615
616
617 } // end namespace MachPlusPlus
618
619 } // end namespace Security