Symptom
The crash call stack is as follows:
#00 pc 00000000017793f8 /system/lib64/ndk/libjsvm.so #01 pc 000000000163cef4 /system/lib64/ndk/libjsvm.so(v8::internal::Heap::AllocateExternalBackingStore(std::__h::function<void* (unsigned long)> const&, unsigned long)+212) #02 pc 0000000001777454 /system/lib64/ndk/libjsvm.so(v8::internal::BackingStore::Allocate(v8::internal::Isolate*, unsigned long, v8::internal::SharedFlag, v8::internal::InitializedFlag)+220) #03 pc 000000000149e420 /system/lib64/ndk/libjsvm.so #04 pc 000000000149d99c /system/lib64/ndk/libjsvm.so(v8::internal::Builtin_ArrayBufferConstructor(int, unsigned long*, v8::internal::Isolate*)+260) #05 pc 0000000000f75984 /system/lib64/ndk/libjsvm.so(Builtins_CEntry_Return1_ArgvOnStack_BuiltinExit+100) #06 pc 0000000000ee6e40 /system/lib64/ndk/libjsvm.so(Builtins_JSBuiltinsConstructStub+320) #07 pc 000000000102eae4 /system/lib64/ndk/libjsvm.so(Builtins_ConstructHandler+644) #08 pc 0000000000ee9b88 /system/lib64/ndk/libjsvm.so(Builtins_InterpreterEntryTrampoline+264) #09 pc 0000000000ee9b88 /system/lib64/ndk/libjsvm.so(Builtins_InterpreterEntryTrampoline+264)
Possible Causes
According to the call stack, the error occurs in the v8::internal::Heap::AllocateExternalBackingStore function of the libjsvm.so library. When the function attempts to allocate external storage, it calls the allocate function, in which a null pointer exception occurs.
std::unique_ptr<BackingStore> BackingStore::Allocate(
Isolate* isolate, size_t byte_length, SharedFlag shared,
InitializedFlag initialized) {
...
auto allocator = isolate->array_buffer_allocator();
...
auto allocate_buffer = [allocator, initialized](size_t byte_length) {
if (initialized == InitializedFlag::kUninitialized) {
return allocator->AllocateUninitialized(byte_length);
}
void* buffer_start = allocator->Allocate(byte_length);
if (buffer_start) {
// TODO(wasm): node does not implement the zero-initialization API.
// Reenable this debug check when node does implement it properly.
constexpr bool
kDebugCheckZeroDisabledDueToNodeNotImplementingZeroInitAPI = true;
if ((!(kDebugCheckZeroDisabledDueToNodeNotImplementingZeroInitAPI)) &&
!v8_flags.mock_arraybuffer_allocator) {
DebugCheckZero(buffer_start, byte_length);
}
}
return buffer_start;
};
buffer_start = isolate->heap()->AllocateExternalBackingStore(
allocate_buffer, byte_length);
...
} The root cause of the error is that the allocator is empty. The allocator is returned by the array_buffer_allocator() method of Isolate, which is created by v8::Isolate::New(create_params) in the OH_JSVM_CreateVM function. Therefore, the initialization process of array_buffer_allocator may be incorrect. Since array_buffer_allocator is created by GetOrCreateDefaultArrayBufferAllocator() in the OH_JSVM_CreateVM function, competitions may occur during the creation of array_buffer_allocator if multiple threads call the OH_JSVM_CreateVM function at the same time, resulting in memory exceptions.
To conclude, the allocator object is empty because the OH_JSVM_CreateVM function is called by multiple threads when array_buffer_allocator is created. As a result, a null pointer exception occurs in the AllocateExternalBackingStore function.
Solution
Add a lock on the application to ensure that OH_JSVM_CreateVM can only be called by one thread at a time. The sample code is as follows:
// Create an instance of JSVM.
const JSVM_CreateVMOptions* options = new JSVM_CreateVMOptions();
JSVM_Status res = USVM_OK;
{
std::Lock_guard<std::mutex> Lock(create_jsym_mutex_);
res = OH_JSVM_CreateVM(options, &vm_);
}
if (res != JSVM_OK vm_ == nullptr) {
XLOG(ERROR) << "JSVM create vm failed";
}
// When we start, open vm scope.