Skip to content

A repro where the Android method tracer aborts, because a native dependency called pthread_[join|exit] before detaching.

Notifications You must be signed in to change notification settings

supervacuus/invalid_pthread_repro

Repository files navigation

Repro for invalid pthread_t 0x... passed to pthread_getcpuclockid

This is a repro of the abort seen in the following issue: getsentry/sentry-java#2604. It is based on a basic Android Studio app template. The only things that changed were the following:

  • text content and size of the centered TextView, which is clickable to trigger the repro.
  • the MainActivity starts the Android method tracer onCreate in background sampling mode (using the same parameters as the Android SDK).
  • an OnClickListener calling the native code that triggers the abort.

What happens in the native code?

  • The JNI function starts a native thread (appending it to the pthread thread list)

  • the native thread attaches to the ART (appending it to the ART thread list)

  • but exits before detaching (-> the thread remains in the ART thread list)

  • the JNI function joins that thread ( removing it from the pthread thread list)

    JavaVM *parent_vm = nullptr;
    env->GetJavaVM(&parent_vm);
    // create a single thread that attaches, but never detaches.
    auto t = std::thread([=] {
    JNIEnv *child_env = nullptr;
    parent_vm->GetEnv(reinterpret_cast<void **>(&child_env), JNI_VERSION_1_6);
    JavaVMAttachArgs args;
    args.version = JNI_VERSION_1_6;
    args.name = "non_detaching_thread";
    args.group = nullptr;
    // this adds the thread to the runtime thread-list, that
    // the tracer uses to sample all running runtime threads
    parent_vm->AttachCurrentThread(&child_env, &args);
    #if IGNORED
    parent_vm->DetachCurrentThread();
    #endif
    });
    // Join so that bionic removes this thread from the pthread thread-list. After join completion
    // the thread will still be in the runtime thread-list, leading tracer to sample a thread that
    // has no pthread equivalent. A `pthread_exit()` before Detach will achieve the same thing.
    t.join();

Why does that lead to an abort?

  • Calling pthread_exit() or pthread_join() before an attached thread could be detached is illegal.
  • But that doesn't necessarily mean that it will fail at that point.
  • Since the two thread lists are now out of sync, the ART method tracer will retrieve a thread from the ART thread list that is no longer maintained in pthread state
  • The first function requiring a look-up in the tracer sampling path seems to be pthread_getcpuclockid() [1], which aborts with a fatal log if the provided pthread_t cannot be found [2].

[1] https://cs.android.com/android/platform/superproject/main/+/main:bionic/libc/bionic/pthread_getcpuclockid.cpp;l=36

[2] https://cs.android.com/android/platform/superproject/main/+/main:bionic/libc/bionic/pthread_internal.cpp;l=119-130

About

A repro where the Android method tracer aborts, because a native dependency called pthread_[join|exit] before detaching.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published