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