Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exception 0x406D1388 thrown #4128

Closed
s-zhao-3g opened this issue Jan 26, 2021 · 5 comments
Closed

Exception 0x406D1388 thrown #4128

s-zhao-3g opened this issue Jan 26, 2021 · 5 comments

Comments

@s-zhao-3g
Copy link
Contributor

Issue description

Unexpected exception (0x406D1388) thrown when spawning io/reaper thread with debugger other than Visual Studio Debbugger attached in x64 arch.

Environment

  • libzmq version: 4.3.3
  • OS: Windows 10 x64 Pro (10.0.19042.746)
  • Compiler: cl.exe
  • Debugger: cLion 2020.3 bundled LLDB

Minimal test code to reproduce the issue

#include "zmq.h"
int main() {
  void* ctx = zmq_ctx_new();
  void* sock = zmq_socket(ctx, ZMQ_PUSH);
  return 0;
}

What's the actual result?

Exception 0x406d1388 encountered at address xxx.

What's the expected result?

Exit with code 0.

@s-zhao-3g
Copy link
Contributor Author

s-zhao-3g commented Jan 26, 2021

It appears to me the problem is in thread.cpp:

tib->ExceptionList = (_EXCEPTION_REGISTRATION_RECORD *) &rec;

const DWORD MS_VC_EXCEPTION = 0x406D1388;
RaiseException (MS_VC_EXCEPTION, 0,
                    sizeof (thread_info) / sizeof (ULONG_PTR),
                    (ULONG_PTR *) &thread_info);

tib->ExceptionList =
      (_EXCEPTION_REGISTRATION_RECORD
         *) (((MY_EXCEPTION_REGISTRATION_RECORD *) tib->ExceptionList)->Next);

The exception handlers are accessible through tib->ExceptionList in x86, while in x64 the pointer is set to nullptr, so the push & pull operation does not have any effect.

Test step 1: Test 0x406D1388

I extracted the exception part from thread.cpp and write this code:

#include "windows.h"
#include "winnt.h"

struct thread_info_t
{
  DWORD _type;
  LPCSTR _name;
  DWORD _thread_id;
  DWORD _flags;
};

int main() {
  thread_info_t thread_info{};
  thread_info._type = 0x1000;
  thread_info._name = "test_name";
  thread_info._thread_id = -1;
  thread_info._flags = 0;

  const DWORD MS_VC_EXCEPTION = 0x406D1388;
  RaiseException(MS_VC_EXCEPTION, 0,
                 sizeof(thread_info) / sizeof(ULONG_PTR),
                 (ULONG_PTR*)&thread_info);

  return 0;
}

Here is the summary of my test:
x64 & x86:

  • MinGW: Throws exception unless debugger is attached;
  • cl.exe: Throws exception unless Visual Studio Debugger is attached;

Test step 2: Test 0x406D1387 (some random exception) with pushing & poping exception registration list

#include "windows.h"
#include "winnt.h"

struct thread_info_t
{
  DWORD _type;
  LPCSTR _name;
  DWORD _thread_id;
  DWORD _flags;
};

struct MY_EXCEPTION_REGISTRATION_RECORD
{
  typedef EXCEPTION_DISPOSITION (NTAPI *HandlerFunctionType) (
      EXCEPTION_RECORD *, void *, CONTEXT *, void *);

  MY_EXCEPTION_REGISTRATION_RECORD *Next;
  HandlerFunctionType Handler;
};

static EXCEPTION_DISPOSITION NTAPI continue_execution (EXCEPTION_RECORD *rec,
                                                       void *frame,
                                                       CONTEXT *ctx,
                                                       void *disp)
{
  return ExceptionContinueExecution;
}

int main() {
  thread_info_t thread_info{};
  thread_info._type = 0x1000;
  thread_info._name = "test_name";
  thread_info._thread_id = -1;
  thread_info._flags = 0;

  NT_TIB *tib = ((NT_TIB *) NtCurrentTeb ());

  MY_EXCEPTION_REGISTRATION_RECORD rec{};
  rec.Next = (MY_EXCEPTION_REGISTRATION_RECORD *) tib->ExceptionList;
  rec.Handler = continue_execution;

  tib->ExceptionList = (_EXCEPTION_REGISTRATION_RECORD*)&rec;

  const DWORD MS_VC_EXCEPTION = 0x406D1387;
  RaiseException(MS_VC_EXCEPTION, 0,
                 sizeof(thread_info) / sizeof(ULONG_PTR),
                 (ULONG_PTR*)&thread_info);

  tib->ExceptionList =
            (_EXCEPTION_REGISTRATION_RECORD
                *)(((MY_EXCEPTION_REGISTRATION_RECORD*)tib->ExceptionList)->Next);

  return 0;
}

Test result:
cl.exe & MinGW

  • x86: Does not throw exception;
  • x64: Throws exception;

@s-zhao-3g
Copy link
Contributor Author

s-zhao-3g commented Jan 26, 2021

My suggestion is to __try...__except instead of messing up with exception registration if using cl.exe; and use pthread_setname_np if using MinGW. I have not tested with other compilers, but I think there should be more elegant solution than reg, since it is machine dependent

s-zhao-3g added a commit to s-zhao-3g/libzmq that referenced this issue Jan 27, 2021
@bluca
Copy link
Member

bluca commented Jan 27, 2021

#4129

@thxkiwi
Copy link
Contributor

thxkiwi commented Aug 22, 2022

What was the resolution here? I'm experiencing this in 4.3.4.

"MyLibrary.lib" uses ZeroMQ 4.3.4
"ManagedMyLibrary.dll" static links "MyLibrary.lib" and calls "MyFunction()" in "MyLibrary.lib"
"ManagedMyLibrarydll" is then used from C# code.

Part of "MyFunction()" creates a ZeroMQ socket and I'm getting this exit code from overall C#/CLR process when debugging it.

@s-zhao-3g
Copy link
Contributor Author

What was the resolution here? I'm experiencing this in 4.3.4.

"MyLibrary.lib" uses ZeroMQ 4.3.4 "ManagedMyLibrary.dll" static links "MyLibrary.lib" and calls "MyFunction()" in "MyLibrary.lib" "ManagedMyLibrarydll" is then used from C# code.

Part of "MyFunction()" creates a ZeroMQ socket and I'm getting this exit code from overall C#/CLR process when debugging it.

The reason for this is that throwing 0x406D1388 is kindof... the 'brute force' idiom for cl.exe compiler to name its threads.

Actually I submitted a PR (#4129) to resolve this back then, just several days before 4.3.4 release. Although I only added thread naming for MinGW and cl, the exception won't show in other compilers.

Unfortunately it seems that the PR was not included in 4.3.4 (Jan 2021), and for some reason...there is no '4.3.5' for now (Aug 2022). To resolve this, depending on your source code, you can fork or download 4.3.4 source and mannually update thread.cpp using the code in #4129.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants