// Copyright 2020 The BoringSSL Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #if !defined(_GNU_SOURCE) #define _GNU_SOURCE // needed for madvise() and MAP_ANONYMOUS on Linux. #endif #include "../bcm_support.h" #include "../internal.h" #include "internal.h" #if defined(OPENSSL_FORK_DETECTION_MADVISE) #include #include #include #include #if defined(MADV_WIPEONFORK) static_assert(MADV_WIPEONFORK == 18, "MADV_WIPEONFORK is not 18"); #else #define MADV_WIPEONFORK 18 #endif #elif defined(OPENSSL_FORK_DETECTION_PTHREAD_ATFORK) #include #include #include #endif // OPENSSL_FORK_DETECTION_PTHREAD_ATFORK #if defined(OPENSSL_FORK_DETECTION_MADVISE) static int g_force_madv_wipeonfork; static int g_force_madv_wipeonfork_enabled; static CRYPTO_once_t g_fork_detect_once = CRYPTO_ONCE_INIT; static CRYPTO_MUTEX g_fork_detect_lock = CRYPTO_MUTEX_INIT; static CRYPTO_atomic_u32 *g_fork_detect_addr; static uint64_t g_fork_generation; static void init_fork_detect(void) { if (g_force_madv_wipeonfork) { return; } long page_size = sysconf(_SC_PAGESIZE); if (page_size <= 0) { return; } void *addr = mmap(NULL, (size_t)page_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (addr == MAP_FAILED) { return; } // Some versions of qemu (up to at least 5.0.0-rc4, see linux-user/syscall.c) // ignore |madvise| calls and just return zero (i.e. success). But we need to // know whether MADV_WIPEONFORK actually took effect. Therefore try an invalid // call to check that the implementation of |madvise| is actually rejecting // unknown |advice| values. if (madvise(addr, (size_t)page_size, -1) == 0 || madvise(addr, (size_t)page_size, MADV_WIPEONFORK) != 0) { munmap(addr, (size_t)page_size); return; } CRYPTO_atomic_u32 *const atomic = reinterpret_cast(addr); CRYPTO_atomic_store_u32(atomic, 1); g_fork_detect_addr = atomic; g_fork_generation = 1; } uint64_t CRYPTO_get_fork_generation(void) { CRYPTO_once(&g_fork_detect_once, init_fork_detect); // In a single-threaded process, there are obviously no races because there's // only a single mutator in the address space. // // In a multi-threaded environment, |CRYPTO_once| ensures that the flag byte // is initialised atomically, even if multiple threads enter this function // concurrently. // // Additionally, while the kernel will only clear WIPEONFORK at a point when a // child process is single-threaded, the child may become multi-threaded // before it observes this. Therefore, we must synchronize the logic below. CRYPTO_atomic_u32 *const flag_ptr = g_fork_detect_addr; if (flag_ptr == NULL) { // Our kernel is too old to support |MADV_WIPEONFORK| or // |g_force_madv_wipeonfork| is set. if (g_force_madv_wipeonfork && g_force_madv_wipeonfork_enabled) { // A constant generation number to simulate support, even if the kernel // doesn't support it. return 42; } // With Linux and clone(), we do not believe that pthread_atfork() is // sufficient for detecting all forms of address space duplication. At this // point we have a kernel that does not support MADV_WIPEONFORK. We could // return the generation number from pthread_atfork() here and it would // probably be safe in almost any situation, but to ensure safety we return // 0 and force an entropy draw on every call. return 0; } // In the common case, try to observe the flag without taking a lock. This // avoids cacheline contention in the PRNG. uint64_t *const generation_ptr = &g_fork_generation; if (CRYPTO_atomic_load_u32(flag_ptr) != 0) { // If we observe a non-zero flag, it is safe to read |generation_ptr| // without a lock. The flag and generation number are fixed for this copy of // the address space. return *generation_ptr; } // The flag was zero. The generation number must be incremented, but other // threads may have concurrently observed the zero, so take a lock before // incrementing. CRYPTO_MUTEX *const lock = &g_fork_detect_lock; CRYPTO_MUTEX_lock_write(lock); uint64_t current_generation = *generation_ptr; if (CRYPTO_atomic_load_u32(flag_ptr) == 0) { // A fork has occurred. current_generation++; if (current_generation == 0) { // Zero means fork detection isn't supported, so skip that value. current_generation = 1; } // We must update |generation_ptr| before |flag_ptr|. Other threads may // observe |flag_ptr| without taking a lock. *generation_ptr = current_generation; CRYPTO_atomic_store_u32(flag_ptr, 1); } CRYPTO_MUTEX_unlock_write(lock); return current_generation; } void CRYPTO_fork_detect_force_madv_wipeonfork_for_testing(int on) { g_force_madv_wipeonfork = 1; g_force_madv_wipeonfork_enabled = on; } #elif defined(OPENSSL_FORK_DETECTION_PTHREAD_ATFORK) static CRYPTO_once_t g_pthread_fork_detection_once = CRYPTO_ONCE_INIT; static uint64_t g_atfork_fork_generation; static void we_are_forked(void) { // Immediately after a fork, the process must be single-threaded. uint64_t value = g_atfork_fork_generation + 1; if (value == 0) { value = 1; } g_atfork_fork_generation = value; } static void init_pthread_fork_detection(void) { if (pthread_atfork(NULL, NULL, we_are_forked) != 0) { abort(); } g_atfork_fork_generation = 1; } uint64_t CRYPTO_get_fork_generation(void) { CRYPTO_once(&g_pthread_fork_detection_once, init_pthread_fork_detection); return g_atfork_fork_generation; } #elif defined(OPENSSL_DOES_NOT_FORK) // These platforms are guaranteed not to fork, and therefore do not require // fork detection support. Returning a constant non zero value makes BoringSSL // assume address space duplication is not a concern and adding entropy to // every RAND_bytes call is not needed. uint64_t CRYPTO_get_fork_generation(void) { return 0xc0ffee; } #else // These platforms may fork, but we do not have a mitigation mechanism in // place. Returning a constant zero value makes BoringSSL assume that address // space duplication could have occured on any call entropy must be added to // every RAND_bytes call. uint64_t CRYPTO_get_fork_generation(void) { return 0; } #endif