Skip to content

Commit

Permalink
Merge pull request #1091 from bugsnag/PLAT-5746/jni-handling
Browse files Browse the repository at this point in the history
Check internal JNI calls for pending exceptions and no-op
  • Loading branch information
fractalwrench authored Jan 28, 2021
2 parents f477853 + b661294 commit 773a06c
Show file tree
Hide file tree
Showing 8 changed files with 286 additions and 83 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

* Check internal JNI calls for pending exceptions and no-op
[#1088](https://github.com/bugsnag/bugsnag-android/pull/1088)
[#1091](https://github.com/bugsnag/bugsnag-android/pull/1091)

## 5.5.2 (2021-01-27)

Expand Down
42 changes: 21 additions & 21 deletions bugsnag-plugin-android-ndk/src/main/jni/bugsnag.c
Original file line number Diff line number Diff line change
Expand Up @@ -63,17 +63,6 @@ jfieldID bsg_parse_jseverity(JNIEnv *env, bugsnag_severity severity,
}
}

jbyteArray bsg_byte_ary_from_string(JNIEnv *env, char *text) {
if (text == NULL) {
return NULL;
}
size_t text_length = bsg_strlen(text);
jbyteArray jtext = (*env)->NewByteArray(env, text_length);
(*env)->SetByteArrayRegion(env, jtext, 0, text_length, (jbyte *)text);

return jtext;
}

void bsg_release_byte_ary(JNIEnv *env, jbyteArray array, char *original_text) {
if (array != NULL) {
(*env)->ReleaseByteArrayElements(env, array, (jbyte *)original_text,
Expand Down Expand Up @@ -123,28 +112,36 @@ void bugsnag_notify_env(JNIEnv *env, char *name, char *message,
return;
}

jobjectArray trace = (*env)->NewObjectArray(
env, (jsize)frame_count,
bsg_safe_find_class(env, "java/lang/StackTraceElement"), NULL);
// create StackTraceElement array
jobjectArray trace = bsg_safe_new_object_array(env, frame_count, trace_class);
if (trace == NULL) {
return;
}

for (int i = 0; i < frame_count; i++) {
bugsnag_stackframe frame = stacktrace[i];
jstring class = (*env)->NewStringUTF(env, "");
jstring filename = (*env)->NewStringUTF(env, frame.filename);

// create Java string objects for class/filename/method
jstring class = bsg_safe_new_string_utf(env, "");
if (class == NULL) {
continue;
}

jstring filename = bsg_safe_new_string_utf(env, frame.filename);
jstring method;
if (strlen(frame.method) == 0) {
char *frame_address = malloc(sizeof(char) * 32);
sprintf(frame_address, "0x%lx", (unsigned long)frame.frame_address);
method = (*env)->NewStringUTF(env, frame_address);
method = bsg_safe_new_string_utf(env, frame_address);
free(frame_address);
} else {
method = (*env)->NewStringUTF(env, frame.method);
method = bsg_safe_new_string_utf(env, frame.method);
}
jobject jframe =
(*env)->NewObject(env, trace_class, trace_constructor, class, method,
filename, frame.line_number);

(*env)->SetObjectArrayElement(env, trace, i, jframe);
bsg_safe_set_object_array_element(env, trace, i, jframe);
(*env)->DeleteLocalRef(env, filename);
(*env)->DeleteLocalRef(env, class);
(*env)->DeleteLocalRef(env, method);
Expand Down Expand Up @@ -190,8 +187,11 @@ void bugsnag_set_binary_arch(JNIEnv *env) {
return;
}

jstring arch = (*env)->NewStringUTF(env, bsg_binary_arch());
(*env)->CallStaticVoidMethod(env, interface_class, set_arch_method, arch);
// call NativeInterface.setBinaryArch()
jstring arch = bsg_safe_new_string_utf(env, bsg_binary_arch());
if (arch != NULL) {
(*env)->CallStaticVoidMethod(env, interface_class, set_arch_method, arch);
}
(*env)->DeleteLocalRef(env, arch);
(*env)->DeleteLocalRef(env, interface_class);
}
Expand Down
57 changes: 36 additions & 21 deletions bugsnag-plugin-android-ndk/src/main/jni/bugsnag_ndk.c
Original file line number Diff line number Diff line change
Expand Up @@ -148,45 +148,47 @@ Java_com_bugsnag_android_ndk_NativeBridge_deliverReportAtPath(
pthread_mutex_lock(&bsg_native_delivery_mutex);
const char *event_path = (*env)->GetStringUTFChars(env, _report_path, 0);
bugsnag_event *event = bsg_deserialize_event_from_file((char *)event_path);
jbyteArray jpayload = NULL;
jbyteArray jstage = NULL;
char *payload = NULL;

if (event != NULL) {
char *payload = bsg_serialize_event_to_json_string(event);
payload = bsg_serialize_event_to_json_string(event);
if (payload != NULL) {

// lookup com/bugsnag/android/NativeInterface
jclass interface_class =
bsg_safe_find_class(env, "com/bugsnag/android/NativeInterface");
if (interface_class == NULL) {
return;
goto exit;
}

// lookup NativeInterface.deliverReport()
jmethodID jdeliver_method = bsg_safe_get_static_method_id(
env, interface_class, "deliverReport", "([B[BLjava/lang/String;)V");
if (jdeliver_method == NULL) {
return;
goto exit;
}

size_t payload_length = bsg_strlen(payload);
jbyteArray jpayload = (*env)->NewByteArray(env, payload_length);
(*env)->SetByteArrayRegion(env, jpayload, 0, payload_length,
(jbyte *)payload);
// generate payload bytearray
jpayload = bsg_byte_ary_from_string(env, payload);
if (jpayload == NULL) {
goto exit;
}

size_t stage_length = bsg_strlen(event->app.release_stage);
jbyteArray jstage = (*env)->NewByteArray(env, stage_length);
(*env)->SetByteArrayRegion(env, jstage, 0, stage_length,
(jbyte *)event->app.release_stage);
// generate releaseStage bytearray
jstage = bsg_byte_ary_from_string(env, event->app.release_stage);
if (jstage == NULL) {
goto exit;
}

jstring japi_key = (*env)->NewStringUTF(env, event->api_key);
(*env)->CallStaticVoidMethod(env, interface_class, jdeliver_method,
jstage, jpayload, japi_key);
// call NativeInterface.deliverReport()
jstring japi_key = bsg_safe_new_string_utf(env, event->api_key);
if (japi_key != NULL) {
(*env)->CallStaticVoidMethod(env, interface_class, jdeliver_method,
jstage, jpayload, japi_key);
}
(*env)->DeleteLocalRef(env, japi_key);
(*env)->ReleaseByteArrayElements(env, jpayload, (jbyte *)payload,
0); // <-- frees payload
(*env)->ReleaseByteArrayElements(
env, jstage, (jbyte *)event->app.release_stage, JNI_COMMIT);
(*env)->DeleteLocalRef(env, jpayload);
(*env)->DeleteLocalRef(env, jstage);
} else {
BUGSNAG_LOG("Failed to serialize event as JSON: %s", event_path);
}
Expand All @@ -196,7 +198,20 @@ Java_com_bugsnag_android_ndk_NativeBridge_deliverReportAtPath(
}
remove(event_path);
(*env)->ReleaseStringUTFChars(env, _report_path, event_path);
pthread_mutex_unlock(&bsg_native_delivery_mutex);
goto exit;

exit:
pthread_mutex_unlock(&bsg_native_delivery_mutex);
if (jpayload != NULL) {
(*env)->ReleaseByteArrayElements(env, jpayload, (jbyte *)payload,
0); // <-- frees payload
}
if (jstage != NULL) {
(*env)->ReleaseByteArrayElements(
env, jstage, (jbyte *)event->app.release_stage, JNI_COMMIT);
}
(*env)->DeleteLocalRef(env, jpayload);
(*env)->DeleteLocalRef(env, jstage);
}

JNIEXPORT void JNICALL
Expand Down
67 changes: 48 additions & 19 deletions bugsnag-plugin-android-ndk/src/main/jni/metadata.c
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,12 @@ bsg_jni_cache *bsg_populate_jni_cache(JNIEnv *env) {

jobject bsg_get_map_value_obj(JNIEnv *env, bsg_jni_cache *jni_cache,
jobject map, const char *_key) {
jstring key = (*env)->NewStringUTF(env, _key);
// create Java string object for map key
jstring key = bsg_safe_new_string_utf(env, _key);
if (key == NULL) {
return NULL;
}

jobject obj =
(*env)->CallObjectMethod(env, map, jni_cache->hash_map_get, key);
(*env)->DeleteLocalRef(env, key);
Expand All @@ -267,8 +272,8 @@ long bsg_get_map_value_long(JNIEnv *env, bsg_jni_cache *jni_cache, jobject map,
jobject _value = bsg_get_map_value_obj(env, jni_cache, map, _key);

if (_value != NULL) {
long value = (long)(*env)->CallDoubleMethod(env, _value,
jni_cache->number_double_value);
long value = bsg_safe_call_double_method(env, _value,
jni_cache->number_double_value);
(*env)->DeleteLocalRef(env, _value);
return value;
}
Expand All @@ -280,8 +285,8 @@ float bsg_get_map_value_float(JNIEnv *env, bsg_jni_cache *jni_cache,
jobject _value = bsg_get_map_value_obj(env, jni_cache, map, _key);

if (_value != NULL) {
float value = (float)(*env)->CallFloatMethod(env, _value,
jni_cache->float_float_value);
float value =
bsg_safe_call_float_method(env, _value, jni_cache->float_float_value);
(*env)->DeleteLocalRef(env, _value);
return value;
}
Expand All @@ -294,7 +299,7 @@ int bsg_get_map_value_int(JNIEnv *env, bsg_jni_cache *jni_cache, jobject map,

if (_value != NULL) {
jint value =
(int)(*env)->CallIntMethod(env, _value, jni_cache->integer_int_value);
bsg_safe_call_int_method(env, _value, jni_cache->integer_int_value);
(*env)->DeleteLocalRef(env, _value);
return value;
}
Expand All @@ -304,23 +309,33 @@ int bsg_get_map_value_int(JNIEnv *env, bsg_jni_cache *jni_cache, jobject map,
bool bsg_get_map_value_bool(JNIEnv *env, bsg_jni_cache *jni_cache, jobject map,
const char *_key) {
jobject obj = bsg_get_map_value_obj(env, jni_cache, map, _key);
return (*env)->CallBooleanMethod(env, obj, jni_cache->boolean_bool_value);
return bsg_safe_call_boolean_method(env, obj, jni_cache->boolean_bool_value);
}

int bsg_populate_cpu_abi_from_map(JNIEnv *env, bsg_jni_cache *jni_cache,
jobject map, bsg_device_info *device) {
jstring key = (*env)->NewStringUTF(env, "cpuAbi");
// create Java string object for map key
jstring key = bsg_safe_new_string_utf(env, "cpuAbi");
if (key == NULL) {
return 0;
}

jobjectArray _value =
(*env)->CallObjectMethod(env, map, jni_cache->hash_map_get, key);
if (_value != NULL) {
int count = (*env)->GetArrayLength(env, _value);

// get the ABI as a Java string and copy it to bsg_device_info
for (int i = 0; i < count && i < sizeof(device->cpu_abi); i++) {
jstring abi_ = (jstring)((*env)->GetObjectArrayElement(env, _value, i));
char *abi = (char *)(*env)->GetStringUTFChars(env, abi_, 0);
jstring jabi = bsg_safe_get_object_array_element(env, _value, i);
if (jabi == NULL) {
break;
}

char *abi = (char *)(*env)->GetStringUTFChars(env, jabi, 0);
bsg_strncpy_safe(device->cpu_abi[i].value, abi,
sizeof(device->cpu_abi[i].value));
(*env)->ReleaseStringUTFChars(env, abi_, abi);
(*env)->ReleaseStringUTFChars(env, jabi, abi);
device->cpu_abi_count++;
}
(*env)->DeleteLocalRef(env, _value);
Expand All @@ -339,7 +354,12 @@ void bsg_populate_crumb_metadata(JNIEnv *env, bugsnag_breadcrumb *crumb,
return;
}

int map_size = (int)(*env)->CallIntMethod(env, metadata, jni_cache->map_size);
// get size of metadata map
jint map_size = bsg_safe_call_int_method(env, metadata, jni_cache->map_size);
if (map_size == -1) {
return;
}

jobject keyset =
(*env)->CallObjectMethod(env, metadata, jni_cache->map_key_set);
jobject keylist = (*env)->NewObject(
Expand Down Expand Up @@ -548,12 +568,14 @@ void bsg_populate_metadata_value(JNIEnv *env, bugsnag_metadata *dst,
bsg_jni_cache *jni_cache, char *section,
char *name, jobject _value) {
if ((*env)->IsInstanceOf(env, _value, jni_cache->number)) {
double value =
(*env)->CallDoubleMethod(env, _value, jni_cache->number_double_value);
// add a double metadata value
double value = bsg_safe_call_double_method(env, _value,
jni_cache->number_double_value);
bsg_add_metadata_value_double(dst, section, name, value);
} else if ((*env)->IsInstanceOf(env, _value, jni_cache->boolean)) {
bool value =
(*env)->CallBooleanMethod(env, _value, jni_cache->boolean_bool_value);
// add a boolean metadata value
bool value = bsg_safe_call_boolean_method(env, _value,
jni_cache->boolean_bool_value);
bsg_add_metadata_value_bool(dst, section, name, value);
} else if ((*env)->IsInstanceOf(env, _value, jni_cache->string)) {
char *value = (char *)(*env)->GetStringUTFChars(env, _value, 0);
Expand All @@ -573,19 +595,26 @@ void bsg_populate_metadata(JNIEnv *env, bugsnag_metadata *dst,
jni_cache->get_metadata);
}
if (metadata != NULL) {
int size = (int)(*env)->CallIntMethod(env, metadata, jni_cache->map_size);
int size = bsg_safe_call_int_method(env, metadata, jni_cache->map_size);
if (size == -1) {
return;
}
jobject keyset =
(*env)->CallObjectMethod(env, metadata, jni_cache->map_key_set);
jobject keylist = (*env)->NewObject(
env, jni_cache->arraylist, jni_cache->arraylist_init_with_obj, keyset);

for (int i = 0; i < size; i++) {
jstring _key = (*env)->CallObjectMethod(
env, keylist, jni_cache->arraylist_get, (jint)i);
char *section = (char *)(*env)->GetStringUTFChars(env, _key, 0);
jobject _section =
(*env)->CallObjectMethod(env, metadata, jni_cache->map_get, _key);
int section_size =
(int)(*env)->CallIntMethod(env, _section, jni_cache->map_size);
jint section_size =
bsg_safe_call_int_method(env, _section, jni_cache->map_size);
if (section_size == -1) {
break;
}
jobject section_keyset =
(*env)->CallObjectMethod(env, _section, jni_cache->map_key_set);
jobject section_keylist =
Expand Down
Loading

0 comments on commit 773a06c

Please sign in to comment.