Skip to content

Android: data race on ArgConverter's process-wide long-conversion cache when workers start or stop concurrently #93

Open

Description

@NathanWalker

Summary

ArgConverter::s_type_long_operations_cache is a single, process-wide robin_hood::unordered_map. Each runtime finds, inserts and erases its own entry from its own thread, and nothing synchronizes those accesses:

  • find / insert: GetTypeLongCache(), on the first long conversion in that runtime.
  • erase: onDisposeEnv() / onDisposeRuntime(), when the runtime is disposed.

When several workers start or stop at once, one thread can rehash the table while another is reading or writing it. That is a data race on the map, and in practice a heap use-after-free.

Affects both binding layers:

  • NativeScript/ffi/jni/napi/conversion/ArgConverter.{h,cpp}: the map is keyed by napi_env.
  • NativeScript/ffi/jni/jsi/conversion/ArgConverter.{h,cpp}: the map is keyed by rt.identity().

How it was found

A worker stress test, run while testing #91 and #92 on an API 36 arm64 emulator. HWASan was enabled locally by uncommenting the -fsanitize=hwaddress lines in platforms/android/test-app/runtime/CMakeLists.txt; it is off by default. The test:

  • 12 workers at a time are created and terminated after 20–300 ms;
  • each worker churns Java objects and forces JS and Java GC;
  • the main runtime forces GC every 25 ms.

HWASan reported a memory error inside ArgConverter::GetTypeLongCache, in both the napi and jsi trees. On the old code (without #91), the crash happened in 1 of 3 napi runs and 1 of 2 jsi runs. It is independent of the GC-delivery race that #91 fixes.

Expected

Each runtime's cache is reachable only by that runtime, and creating or disposing one runtime can't corrupt another's entry. Possible fixes:

  • make the cache per-runtime state, reached the way MetadataNode's per-runtime cache is;
  • or guard the map with a mutex. This is cheap: a lookup happens once per long conversion, and the pointer could be cached after that.

Environment

  • Branch: android-react-native at 64c7fed
  • Emulator: Android API 36, arm64, debug build of platforms/android/test-app with HWASan enabled locally
  • Engine: V8

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions