2  * Copyright (c) 2000-2007,2011-2013 Apple Inc. All Rights Reserved. 
   4  * @APPLE_LICENSE_HEADER_START@ 
   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 
  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. 
  21  * @APPLE_LICENSE_HEADER_END@ 
  26 // machserver - C++ shell for writing Mach 3 servers 
  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> 
  37 #if defined(USECFCURRENTTIME) 
  38 # include <CoreFoundation/CFDate.h> 
  40 # include <sys/time.h> 
  43 #define SEC_MACH_AUDIT_TOKEN_PID (5) 
  46 namespace MachPlusPlus 
{ 
  50 // Global per-thread information 
  52 ModuleNexus
< ThreadNexus
<MachServer::PerThread
> > MachServer::thread
; 
  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. 
  60 MachServer::MachServer() 
  61 { setup("(anonymous)"); } 
  63 MachServer::MachServer(const char *name
) 
  64         : mServerPort(name
, bootstrap
) 
  67 MachServer::MachServer(const char *name
, const Bootstrap 
&boot
) 
  68         : bootstrap(boot
), mServerPort(name
, bootstrap
) 
  71 void MachServer::setup(const char *name
) 
  73         workerTimeout 
= 60 * 2; // 2 minutes default timeout 
  74         maxWorkerCount 
= 100;   // sanity check limit 
  75         useFloatingThread 
= false; // tight thread management 
  77     mPortSet 
+= mServerPort
; 
  80 MachServer::~MachServer() 
  82         // The ReceivePort members will clean themselves up. 
  83         // The bootstrap server will clear us from its map when our receive port dies. 
  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. 
  92 void MachServer::add(Port receiver
) 
  94     secinfo("machserver", "port add: %d", receiver
.port()); 
  98 void MachServer::remove(Port receiver
) 
 100     secinfo("machserver", "port remove: %d", receiver
.port()); 
 101         mPortSet 
-= receiver
; 
 106 // Register for mach port notifications 
 108 void MachServer::notifyIfDead(Port port
, bool doNotify
) const 
 111                 port
.requestNotify(mServerPort
); 
 116 void MachServer::notifyIfUnused(Port port
, bool doNotify
) const 
 119                 port
.requestNotify(port
, MACH_NOTIFY_NO_SENDERS
, true); 
 121                 port
.cancelNotify(MACH_NOTIFY_NO_SENDERS
); 
 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 
 132 // @@@ Msg-errors in additional threads are not acted upon. 
 134 void MachServer::run(mach_msg_size_t maxSize
, mach_msg_options_t options
) 
 136         // establish server-global (thread-shared) parameters 
 138         mMsgOptions 
= options
; 
 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; 
 147         // run server loop in initial (immortal) thread 
 148     secinfo("machserver", "start thread"); 
 149         runServerThread(false); 
 150     secinfo("machserver", "end thread"); 
 152         // primary server thread exited somehow (not currently possible) 
 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. 
 164 extern "C" boolean_t 
cdsa_notify_server(mach_msg_header_t 
*in
, mach_msg_header_t 
*out
); 
 166 void MachServer::runServerThread(bool doTimeout
) 
 168         // allocate request/reply buffers 
 169     Message 
bufRequest(mMaxSize
); 
 170     Message 
bufReply(mMaxSize
); 
 172         // all exits from runServerThread are through exceptions 
 174                 // register as a worker thread 
 175                 perThread().server 
= this; 
 181                         // process all pending timers 
 182                         while (processTimer()) {} 
 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
; 
 190                                 // perform self-timeout processing 
 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 
 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
; 
 213                                         timeout 
= max(Time::Interval(0), timers
.next() - Time::now()); 
 214                                         if (doTimeout 
&& workerTimeout 
< timeout
) 
 215                                                 timeout 
= workerTimeout
; 
 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) 
 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); 
 234                         case MACH_MSG_SUCCESS
: 
 235                                 // process received request message below 
 238                 secinfo("machserver", "received error: %d", mr
); 
 242                         // reset the buffer each time, handlers don't consistently set out params 
 243                         bufReply
.clearBuffer(); 
 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"); 
 255                                 cdsa_notify_server(bufRequest
, bufReply
); 
 257                                 // normal request message 
 258                                 StLock
<MachServer
, &MachServer::busy
, &MachServer::idle
> _(*this); 
 259                 secinfo("machserver", "begin request: %d, %d", bufRequest
.localPort().port(), bufRequest
.msgId()); 
 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
); 
 270                                         // unclaimed, send to main handler 
 271                     handle(bufRequest
, bufReply
); 
 274                 secinfo("machserver", "end request"); 
 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
) 
 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
); 
 287             if (bufReply
.remotePort() == MACH_PORT_NULL
) { 
 288                 // no reply port, so destroy the reply 
 289                 if (bufReply
.bits() & MACH_MSGH_BITS_COMPLEX
) 
 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. 
 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); 
 311                         case MACH_MSG_SUCCESS
: 
 314                 secinfo("machserver", "send error: %d %d", mr
, bufReply
.remotePort().port()); 
 320             // clean up after the transaction 
 321             releaseDeferredAllocations(); 
 323                 perThread().server 
= NULL
; 
 326                 perThread().server 
= NULL
; 
 333 // Manage subsidiary port handlers 
 335 void MachServer::add(Handler 
&handler
) 
 337     assert(mHandlers
.find(&handler
) == mHandlers
.end()); 
 338     assert(handler
.port() != MACH_PORT_NULL
); 
 339     mHandlers
