X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/72a12576750f52947eb043106ba5c12c0d07decf..b1ab9ed8d0e0f1c3b66d7daa8fd5564444c56195:/libsecurity_utilities/lib/globalizer.cpp diff --git a/libsecurity_utilities/lib/globalizer.cpp b/libsecurity_utilities/lib/globalizer.cpp new file mode 100644 index 00000000..1ef33e31 --- /dev/null +++ b/libsecurity_utilities/lib/globalizer.cpp @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2000-2004 Apple Computer, Inc. All Rights Reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +// +// globalizer - multiscope globalization services. +// +// This is a tentative, partial implementation. +// Status: +// module scope: constructs, optional cleanup +// thread scope: constructs, optional cleanup +// process scope: not implemented (obsolete implementation, unused) +// system scope: not implemented (probably never will) +// +// @@@ Assumption: {bool,T*} atomic unless PTHREAD_STRICT +// +#include +#include +#include +#include + +// +// The Error class thrown if Nexus operations fail +// +GlobalNexus::Error::~Error() throw() +{ +} + + +// +// The long (and possibly contentious) path of ModuleNexus() +// +// Briefly, the trick here is to go through a three-stage sequence +// to lazily construct a unique singleton object, no matter how many +// threads all of a sudden decide they need it. +// State sequence: +// State 0: pointer == 0, not initialized, idle +// State 1: pointer == mutexp | 0x1, where mutexp points to a Mutex +// used to serialize construction of the singleton object +// State 2: pointer == &singleton, and we're done +// +// TAKE NOTE: +// This code is optimized with a particular issue in mind: when placed +// into static storage (as ModuleNexi are wont to), it should not require +// dynamic initialization. This is important because our code is, in effect, +// linked into just about every program in the system. The price we pay +// for this coolness is +// (a) This won't work *except* in static storage (not on stack or heap) +// (b) We slightly fracture portability (see below) +// This has been considered Worth It, at least for now. Before you throw +// up and throw this code out, please try to figure out whether you know +// the Whole Story. Thank you. +// +// WARNING: +// This code makes the following non-portable assumptions: +// (a) NULL == 0 (binary representation of NULL pointer is zero value) +// (b) Pointers acquired from new have at least their LSB zero (are at +// least two-byte aligned). +// It seems like it's been a while since anyone made a machine/runtime that +// violated either of those. But you have been warned. +// +void *ModuleNexusCommon::create(void *(*make)()) +{ + sync++; // keep mutex alive if needed + retry: + void *initialPointer = Atomic::load(pointer); // latch pointer + if (!initialPointer || (uintptr_t(initialPointer) & 0x1)) { + Mutex *mutex; + if (initialPointer == 0) { + mutex = new Mutex; + mutex->lock(); + if (!Atomic::casb(0, (void *)(uintptr_t(mutex) | 0x1), pointer)) { + // somebody beat us to the lead - back off + mutex->unlock(); + delete mutex; + goto retry; + } + // we have the ball + try { + void *singleton = make(); + pointer = singleton; + // we need a write barrier here, but the mutex->unlock below provides it for free + } catch (...) { + secdebug("nexus", "ModuleNexus %p construction failed", this); + mutex->unlock(); + if (--sync == 0) { + delete mutex; + pointer = 0; + } + throw; + } + } else { + mutex = reinterpret_cast(uintptr_t(initialPointer) & ~0x1); + mutex->lock(); // we'll wait here + } + mutex->unlock(); + //@@@ retry if not resolved -- or fail here (with "object can't be built") + if (--sync == 0) + delete mutex; + } + return pointer; +} + + +// thread nexus static globals +ModuleNexus ThreadNexusBase::mInstanceLock; + +// Thread nexus globals +ModuleNexus ThreadNexusBase::mInstances; + +// +// Process nexus operation +// +ProcessNexusBase::ProcessNexusBase(const char *identifier) +{ + const char *env = getenv(identifier); + if (env == NULL) { // perhaps we're first... + auto_ptr store(new Store); + char form[2*sizeof(Store *) + 2]; + sprintf(form, "*%p", &store); + setenv(identifier, form, 0); // do NOT overwrite... + env = getenv(identifier); // ... and refetch to resolve races + if (sscanf(env, "*%p", &mStore) != 1) + throw std::runtime_error("environment communication failed"); + if (mStore == store.get()) // we won the race... + store.release(); // ... so keep the store + } else + if (sscanf(env, "*%p", &mStore) != 1) + throw std::runtime_error("environment communication failed"); +}