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