Add CRYPTO_pre_sandbox_init.

The intent is to replace the logic in [0] and allows Chromium to set up
the MADV_WIPEONFORK page without increasing sandbox syscall surface.
From there we can remove RAND_set_urandom_fd and trim a bit of
complexity from the PRNG logic.

[0] https://source.chromium.org/chromium/chromium/src/+/master:content/app/content_main_runner_impl.cc;l=333-341;drc=975850fa57e140ec696114477e9416a19f06d29f

Change-Id: I9b679e15da551a10302389556c6c77d192be662a
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/41326
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/README.md b/README.md
index 5d116c0..2a99b60 100644
--- a/README.md
+++ b/README.md
@@ -39,3 +39,4 @@
   * [FUZZING.md](/FUZZING.md): information about fuzzing BoringSSL.
   * [CONTRIBUTING.md](/CONTRIBUTING.md): how to contribute to BoringSSL.
   * [BREAKING-CHANGES.md](/BREAKING-CHANGES.md): notes on potentially-breaking changes.
+  * [SANDBOXING.md](/SANDBOXING.md): notes on using BoringSSL in a sandboxed environment.
diff --git a/SANDBOXING.md b/SANDBOXING.md
new file mode 100644
index 0000000..95ac6e8
--- /dev/null
+++ b/SANDBOXING.md
@@ -0,0 +1,133 @@
+# Using BoringSSL in a Sandbox
+
+Sandboxes are a valuable tool for securing applications, so BoringSSL aims to
+support them. However, it is difficult to make concrete API guarantees with
+sandboxes. Sandboxes remove low-level OS resources and system calls, which
+breaks platform abstractions. A syscall-filtering sandbox may, for instance, be
+sensitive to otherwise non-breaking changes to use newer syscalls
+in either BoringSSL or the C library.
+
+Some functions in BoringSSL, such as `BIO_new_file`, inherently need OS
+resources like the filesystem. We assume that sandboxed consumers either avoid
+those functions or make necessary resources available. Other functions like
+`RSA_sign` are purely computational, but still have some baseline OS
+dependencies.
+
+Sandboxes which drop privileges partway through a process's lifetime are
+additionally sensitive to OS resources retained across the transitions. For
+instance, if a library function internally opened and retained a handle to the
+user's home directory, and then the application called `chroot`, that handle
+would be a sandbox escape.
+
+This document attempts to describe these baseline OS dependencies and long-lived
+internal resources. These dependencies may change over time, but we aim to
+[work with sandboxed consumers](/BREAKING-CHANGES.md) when they do. However,
+each sandbox imposes different constraints, so, above all, sandboxed consumers
+must have ample test coverage to detect issues as they arise.
+
+## Baseline dependencies
+
+Callers must assume that any BoringSSL function may perform one of the following
+operations:
+
+### Memory allocation
+
+Any BoringSSL function may allocate memory via `malloc` and related functions.
+
+### Thread synchronization
+
+Any BoringSSL function may call into the platform's thread synchronization
+primitives, including read/write locks and the equivalent of `pthread_once`.
+These must succeed, or BoringSSL will abort the process. Callers, however, can
+assume that BoringSSL functions will not spawn internal threads, unless
+otherwise documented.
+
+Syscall-filtering sandboxes should note that BoringSSL uses `pthread_rwlock_t`
+on POSIX systems, which is less common and may not be part of other libraries'
+syscall surface. Additionally, thread synchronization primitives usually have an
+atomics-based fast path. If a sandbox blocks a necessary pthreads syscall, it
+may not show up in testing without lock contention.
+
+### Standard error
+
+Any BoringSSL function may write to `stderr` or file descriptor
+`STDERR_FILENO` (2), either via `FILE` APIs or low-level functions like `write`.
+Writes to `stderr` may fail, but there must some file at `STDERR_FILENO` which
+will tolerate error messages from BoringSSL. (The file descriptor must be
+allocated so calls to `open` do not accidentally open something else there.)
+
+Note some C standard library implementations also log to `stderr`, so callers
+should ensure this regardless.
+
+### Entropy
+
+Any BoringSSL function may draw entropy from the OS. On Windows, this uses
+`RtlGenRandom` and, on POSIX systems, this uses `getrandom`, `getentropy`, or a
+`read` from a file descriptor to `/dev/urandom`. These operations must succeed
+or BoringSSL will abort the process.
+
+Note even deterministic algorithms may require OS entropy. For example,
+RSASSA-PKCS1-v1_5 is deterministic, but BoringSSL draws entropy to implement
+RSA blinding.
+
+Entropy gathering additionally has some initialization dependencies described in
+the following section.
+
+## Initialization
+
+BoringSSL has some uncommon OS dependencies which are only used once to
+initialize some state. Sandboxes which drop privileges after some setup work may
+use `CRYPTO_pre_sandbox_init` to initialize this state ahead of time. Otherwise,
+callers must assume any BoringSSL function may depend on these resources, in
+addition to the operations above.
+
+### CPU capabilities
+
+On Linux ARM platforms, BoringSSL depends on OS APIs to query CPU capabilities.
+32-bit and 64-bit ARM both depend on the `getauxval` function. 32-bit ARM, to
+work around bugs in older Android devices, may additionally read `/proc/cpuinfo`
+and `/proc/self/auxv`.
+
+If querying CPU capabilities fails, BoringSSL will still function, but may not
+perform as well.
+
+### Entropy
+
+On Linux systems without a working `getrandom`, drawing entropy from the OS
+additionally requires opening `/dev/urandom`. If this fails, BoringSSL will
+abort the process. BoringSSL retains the resulting file descriptor, even across
+privilege transitions.
+
+### Fork protection
+
+On Linux, BoringSSL allocates a page and calls `madvise` with `MADV_WIPEONFORK`
+to protect single-use state from `fork`. This operation must not crash, but if
+it fails, BoringSSL will use alternate fork-safety strategies, potentially at a
+performance cost. If it succeeds, BoringSSL assumes `MADV_WIPEONFORK` is
+functional and relies on it for fork-safety. Sandboxes must not report success
+if they ignore the `MADV_WIPEONFORK` flag. As of writing, QEMU will ignore
+`madvise` calls and report success, so BoringSSL detects this by calling
+`madvise` with -1. Sandboxes must cleanly report an error instead of crashing.
+
+Once initialized, this mechanism does not require system calls in the steady
+state, though note the configured page will be inherited across privilege
+transitions.
+
+## C and C++ standard library
+
+BoringSSL depends on the C and C++ standard libraries which, themselves, do not
+make any guarantees about sandboxes. If it produces the correct answer and has
+no observable invalid side effects, it is possible, though unreasonable, for
+`memcmp` to create and close a socket.
+
+BoringSSL assumes that functions in the C and C++ library only have the platform
+dependencies which would be "reasonable". For instance, a function in BoringSSL
+which aims not to open files will still freely call any libc memory and
+string functions.
+
+Note some C functions, such as `strerror`, may read files relating to the user's
+locale. BoringSSL may trigger these paths and assumes the sandbox environment
+will tolerate this. BoringSSL additionally cannot make guarantees about which
+system calls are used by standard library's syscall wrappers. In some cases, the
+compiler may add dependencies. (Some C++ language features emit locking code.)
+Syscall-filtering sandboxes may need updates as these dependencies change.
diff --git a/crypto/crypto.c b/crypto/crypto.c
index 297240e..6886aa4 100644
--- a/crypto/crypto.c
+++ b/crypto/crypto.c
@@ -16,6 +16,8 @@
 
 #include <openssl/cpu.h>
 
