Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions lib/internal/bootstrap/realm.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ ObjectDefineProperty(process, 'moduleLoadList', {
// more, we just implement them as legacy wrappers instead. See the
// legacyWrapperList.
const processBindingAllowList = new SafeSet([
// Qualia: node-fibers (fibers_sync.js setupAsyncHacks) saves/restores the async id stack across
// fiber switches through process.binding('async_wrap'). Upstream dropped it from this list; without
// it the hack silently disables itself and the stack corrupts on the first yield.
'async_wrap',
'buffer',
'cares_wrap',
'config',
Expand Down
5 changes: 5 additions & 0 deletions lib/internal/main/watch_mode.js
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,11 @@ async function restart(child, trigger) {
process.stdout.write(`${blue}Change detected in ${inspect(trigger)}${white}\n`);
}
process.stdout.write(`${green}Restarting ${kCommandStr}${white}\n`);
if (process.send) {
// A parent process (e.g. meteor-lite dev-run) uses this to know the child is going down
// before it starts seeing the new child's watch:require / watch:import messages.
process.send({ 'watch:restarting': {} });
}
await stop(child);
return start();
} finally {
Expand Down
57 changes: 46 additions & 11 deletions src/crypto/crypto_context.cc
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#endif

#include <set>
#include <unordered_set>

namespace node {

Expand Down Expand Up @@ -92,16 +93,38 @@ struct X509Less {
};
using X509Set = std::set<X509*, X509Less>;

// Per-thread root cert store. See NewRootCertStore() on what it contains.
static thread_local X509_STORE* root_cert_store = nullptr;
// Process-wide root cert store. See NewRootCertStore() on what it contains.
//
// Upstream keeps the store in thread_local storage (one per worker thread,
// freed by that thread's Environment cleanup hook). Qualia runs Meteor on
// node-fibers with thread-backed coroutines (CORO_PTHREAD), so the JavaScript
// of a single Environment executes on many OS threads, one per fiber. With
// thread_local storage every fiber thread that first performs a TLS handshake
// parses its own copy of the root store and registers the same
// (callback, argument) cleanup hook on the shared Environment again, which
// trips the CHECK in CleanupQueue::Add. The store is therefore process-wide,
// as it was through node 18, guarded by a mutex; the cleanup hook is
// registered once per Environment and the store is freed when the last
// Environment that used it is torn down.
static Mutex root_cert_store_mutex;
static X509_STORE* root_cert_store = nullptr;
// If the user calls tls.setDefaultCACertificates() this will be used
// to hold the user-provided certificates, the root_cert_store and any new
// copy generated by NewRootCertStore() will then contain the certificates
// from this set.
static thread_local std::unique_ptr<X509Set> root_certs_from_users;
static thread_local bool has_cleanup_hook = false;
static std::unique_ptr<X509Set> root_certs_from_users;
static std::unordered_set<Environment*> root_cert_store_envs;

static X509_STORE* NewRootCertStoreLocked(Environment* env);

static void CleanupRootCertStore(void* arg) {
Mutex::ScopedLock lock(root_cert_store_mutex);
root_cert_store_envs.erase(static_cast<Environment*>(arg));
if (!root_cert_store_envs.empty()) {
// Another Environment still shares the store.
return;
}

static void CleanupRootCertStore(void*) {
if (root_cert_store != nullptr) {
X509_STORE_free(root_cert_store);
root_cert_store = nullptr;
Expand All @@ -113,25 +136,27 @@ static void CleanupRootCertStore(void*) {
}
root_certs_from_users.reset();
}

has_cleanup_hook = false;
}

static void EnsureRootCertStoreCleanupHook(Environment* env) {
if (env == nullptr || has_cleanup_hook) {
if (env == nullptr) {
return;
}

env->AddCleanupHook(CleanupRootCertStore, nullptr);
has_cleanup_hook = true;
Mutex::ScopedLock lock(root_cert_store_mutex);
if (!root_cert_store_envs.insert(env).second) {
return;
}
env->AddCleanupHook(CleanupRootCertStore, env);
}

X509_STORE* GetOrCreateRootCertStore(Environment* env) {
EnsureRootCertStoreCleanupHook(env);
Mutex::ScopedLock lock(root_cert_store_mutex);
if (root_cert_store != nullptr) {
return root_cert_store;
}
root_cert_store = NewRootCertStore(env);
root_cert_store = NewRootCertStoreLocked(env);
return root_cert_store;
}

Expand Down Expand Up @@ -1061,6 +1086,12 @@ void StartLoadingCertificatesOffThread(
// the certificates provided by users.
// TODO(joyeecheung): maybe these rules need a bit of consolidation?
X509_STORE* NewRootCertStore(Environment* env) {
Mutex::ScopedLock lock(root_cert_store_mutex);
return NewRootCertStoreLocked(env);
}

// Caller holds root_cert_store_mutex.
static X509_STORE* NewRootCertStoreLocked(Environment* env) {
X509_STORE* store = X509_STORE_new();
CHECK_NOT_NULL(store);

Expand Down Expand Up @@ -1246,6 +1277,7 @@ MaybeLocal<Array> X509sToArrayOfStrings(Environment* env,

void GetUserRootCertificates(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
Mutex::ScopedLock lock(root_cert_store_mutex);
CHECK_NOT_NULL(root_certs_from_users);
Local<Array> results;
if (X509sToArrayOfStrings(env,
Expand All @@ -1266,6 +1298,7 @@ void ResetRootCertStore(const FunctionCallbackInfo<Value>& args) {

if (cert_array->Length() == 0) {
// If the array is empty, just clear the user certs and reset the store.
Mutex::ScopedLock lock(root_cert_store_mutex);
if (root_cert_store != nullptr) {
X509_STORE_free(root_cert_store);
root_cert_store = nullptr;
Expand Down Expand Up @@ -1303,6 +1336,8 @@ void ResetRootCertStore(const FunctionCallbackInfo<Value>& args) {
}
}

Mutex::ScopedLock lock(root_cert_store_mutex);

// Free any existing certificates in the old set.
if (root_certs_from_users != nullptr) {
for (X509* cert : *root_certs_from_users) {
Expand Down
Loading