Skip to content

Commit

Permalink
worker: set stack size for worker threads
Browse files Browse the repository at this point in the history
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: #26049
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Ben Noordhuis <[email protected]>
Reviewed-By: Colin Ihrig <[email protected]>
  • Loading branch information
addaleax committed Feb 13, 2019
1 parent 8b5a2c4 commit 9c9aefe
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 1 deletion.
13 changes: 12 additions & 1 deletion src/node_worker.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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_,
Expand Down Expand Up @@ -488,8 +490,17 @@ void Worker::StartThread(const FunctionCallbackInfo<Value>& args) {
static_cast<Worker*>(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<Worker*>(arg);
const uintptr_t stack_top = reinterpret_cast<uintptr_t>(&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_);
Expand Down
6 changes: 6 additions & 0 deletions src/node_worker.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<MessagePortData> child_port_data_;

Expand Down
11 changes: 11 additions & 0 deletions test/parallel/test-worker-stack-overflow.js
Original file line number Diff line number Diff line change
@@ -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');
}));

0 comments on commit 9c9aefe

Please sign in to comment.