+#include "fipsmodule/rand/fork_detect.h"
+#include "fipsmodule/rand/internal.h"
 #include "internal.h"
 
 
@@ -174,6 +176,15 @@
 #endif
 }
 
+void CRYPTO_pre_sandbox_init(void) {
+  // Read from /proc/cpuinfo if needed.
+  CRYPTO_library_init();
+  // Open /dev/urandom if needed.
+  CRYPTO_init_sysrand();
+  // Set up MADV_WIPEONFORK state if needed.
+  CRYPTO_get_fork_generation();
+}
+
 const char *SSLeay_version(int which) { return OpenSSL_version(which); }
 
 const char *OpenSSL_version(int which) {
diff --git a/crypto/fipsmodule/rand/internal.h b/crypto/fipsmodule/rand/internal.h
index 1b15928..db81c33 100644
--- a/crypto/fipsmodule/rand/internal.h
+++ b/crypto/fipsmodule/rand/internal.h
@@ -41,6 +41,10 @@
 void CRYPTO_sysrand(uint8_t *buf, size_t len);
 
 #if defined(OPENSSL_URANDOM)
+// CRYPTO_init_sysrand initializes long-lived resources needed to draw entropy
+// from the operating system.
+void CRYPTO_init_sysrand(void);
+
 // CRYPTO_sysrand_for_seed fills |len| bytes at |buf| with entropy from the
 // operating system. It may draw from the |GRND_RANDOM| pool on Android,
 // depending on the vendor's configuration.
@@ -53,6 +57,8 @@
 // return 0.
 int CRYPTO_sysrand_if_available(uint8_t *buf, size_t len);
 #else
+OPENSSL_INLINE void CRYPTO_init_sysrand(void) {}
+
 OPENSSL_INLINE void CRYPTO_sysrand_for_seed(uint8_t *buf, size_t len) {
   CRYPTO_sysrand(buf, len);
 }
diff --git a/crypto/fipsmodule/rand/urandom.c b/crypto/fipsmodule/rand/urandom.c
index c20340a..bf15eda 100644
--- a/crypto/fipsmodule/rand/urandom.c
+++ b/crypto/fipsmodule/rand/urandom.c
@@ -332,7 +332,7 @@
   *urandom_fd_requested_bss_get() = fd;
   CRYPTO_STATIC_MUTEX_unlock_write(rand_lock_bss_get());
 
-  CRYPTO_once(rand_once_bss_get(), init_once);
+  CRYPTO_init_sysrand();
   if (*urandom_fd_bss_get() == kHaveGetrandom) {
     close(fd);
   } else if (*urandom_fd_bss_get() != fd) {
@@ -362,7 +362,7 @@
   }
 #endif
 
-  CRYPTO_once(rand_once_bss_get(), init_once);
+  CRYPTO_init_sysrand();
   if (block) {
     CRYPTO_once(wait_for_entropy_once_bss_get(), wait_for_entropy);
   }
@@ -417,6 +417,10 @@
   }
 }
 
