nodejs / nodejs/nan

EXC_BAD_ACCESS in node::EmitAsyncDestroy

Open
#772 9 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
C++
Stars
3.4k
Forks
531
Avg merge
21m
Merged PRs (30d)
1

Description

Hi,

I'm occasionally hitting an error in the destructor for AsyncResource. Sorry I don't have a minimal reproduction example yet. I've just started hitting this since migrating js-platform/node-webrtc to AsyncResource. I've got a graph of objects, some of them just ObjectWrap instances and some of the ObjectWrap + AsyncResource instances. The AsyncResource instances wrap libuv's uv_default_loop(). I call Ref when constructing these and Unref in the uv_close_cb. The graph looks like this:

RTCPeerConnection (ObjectWrap, AsyncResource)
  |
  |
  +--> RTCRtpReceiver (ObjectWrap)
         |
         |
         |    MediaStream (ObjectWrap)
         |      |
         |      v
         +--> MediaStreamTrack (ObjectWrap, AsyncResource)

In my tests, I'm only seeing the issues in ~MediaStreamTrack, and then, only occasionally. I'm also not seeing the destructor fire twice or anything like that. Interestingly, if I comment out the node::EmitAsyncDestroy call, I don't see crashes. I'm using Node v9.8.0.

* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x0)
    frame #0: 0x0000000100009322 node`node::EmitAsyncDestroy(v8::Isolate*, node::async_context) + 18
node`node::EmitAsyncDestroy:
->  0x100009322 <+18>: movq   (%rax), %rax
    0x100009325 <+21>: movq   0x37(%rax), %rax
    0x100009329 <+25>: movq   0x10f(%rax), %rdi
    0x100009330 <+32>: movsd  -0x8(%rbp), %xmm0         ; xmm0 = mem[0],zero
Target 0: (node) stopped.
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x0)
  * frame #0: 0x0000000100009322 node`node::EmitAsyncDestroy(v8::Isolate*, node::async_context) + 18
    frame #1: 0x000000010491cbe0 wrtc.node`Nan::AsyncResource::~AsyncResource(this=0x0000000102a02e88) at nan.h:514
    frame #2: 0x00000001049536f6 wrtc.node`node_webrtc::MediaStreamTrack::~MediaStreamTrack(this=0x0000000102a02e70) at mediastreamtrack.cc:44
    frame #3: 0x00000001049539c5 wrtc.node`node_webrtc::MediaStreamTrack::~MediaStreamTrack(this=0x0000000102a02e70) at mediastreamtrack.cc:40
    frame #4: 0x0000000104953a39 wrtc.node`node_webrtc::MediaStreamTrack::~MediaStreamTrack(this=0x0000000102a02e70) at mediastreamtrack.cc:40
    frame #5: 0x0000000104923fbb wrtc.node`Nan::ObjectWrap::WeakCallback(info=0x00007fff5fbf5b48) at nan_object_wrap.h:126
    frame #6: 0x00000001004f3384 node`v8::internal::GlobalHandles::DispatchPendingPhantomCallbacks(bool) + 180
    frame #7: 0x00000001004f36b1 node`v8::internal::GlobalHandles::PostGarbageCollectionProcessing(v8::internal::GarbageCollector, v8::GCCallbackFlags) + 49
    frame #8: 0x0000000100509085 node`v8::internal::Heap::PerformGarbageCollection(v8::internal::GarbageCollector, v8::GCCallbackFlags) + 2533
    frame #9: 0x000000010050800d node`v8::internal::Heap::CollectGarbage(v8::internal::AllocationSpace, v8::internal::GarbageCollectionReason, v8::GCCallbackFlags) + 733
    frame #10: 0x00000001004c9c92 node`v8::internal::Factory::NewFillerObject(int, bool, v8::internal::AllocationSpace) + 82
    frame #11: 0x0000000100774125 node`v8::internal::Runtime_AllocateInNewSpace(int, v8::internal::Object**, v8::internal::Isolate*) + 101
    frame #12: 0x00000c0326b842fd
    frame #13: 0x00000c0326c842ab
    frame #14: 0x00000c0326c3d196
    frame #15: 0x00000c0326c09cfc
    frame #16: 0x00000c0326b84239
    frame #17: 0x00000c0326b84101
    frame #18: 0x00000001004bf669 node`v8::internal::(anonymous namespace)::Invoke(v8::internal::Isolate*, bool, v8::internal::Handle<v8::internal::Object>, v8::internal::Handle<v8::internal::Object>, int, v8::internal::Handle<v8::internal::Object>*, v8::internal::Handle<v8::internal::Object>, v8::internal::Execution::MessageHandling) + 633
    frame #19: 0x00000001004bf893 node`v8::internal::Execution::TryCall(v8::internal::Isolate*, v8::internal::Handle<v8::internal::Object>, v8::internal::Handle<v8::internal::Object>, int, v8::internal::Handle<v8::internal::Object>*, v8::internal::Execution::MessageHandling, v8::internal::MaybeHandle<v8::internal::Object>*) + 227
    frame #20: 0x00000001005e1c9e node`v8::internal::Isolate::PromiseReactionJob(v8::internal::Handle<v8::internal::PromiseReactionJobInfo>, v8::internal::MaybeHandle<v8::internal::Object>*, v8::internal::MaybeHandle<v8::internal::Object>*) + 654
    frame #21: 0x00000001005e273c node`v8::internal::Isolate::RunMicrotasksInternal() + 1180
    frame #22: 0x00000001005e15e8 node`v8::internal::Isolate::RunMicrotasks() + 56
    frame #23: 0x000000010497d872 wrtc.node`node_webrtc::PromiseFulfillingEventLoop<node_webrtc::PeerConnection>::Run(this=0x0000000101ff7700) at promisefulfillingeventloop.h:34
    frame #24: 0x00000001049822c0 wrtc.node`node_webrtc::EventLoop<node_webrtc::PeerConnection>::EventLoop(this=0x0000000104982270, handle=0x0000000101ff77a0)::'lambda'(uv_async_s*)::operator()(uv_async_s*) const at eventloop.h:49
    frame #25: 0x0000000104982288 wrtc.node`node_webrtc::EventLoop<node_webrtc::PeerConnection>::EventLoop(handle=0x0000000101ff77a0)::'lambda'(uv_async_s*)::__invoke(uv_async_s*) at eventloop.h:47
    frame #26: 0x00000001019e77cb libuv.1.dylib`uv__async_io + 331
    frame #27: 0x00000001019fa41d libuv.1.dylib`uv__io_poll + 2061
    frame #28: 0x00000001019e7d43 libuv.1.dylib`uv_run + 371
    frame #29: 0x0000000100034b75 node`node::Start(v8::Isolate*, node::IsolateData*, int, char const* const*, int, char const* const*) + 661
    frame #30: 0x0000000100031e83 node`node::Start(uv_loop_s*, int, char const* const*, int, char const* const*) + 435
    frame #31: 0x0000000100031988 node`node::Start(int, char**) + 536
    frame #32: 0x00000001000012c4 node`start + 52

I'm opening this in case there's something obvious here I'm missing. The async hooks functionality is very new to me. If it's not obvious, I'll try to create a minimal repro.

Thanks,
Mark

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with nan.h around line 513 and node::EmitAsyncDestroy in Node v9.8.0, then inspect the MediaStreamTrack destructor at mediastreamtrack.cc:44 and the described uv_close_cb Ref/Unref lifecycle. Reproduce the EXC_BAD_ACCESS using a minimal example or the node-webrtc object graph; done means isolating the lifecycle condition causing the crash.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp, node.js
Domain
devtools
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.