.insert(&handler
); 
 340     mPortSet 
+= handler
.port(); 
 343 void MachServer::remove(Handler 
&handler
) 
 345     assert(mHandlers
.find(&handler
) != mHandlers
.end()); 
 346     mHandlers
.erase(&handler
); 
 347     mPortSet 
-= handler
.port(); 
 352 // Abstract auxiliary message handlers 
 354 MachServer::Handler::~Handler() 
 359 // Implement a Handler that sends no reply 
 361 boolean_t 
MachServer::NoReplyHandler::handle(mach_msg_header_t 
*in
, mach_msg_header_t 
*out
) 
 363     // set up reply message to be valid (enough) and read "do not send reply" 
 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
; 
 369     // call input-only handler 
 375 // Register a memory block for deferred release. 
 377 void MachServer::releaseWhenDone(Allocator 
&alloc
, void *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
)); 
 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 
 393 // @@@X Needs to be thread local 
 395 void MachServer::releaseDeferredAllocations() 
 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
); 
 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
); 
 406         releaseSet
.erase(releaseSet
.begin(), releaseSet
.end()); 
 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. 
 417 void MachServer::longTermActivity() 
 419         if (!useFloatingThread
) { 
 420                 StLock
<Mutex
> _(managerLock
); 
 425 void MachServer::busy() 
 427         StLock
<Mutex
> _(managerLock
); 
 429         if (useFloatingThread
) 
 433 void MachServer::idle() 
 435         StLock
<Mutex
> _(managerLock
); 
 440 void MachServer::ensureReadyThread() 
 442         if (idleCount 
== 0) { 
 443                 if (workerCount 
>= maxWorkerCount
) { 
 444                         this->threadLimitReached(workerCount
);  // call remedial handler 
 446                 if (workerCount 
< maxWorkerCount
) { // threadLimit() may have raised maxWorkerCount 
 447                         (new LoadThread(*this))->run(); 
 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). 
 460 void MachServer::threadLimitReached(UInt32 limit
) 
 466 // What our (non-primary) load threads do 
 468 void MachServer::LoadThread::action() 
 470         //@@@ race condition?! can server exit before helpers thread gets here? 
 472         // register the worker thread and go 
 473         server
.addThread(this); 
 475         secinfo("machserver", "start thread"); 
 476                 server
.runServerThread(true); 
 477         secinfo("machserver", "end thread"); 
 479                 // fell out of server loop by error. Let the thread go quietly 
 480         secinfo("machserver", "end thread (due to error)"); 
 482         server
.removeThread(this); 
 489 void MachServer::addThread(Thread 
*thread
) 
 491         StLock
<Mutex
> _(managerLock
); 
 494         workers
.insert(thread
); 
 497 void MachServer::removeThread(Thread 
*thread
) 
 499         StLock
<Mutex
> _(managerLock
); 
 502         workers
.erase(thread
); 
 509 MachServer::Timer::~Timer() 
 512 void MachServer::Timer::select() 
 515 void MachServer::Timer::unselect() 
 518 bool MachServer::processTimer() 
 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 
 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); 
 535         secinfo("machserver", "timer end (false)"); 
 537         secinfo("machserver", "timer end (true)"); 
 542 void MachServer::setTimer(Timer 
*timer
, Time::Absolute when
) 
 544         StLock
<Mutex
> _(managerLock
); 
 545         timers
.schedule(timer
, when
);  
 548 void MachServer::clearTimer(Timer 
*timer
) 
 550         StLock
<Mutex
> _(managerLock
);  
 551         if (timer
->scheduled()) 
 552                 timers
.unschedule(timer
);  
 557 // Notification hooks and shims. Defaults do nothing. 
 559 kern_return_t 
cdsa_mach_notify_dead_name(mach_port_t
, mach_port_name_t port
) 
 562                 MachServer::active().notifyDeadName(port
); 
 565     // the act of receiving a dead name notification allocates a dead-name 
 566     // right that must be deallocated 
 567     mach_port_deallocate(mach_task_self(), port
); 
 571 void MachServer::notifyDeadName(Port
) { } 
 573 kern_return_t 
cdsa_mach_notify_port_deleted(mach_port_t
, mach_port_name_t port
) 
 576                 MachServer::active().notifyPortDeleted(port
); 
 582 void MachServer::notifyPortDeleted(Port
) { } 
 584 kern_return_t 
cdsa_mach_notify_port_destroyed(mach_port_t
, mach_port_name_t port
) 
 587                 MachServer::active().notifyPortDestroyed(port
); 
 593 void MachServer::notifyPortDestroyed(Port
) { } 
 595 kern_return_t 
cdsa_mach_notify_send_once(mach_port_t port
) 
 598                 MachServer::active().notifySendOnce(port
); 
 604 void MachServer::notifySendOnce(Port
) { } 
 606 kern_return_t 
cdsa_mach_notify_no_senders(mach_port_t port
, mach_port_mscount_t count
) 
 609                 MachServer::active().notifyNoSenders(port
, count
); 
 615 void MachServer::notifyNoSenders(Port
, mach_port_mscount_t
) { } 
 617 void MachServer::eventDone() { } 
 620 } // end namespace MachPlusPlus 
 622 } // end namespace Security