+void CRYPTO_init_sysrand(void) {
+  CRYPTO_once(rand_once_bss_get(), init_once);
+}
+
 #if defined(BORINGSSL_FIPS)
 void CRYPTO_sysrand_for_seed(uint8_t *out, size_t requested) {
   if (!fill_with_entropy(out, requested, /*block=*/1, /*seed=*/1)) {
diff --git a/crypto/thread_test.cc b/crypto/thread_test.cc
index f9fad9b..aa17e35 100644
--- a/crypto/thread_test.cc
+++ b/crypto/thread_test.cc
@@ -15,6 +15,7 @@
 #include "internal.h"
 
 #include <chrono>
+#include <vector>
 #include <thread>
 
 #include <gtest/gtest.h>
@@ -130,4 +131,32 @@
   thread.join();
 }
 
+TEST(ThreadTest, InitThreads) {
+  constexpr size_t kNumThreads = 10;
+
+  // |CRYPTO_library_init| is safe to call across threads.
+  std::vector<std::thread> threads;
+  threads.reserve(kNumThreads);
+  for (size_t i = 0; i < kNumThreads; i++) {
+    threads.emplace_back(&CRYPTO_library_init);
+  }
+  for (auto &thread : threads) {
+    thread.join();
+  }
+}
+
+TEST(ThreadTest, PreSandboxInitThreads) {
+  constexpr size_t kNumThreads = 10;
+
+  // |CRYPTO_pre_sandbox_init| is safe to call across threads.
+  std::vector<std::thread> threads;
+  threads.reserve(kNumThreads);
+  for (size_t i = 0; i < kNumThreads; i++) {
+    threads.emplace_back(&CRYPTO_pre_sandbox_init);
+  }
+  for (auto &thread : threads) {
+    thread.join();
+  }
+}
+
 #endif  // OPENSSL_THREADS
diff --git a/include/openssl/crypto.h b/include/openssl/crypto.h
index e539bdb..0dc5373 100644
--- a/include/openssl/crypto.h
+++ b/include/openssl/crypto.h
@@ -63,6 +63,14 @@
 // success and zero on error.
 OPENSSL_EXPORT int BORINGSSL_self_test(void);
 
+// CRYPTO_pre_sandbox_init initializes the crypto library, pre-acquiring some
+// unusual resources to aid running in sandboxed environments. It is safe to
+// call this function multiple times and concurrently from multiple threads.
+//
+// For more details on using BoringSSL in a sandboxed environment, see
+// SANDBOXING.md in the source tree.
+OPENSSL_EXPORT void CRYPTO_pre_sandbox_init(void);
+
 
 // Deprecated functions.