| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601 | /* * Copyright 2019-2023 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the Apache License 2.0 (the "License").  You may not use * this file except in compliance with the License.  You can obtain a copy * in the file LICENSE in the source distribution or at * https://www.openssl.org/source/license.html */#include <internal/thread_arch.h>#if defined(OPENSSL_THREADS_WINNT)# include <process.h># include <windows.h>static unsigned __stdcall thread_start_thunk(LPVOID vthread){    CRYPTO_THREAD *thread;    CRYPTO_THREAD_RETVAL ret;    thread = (CRYPTO_THREAD *)vthread;    thread->thread_id = GetCurrentThreadId();    ret = thread->routine(thread->data);    ossl_crypto_mutex_lock(thread->statelock);    CRYPTO_THREAD_SET_STATE(thread, CRYPTO_THREAD_FINISHED);    thread->retval = ret;    ossl_crypto_condvar_signal(thread->condvar);    ossl_crypto_mutex_unlock(thread->statelock);    return 0;}int ossl_crypto_thread_native_spawn(CRYPTO_THREAD *thread){    HANDLE *handle;    handle = OPENSSL_zalloc(sizeof(*handle));    if (handle == NULL)        goto fail;    *handle = (HANDLE)_beginthreadex(NULL, 0, &thread_start_thunk, thread, 0, NULL);    if (*handle == NULL)        goto fail;    thread->handle = handle;    return 1;fail:    thread->handle = NULL;    OPENSSL_free(handle);    return 0;}int ossl_crypto_thread_native_perform_join(CRYPTO_THREAD *thread, CRYPTO_THREAD_RETVAL *retval){    DWORD thread_retval;    HANDLE *handle;    if (thread == NULL || thread->handle == NULL)        return 0;    handle = (HANDLE *) thread->handle;    if (WaitForSingleObject(*handle, INFINITE) != WAIT_OBJECT_0)        return 0;    if (GetExitCodeThread(*handle, &thread_retval) == 0)        return 0;    /*     * GetExitCodeThread call followed by this check is to make sure that     * the thread exited properly. In particular, thread_retval may be     * non-zero when exited via explicit ExitThread/TerminateThread or     * if the thread is still active (returns STILL_ACTIVE (259)).     */    if (thread_retval != 0)        return 0;    if (CloseHandle(*handle) == 0)        return 0;    return 1;}int ossl_crypto_thread_native_exit(void){    _endthreadex(0);    return 1;}int ossl_crypto_thread_native_is_self(CRYPTO_THREAD *thread){    return thread->thread_id == GetCurrentThreadId();}CRYPTO_MUTEX *ossl_crypto_mutex_new(void){    CRITICAL_SECTION *mutex;    if ((mutex = OPENSSL_zalloc(sizeof(*mutex))) == NULL)        return NULL;    InitializeCriticalSection(mutex);    return (CRYPTO_MUTEX *)mutex;}void ossl_crypto_mutex_lock(CRYPTO_MUTEX *mutex){    CRITICAL_SECTION *mutex_p;    mutex_p = (CRITICAL_SECTION *)mutex;    EnterCriticalSection(mutex_p);}int ossl_crypto_mutex_try_lock(CRYPTO_MUTEX *mutex){    CRITICAL_SECTION *mutex_p;    mutex_p = (CRITICAL_SECTION *)mutex;    if (TryEnterCriticalSection(mutex_p))        return 1;    return 0;}void ossl_crypto_mutex_unlock(CRYPTO_MUTEX *mutex){    CRITICAL_SECTION *mutex_p;    mutex_p = (CRITICAL_SECTION *)mutex;    LeaveCriticalSection(mutex_p);}void ossl_crypto_mutex_free(CRYPTO_MUTEX **mutex){    CRITICAL_SECTION **mutex_p;    mutex_p = (CRITICAL_SECTION **)mutex;    if (*mutex_p != NULL)        DeleteCriticalSection(*mutex_p);    OPENSSL_free(*mutex_p);    *mutex = NULL;}static int determine_timeout(OSSL_TIME deadline, DWORD *w_timeout_p){    OSSL_TIME now, delta;    uint64_t ms;    if (ossl_time_is_infinite(deadline)) {        *w_timeout_p = INFINITE;        return 1;    }    now = ossl_time_now();    delta = ossl_time_subtract(deadline, now);    if (ossl_time_is_zero(delta))        return 0;    ms = ossl_time2ms(delta);    /*     * Amount of time we want to wait is too long for the 32-bit argument to     * the Win32 API, so just wait as long as possible.     */    if (ms > (uint64_t)(INFINITE - 1))        *w_timeout_p = INFINITE - 1;    else        *w_timeout_p = (DWORD)ms;    return 1;}# if defined(OPENSSL_THREADS_WINNT_LEGACY)#  include <assert.h>/* * Win32, before Vista, did not have an OS-provided condition variable * construct. This leads to the need to construct our own condition variable * construct in order to support Windows XP. * * It is difficult to construct a condition variable construct using the * OS-provided primitives in a way that is both correct (avoiding race * conditions where broadcasts get lost) and fair. * * CORRECTNESS: *   A blocked thread is a thread which is calling wait(), between the *   precise instants at which the external mutex passed to wait() is *   unlocked and the instant at which it is relocked. * *   a) *     - If broadcast() is called, ALL blocked threads MUST be unblocked. *     - If signal() is called, at least one blocked thread MUST be unblocked. * *     (i.e.: a signal or broadcast must never get 'lost') * *   b) *     - If broadcast() or signal() is called, this must not cause a thread *       which is not blocked to return immediately from a subsequent *       call to wait(). * * FAIRNESS: *   If broadcast() is called at time T1, all blocked threads must be unblocked *   before any thread which subsequently calls wait() at time T2 > T1 is *   unblocked. * *   An example of an implementation which lacks fairness is as follows: * *     t1 enters wait() *     t2 enters wait() * *     tZ calls broadcast() * *     t1 exits wait() *     t1 enters wait() * *     tZ calls broadcast() * *     t1 exits wait() * * IMPLEMENTATION: * *   The most suitable primitives available to us in Windows XP are semaphores, *   auto-reset events and manual-reset events. A solution based on semaphores *   is chosen. * *   PROBLEM. Designing a solution based on semaphores is non-trivial because, *   while it is easy to track the number of waiters in an interlocked data *   structure and then add that number to the semaphore, this does not *   guarantee fairness or correctness. Consider the following situation: * *     - t1 enters wait(), adding 1 to the wait counter & blocks on the semaphore *     - t2 enters wait(), adding 1 to the wait counter & blocks on the semaphore *     - tZ calls broadcast(), finds the wait counter is 2, adds 2 to the semaphore * *     - t1 exits wait() *     - t1 immediately reenters wait() and blocks on the semaphore *     - The semaphore is still positive due to also having been signalled *       for t2, therefore it is decremented *     - t1 exits wait() immediately; t2 is never woken * *   GENERATION COUNTERS. One naive solution to this is to use a generation *   counter. Each broadcast() invocation increments a generation counter. If *   the generation counter has not changed during a semaphore wait operation *   inside wait(), this indicates that no broadcast() call has been made in *   the meantime; therefore, the successful semaphore decrement must have *   'stolen' a wakeup from another thread which was waiting to wakeup from the *   prior broadcast() call but which had not yet had a chance to do so. The *   semaphore can then be reincremented and the wait() operation repeated. * *   However, this suffers from the obvious problem that without OS guarantees *   as to how semaphore readiness events are distributed amongst threads, *   there is no particular guarantee that the semaphore readiness event will *   not be immediately redistributed back to the same thread t1. * *   SOLUTION. A solution is chosen as follows. In its initial state, a *   condition variable can accept waiters, who wait for the semaphore *   normally. However, once broadcast() is called, the condition *   variable becomes 'closed'. Any existing blocked threads are unblocked, *   but any new calls to wait() will instead enter a blocking pre-wait stage. *   Pre-wait threads are not considered to be waiting (and the external *   mutex remains held). A call to wait() in pre-wait cannot progress *   to waiting until all threads due to be unblocked by the prior broadcast() *   call have returned and had a chance to execute. * *   This pre-wait does not affect a thread if it does not call wait() *   again until after all threads have had a chance to execute. * *   RESOURCE USAGE. Aside from an allocation for the condition variable *   structure, this solution uses two Win32 semaphores. * * FUTURE OPTIMISATIONS: * *   An optimised multi-generation implementation is possible at the cost of *   higher Win32 resource usage. Multiple 'buckets' could be defined, with *   usage rotating between buckets internally as buckets become closed. *   This would avoid the need for the prewait in more cases, depending *   on intensity of usage. * */typedef struct legacy_condvar_st {    CRYPTO_MUTEX    *int_m;       /* internal mutex */    HANDLE          sema;         /* main wait semaphore */    HANDLE          prewait_sema; /* prewait semaphore */    /*     * All of the following fields are protected by int_m.     *     * num_wake only ever increases by virtue of a corresponding decrease in     * num_wait. num_wait can decrease for other reasons (for example due to a     * wait operation timing out).     */    size_t          num_wait;     /* Num. threads currently blocked */    size_t          num_wake;     /* Num. threads due to wake up */    size_t          num_prewait;  /* Num. threads in prewait */    size_t          gen;          /* Prewait generation */    int             closed;       /* Is closed? */} LEGACY_CONDVAR;CRYPTO_CONDVAR *ossl_crypto_condvar_new(void){    LEGACY_CONDVAR *cv;    if ((cv = OPENSSL_malloc(sizeof(LEGACY_CONDVAR))) == NULL)        return NULL;    if ((cv->int_m = ossl_crypto_mutex_new()) == NULL) {        OPENSSL_free(cv);        return NULL;    }    if ((cv->sema = CreateSemaphoreA(NULL, 0, LONG_MAX, NULL)) == NULL) {        ossl_crypto_mutex_free(&cv->int_m);        OPENSSL_free(cv);        return NULL;    }    if ((cv->prewait_sema = CreateSemaphoreA(NULL, 0, LONG_MAX, NULL)) == NULL) {        CloseHandle(cv->sema);        ossl_crypto_mutex_free(&cv->int_m);        OPENSSL_free(cv);        return NULL;    }    cv->num_wait      = 0;    cv->num_wake      = 0;    cv->num_prewait   = 0;    cv->closed        = 0;    return (CRYPTO_CONDVAR *)cv;}void ossl_crypto_condvar_free(CRYPTO_CONDVAR **cv_p){    if (*cv_p != NULL) {        LEGACY_CONDVAR *cv = *(LEGACY_CONDVAR **)cv_p;        CloseHandle(cv->sema);        CloseHandle(cv->prewait_sema);        ossl_crypto_mutex_free(&cv->int_m);        OPENSSL_free(cv);    }    *cv_p = NULL;}static uint32_t obj_wait(HANDLE h, OSSL_TIME deadline){    DWORD timeout;    if (!determine_timeout(deadline, &timeout))        timeout = 1;    return WaitForSingleObject(h, timeout);}void ossl_crypto_condvar_wait_timeout(CRYPTO_CONDVAR *cv_, CRYPTO_MUTEX *ext_m,                                      OSSL_TIME deadline){    LEGACY_CONDVAR *cv = (LEGACY_CONDVAR *)cv_;    int closed, set_prewait = 0, have_orig_gen = 0;    uint32_t rc;    size_t orig_gen;    /* Admission control - prewait until we can enter our actual wait phase. */    do {        ossl_crypto_mutex_lock(cv->int_m);        closed = cv->closed;        /*         * Once prewait is over the prewait semaphore is signalled and         * num_prewait is set to 0. Use a generation counter to track if we need         * to remove a value we added to num_prewait when exiting (e.g. due to         * timeout or failure of WaitForSingleObject).         */        if (!have_orig_gen) {            orig_gen = cv->gen;            have_orig_gen = 1;        } else if (cv->gen != orig_gen) {            set_prewait = 0;            orig_gen = cv->gen;        }        if (!closed) {            /* We can now be admitted. */            ++cv->num_wait;            if (set_prewait) {                --cv->num_prewait;                set_prewait = 0;            }        } else if (!set_prewait) {            ++cv->num_prewait;            set_prewait = 1;        }        ossl_crypto_mutex_unlock(cv->int_m);        if (closed)            if (obj_wait(cv->prewait_sema, deadline) != WAIT_OBJECT_0) {                /*                 * If we got WAIT_OBJECT_0 we are safe - num_prewait has been                 * set to 0 and the semaphore has been consumed. On the other                 * hand if we timed out, there may be a residual posting that                 * was made just after we timed out. However in the worst case                 * this will just cause an internal spurious wakeup here in the                 * future, so we do not care too much about this. We treat                 * failure and timeout cases as the same, and simply exit in                 * this case.                 */                ossl_crypto_mutex_lock(cv->int_m);                if (set_prewait && cv->gen == orig_gen)                    --cv->num_prewait;                ossl_crypto_mutex_unlock(cv->int_m);                return;            }    } while (closed);    /*     * Unlock external mutex. Do not do this until we have been admitted, as we     * must guarantee we wake if broadcast is called at any time after ext_m is     * unlocked.     */    ossl_crypto_mutex_unlock(ext_m);    for (;;) {        /* Wait. */        rc = obj_wait(cv->sema, deadline);        /* Reacquire internal mutex and probe state. */        ossl_crypto_mutex_lock(cv->int_m);        if (cv->num_wake > 0) {            /*             * A wake token is available, so we can wake up. Consume the token             * and get out of here. We don't care what WaitForSingleObject             * returned here (e.g. if it timed out coincidentally). In the             * latter case a signal might be left in the semaphore which causes             * a future WaitForSingleObject call to return immediately, but in             * this case we will just loop again.             */            --cv->num_wake;            if (cv->num_wake == 0 && cv->closed) {                /*                 * We consumed the last wake token, so we can now open the                 * condition variable for new admissions.                 */                cv->closed = 0;                if (cv->num_prewait > 0) {                    ReleaseSemaphore(cv->prewait_sema, (LONG)cv->num_prewait, NULL);                    cv->num_prewait = 0;                    ++cv->gen;                }            }        } else if (rc == WAIT_OBJECT_0) {            /*             * We got a wakeup from the semaphore but we did not have any wake             * tokens. This ideally does not happen, but might if during a             * previous wait() call the semaphore is posted just after             * WaitForSingleObject returns due to a timeout (such that the             * num_wake > 0 case is taken above). Just spin again. (It is worth             * noting that repeated WaitForSingleObject calls is the only method             * documented for decrementing a Win32 semaphore, so this is             * basically the best possible strategy.)             */            ossl_crypto_mutex_unlock(cv->int_m);            continue;        } else {            /*             * Assume we timed out. The WaitForSingleObject call may also have             * failed for some other reason, which we treat as a timeout.             */            assert(cv->num_wait > 0);            --cv->num_wait;        }        break;    }    ossl_crypto_mutex_unlock(cv->int_m);    ossl_crypto_mutex_lock(ext_m);}void ossl_crypto_condvar_wait(CRYPTO_CONDVAR *cv, CRYPTO_MUTEX *ext_m){    ossl_crypto_condvar_wait_timeout(cv, ext_m, ossl_time_infinite());}void ossl_crypto_condvar_broadcast(CRYPTO_CONDVAR *cv_){    LEGACY_CONDVAR *cv = (LEGACY_CONDVAR *)cv_;    size_t num_wake;    ossl_crypto_mutex_lock(cv->int_m);    num_wake = cv->num_wait;    if (num_wake == 0) {        ossl_crypto_mutex_unlock(cv->int_m);        return;    }    cv->num_wake  += num_wake;    cv->num_wait  -= num_wake;    cv->closed     = 1;    ossl_crypto_mutex_unlock(cv->int_m);    ReleaseSemaphore(cv->sema, num_wake, NULL);}void ossl_crypto_condvar_signal(CRYPTO_CONDVAR *cv_){    LEGACY_CONDVAR *cv = (LEGACY_CONDVAR *)cv_;    ossl_crypto_mutex_lock(cv->int_m);    if (cv->num_wait == 0) {        ossl_crypto_mutex_unlock(cv->int_m);        return;    }    /*     * We do not close the condition variable when merely signalling, as there     * are no guaranteed fairness semantics here, unlike for a broadcast.     */    --cv->num_wait;    ++cv->num_wake;    ossl_crypto_mutex_unlock(cv->int_m);    ReleaseSemaphore(cv->sema, 1, NULL);}# elseCRYPTO_CONDVAR *ossl_crypto_condvar_new(void){    CONDITION_VARIABLE *cv_p;    if ((cv_p = OPENSSL_zalloc(sizeof(*cv_p))) == NULL)        return NULL;    InitializeConditionVariable(cv_p);    return (CRYPTO_CONDVAR *)cv_p;}void ossl_crypto_condvar_wait(CRYPTO_CONDVAR *cv, CRYPTO_MUTEX *mutex){    CONDITION_VARIABLE *cv_p;    CRITICAL_SECTION *mutex_p;    cv_p = (CONDITION_VARIABLE *)cv;    mutex_p = (CRITICAL_SECTION *)mutex;    SleepConditionVariableCS(cv_p, mutex_p, INFINITE);}void ossl_crypto_condvar_wait_timeout(CRYPTO_CONDVAR *cv, CRYPTO_MUTEX *mutex,                                      OSSL_TIME deadline){    DWORD timeout;    CONDITION_VARIABLE *cv_p = (CONDITION_VARIABLE *)cv;    CRITICAL_SECTION *mutex_p = (CRITICAL_SECTION *)mutex;    if (!determine_timeout(deadline, &timeout))        timeout = 1;    SleepConditionVariableCS(cv_p, mutex_p, timeout);}void ossl_crypto_condvar_broadcast(CRYPTO_CONDVAR *cv){    CONDITION_VARIABLE *cv_p;    cv_p = (CONDITION_VARIABLE *)cv;    WakeAllConditionVariable(cv_p);}void ossl_crypto_condvar_signal(CRYPTO_CONDVAR *cv){    CONDITION_VARIABLE *cv_p;    cv_p = (CONDITION_VARIABLE *)cv;    WakeConditionVariable(cv_p);}void ossl_crypto_condvar_free(CRYPTO_CONDVAR **cv){    CONDITION_VARIABLE **cv_p;    cv_p = (CONDITION_VARIABLE **)cv;    OPENSSL_free(*cv_p);    *cv_p = NULL;}# endif#ifndef WINSCPvoid ossl_crypto_mem_barrier(void){    MemoryBarrier();}#endif#endif
 |