From 9c9aefe2a09d601a36366062c856f852f3778b00 Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Tue, 5 Feb 2019 21:51:28 +0100 Subject: [PATCH] worker: set stack size for worker threads MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is so we can inform V8 about a known limit for the stack. Otherwise, on some systems recursive functions may lead to segmentation faults rather than “safe” failures. PR-URL: https://github.com/nodejs/node/pull/26049 Reviewed-By: James M Snell Reviewed-By: Ben Noordhuis Reviewed-By: Colin Ihrig --- src/node_worker.cc | 13 ++++++++++++- src/node_worker.h | 6 ++++++ test/parallel/test-worker-stack-overflow.js | 11 +++++++++++ 3 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 test/parallel/test-worker-stack-overflow.js diff --git a/src/node_worker.cc b/src/node_worker.cc index ebd1924b8f2479..f38b187c18c5b8 100644 --- a/src/node_worker.cc +++ b/src/node_worker.cc @@ -118,6 +118,8 @@ class WorkerThreadData { { Locker locker(isolate); Isolate::Scope isolate_scope(isolate); + isolate->SetStackLimit(w_->stack_base_); + HandleScope handle_scope(isolate); isolate_data_.reset(CreateIsolateData(isolate, &loop_, @@ -488,8 +490,17 @@ void Worker::StartThread(const FunctionCallbackInfo& args) { static_cast(handle->data)->OnThreadStopped(); }), 0); - CHECK_EQ(uv_thread_create(&w->tid_, [](void* arg) { + uv_thread_options_t thread_options; + thread_options.flags = UV_THREAD_HAS_STACK_SIZE; + thread_options.stack_size = kStackSize; + CHECK_EQ(uv_thread_create_ex(&w->tid_, &thread_options, [](void* arg) { Worker* w = static_cast(arg); + const uintptr_t stack_top = reinterpret_cast(&arg); + + // Leave a few kilobytes just to make sure we're within limits and have + // some space to do work in C++ land. + w->stack_base_ = stack_top - (kStackSize - kStackBufferSize); + w->Run(); Mutex::ScopedLock lock(w->mutex_); diff --git a/src/node_worker.h b/src/node_worker.h index 4d7a7335ca6d63..68848c859990f0 100644 --- a/src/node_worker.h +++ b/src/node_worker.h @@ -80,6 +80,12 @@ class Worker : public AsyncWrap { bool thread_joined_ = true; int exit_code_ = 0; uint64_t thread_id_ = -1; + uintptr_t stack_base_; + + // Full size of the thread's stack. + static constexpr size_t kStackSize = 4 * 1024 * 1024; + // Stack buffer size that is not available to the JS engine. + static constexpr size_t kStackBufferSize = 192 * 1024; std::unique_ptr child_port_data_; diff --git a/test/parallel/test-worker-stack-overflow.js b/test/parallel/test-worker-stack-overflow.js new file mode 100644 index 00000000000000..99a34b5369006f --- /dev/null +++ b/test/parallel/test-worker-stack-overflow.js @@ -0,0 +1,11 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { Worker } = require('worker_threads'); + +const worker = new Worker('function f() { f(); } f();', { eval: true }); + +worker.on('error', common.mustCall((err) => { + assert.strictEqual(err.constructor, RangeError); + assert.strictEqual(err.message, 'Maximum call stack size exceeded'); +}));