Skip to content

Commit 9de613f

Browse files
committed
fix another use-after-free...
1 parent 51f3f39 commit 9de613f

File tree

1 file changed

+19
-14
lines changed

1 file changed

+19
-14
lines changed

module.cc

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,7 @@ std::string GetThreadState(Isolate *isolate,
318318

319319
struct InterruptArgs {
320320
std::promise<JsStackTrace> promise;
321-
const std::optional<AsyncLocalStorageLookup> *store;
321+
std::shared_ptr<std::optional<AsyncLocalStorageLookup>> store;
322322
};
323323

324324
// Function to be called when an isolate's execution is interrupted
@@ -348,9 +348,9 @@ static void ExecutionInterrupted(Isolate *isolate, void *data) {
348348
}
349349

350350
// Function to capture the stack trace of a single isolate
351-
JsStackTrace
352-
CaptureStackTrace(Isolate *isolate,
353-
const std::optional<AsyncLocalStorageLookup> &store) {
351+
JsStackTrace CaptureStackTrace(
352+
Isolate *isolate,
353+
const std::shared_ptr<std::optional<AsyncLocalStorageLookup>> &store) {
354354
if (isolate->IsExecutionTerminating()) {
355355
return JsStackTrace{{}, ""};
356356
}
@@ -359,12 +359,18 @@ CaptureStackTrace(Isolate *isolate,
359359
auto future = promise.get_future();
360360

361361
// The v8 isolate must be interrupted to capture the stack trace
362+
// Note: Even if we timeout below, the interrupt may still fire later.
363+
// The InterruptArgs holds a shared_ptr to keep data alive until the callback
364+
// executes.
362365
isolate->RequestInterrupt(ExecutionInterrupted,
363-
new InterruptArgs{std::move(promise), &store});
366+
new InterruptArgs{std::move(promise), store});
364367

365368
// Wait with timeout to prevent infinite hang if isolate never processes
366369
// interrupt (e.g., stuck in native code or terminating)
367370
if (future.wait_for(std::chrono::seconds(5)) == std::future_status::timeout) {
371+
// Timeout occurred. The InterruptArgs is intentionally leaked - it will be
372+
// deleted by ExecutionInterrupted if/when the callback eventually fires.
373+
// The shared_ptr keeps the store data alive.
368374
return JsStackTrace{{}, ""};
369375
}
370376

@@ -393,15 +399,14 @@ void CaptureStackTraces(const FunctionCallbackInfo<Value> &args) {
393399
// from map
394400
auto async_store_ptr = thread_info.async_store;
395401

396-
futures.emplace_back(
397-
std::async(std::launch::async,
398-
[thread_isolate, thread_name, poll_state,
399-
async_store_ptr]() -> ThreadResult {
400-
return ThreadResult{
401-
thread_name,
402-
CaptureStackTrace(thread_isolate, *async_store_ptr),
403-
poll_state};
404-
}));
402+
futures.emplace_back(std::async(
403+
std::launch::async,
404+
[thread_isolate, thread_name, poll_state,
405+
async_store_ptr]() -> ThreadResult {
406+
return ThreadResult{
407+
thread_name, CaptureStackTrace(thread_isolate, async_store_ptr),
408+
poll_state};
409+
}));
405410
}
406411
}
407412

0 commit comments

Comments
 (0)