Skip to content

Commit 9c9aefe

Browse files
committed
worker: set stack size for worker threads
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 <jasnell@gmail.com> Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
1 parent 8b5a2c4 commit 9c9aefe

File tree

3 files changed

+29
-1
lines changed

3 files changed

+29
-1
lines changed

src/node_worker.cc

+12-1
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,8 @@ class WorkerThreadData {
118118
{
119119
Locker locker(isolate);
120120
Isolate::Scope isolate_scope(isolate);
121+
isolate->SetStackLimit(w_->stack_base_);
122+
121123
HandleScope handle_scope(isolate);
122124
isolate_data_.reset(CreateIsolateData(isolate,
123125
&loop_,
@@ -488,8 +490,17 @@ void Worker::StartThread(const FunctionCallbackInfo<Value>& args) {
488490
static_cast<Worker*>(handle->data)->OnThreadStopped();
489491
}), 0);
490492

491-
CHECK_EQ(uv_thread_create(&w->tid_, [](void* arg) {
493+
uv_thread_options_t thread_options;
494+
thread_options.flags = UV_THREAD_HAS_STACK_SIZE;
495+
thread_options.stack_size = kStackSize;
496+
CHECK_EQ(uv_thread_create_ex(&w->tid_, &thread_options, [](void* arg) {
492497
Worker* w = static_cast<Worker*>(arg);
498+
const uintptr_t stack_top = reinterpret_cast<uintptr_t>(&arg);
499+
500+
// Leave a few kilobytes just to make sure we're within limits and have
501+
// some space to do work in C++ land.
502+
w->stack_base_ = stack_top - (kStackSize - kStackBufferSize);
503+
493504
w->Run();
494505

495506
Mutex::ScopedLock lock(w->mutex_);

src/node_worker.h

+6
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,12 @@ class Worker : public AsyncWrap {
8080
bool thread_joined_ = true;
8181
int exit_code_ = 0;
8282
uint64_t thread_id_ = -1;
83+
uintptr_t stack_base_;
84+
85+
// Full size of the thread's stack.
86+
static constexpr size_t kStackSize = 4 * 1024 * 1024;
87+
// Stack buffer size that is not available to the JS engine.
88+
static constexpr size_t kStackBufferSize = 192 * 1024;
8389

8490
std::unique_ptr<MessagePortData> child_port_data_;
8591

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
'use strict';
2+
const common = require('../common');
3+
const assert = require('assert');
4+
const { Worker } = require('worker_threads');
5+
6+
const worker = new Worker('function f() { f(); } f();', { eval: true });
7+
8+
worker.on('error', common.mustCall((err) => {
9+
assert.strictEqual(err.constructor, RangeError);
10+
assert.strictEqual(err.message, 'Maximum call stack size exceeded');
11+
}));

0 commit comments

Comments
 (0)