jni-wrapper crashes JVM on Windows - c

I have written a JNI-Wrapper but used it till today only on Linux.
Now I compiled it for windows and the JVM crashes in this native methods:
If I remove the ReleaseStringUTFChars and free calls it also works on Windows, but It would be interesting why this works in linux but not in windows?(I use Windows 10-64bit). And from my experience this calls are mandatory, got some memory-leaks(on linux) at the beginning before I released them correctly
void onMessageReceived(char* topic, char* timestamp, char* id, char* value) {
JNIEnv * g_env;
int getEnvStat = (*g_vm)->GetEnv(g_vm, (void **) &g_env, JNI_VERSION_1_8);
if (getEnvStat == JNI_EDETACHED) {
if ((*g_vm)->AttachCurrentThread(g_vm, (void **) &g_env, NULL) != 0) {
puts("Failed to attach");
fflush(stdout);
}
}
if (methodHandleMessage) {
} else {
jclass clazz = (*g_env)->GetObjectClass(g_env, store_callback);
methodHandleMessage = (*g_env)->GetMethodID(g_env, clazz, "handleMessage", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
}
jstring j_topic = (*g_env)->NewStringUTF(g_env, topic);
jstring j_timestamp = (*g_env)->NewStringUTF(g_env, timestamp);
jstring j_value = (*g_env)->NewStringUTF(g_env, value);
jstring j_id = (*g_env)->NewStringUTF(g_env, id);
(*g_env)->CallVoidMethod(g_env, store_callback, methodHandleMessage, j_topic, j_timestamp, j_id, j_value);
//HERE IS THE PROBLEM:
(*g_env)->ReleaseStringUTFChars(g_env, j_topic, topic);
(*g_env)->ReleaseStringUTFChars(g_env, j_timestamp, timestamp);
(*g_env)->ReleaseStringUTFChars(g_env, j_value, value);
(*g_env)->ReleaseStringUTFChars(g_env, j_id, id);
//
(*g_vm)->DetachCurrentThread(g_vm);
}
and
void rep_actor(zsock_t *pipe, void *args) {
zsock_signal(pipe, 0);
while (!zsys_interrupted) {
char* timestamp;
char* sender;
char* command;
char* message;
zsock_recv(reply, "ssss", &timestamp, &sender, &command, &message);
char* result = onRequestReceived(timestamp, sender, command, message);
zsock_send(reply, "s", result);
//HERE IS THE PROBLEM:
free(timestamp);
free(sender);
free(command);
free(message);
free(result);
//
}
}

The error happens because you free memory twice. First time - by calling ReleaseStringUTFChars() inside of onMessageReceived(), second - outside of onMessageReceived() you call free() on every released pointer.
Important
You don't need release memory manually for objects created inside of java enviroment (calling of env->New*() methods). In your case:
// j_topic - it's a pointer for object inside of java heap
// garbage collector will free this memory
jstring j_topic = env->NewStringUTF(topic);
Call of ReleaseStringUTFChars() is inapropriate here. This used only when you create native string from java String using GetStringUTFChars():
// j_topic - string inside of java heap
const char* native_str = env->GetStringUTFChars(j_topic, isCopy);
// your logic for native_str array
// informs the VM that the native code no longer needs access to native_str
env->ReleaseStringUTFChars(j_topic, native_str);
Look at javadoc for methods GetStringUTFChars() and ReleaseStringUTFChars() - you should use they only together.

Related

Why does this JNI call segfault on Windows only?

I have some C code that adds strings to a Java array using JNI. The call to NewStringUTF segfaults -- but only on Windows 7 32-bit (in a VirtualBox VM, which is all I have to test on). In some cases, it makes it to SetObjectArrayElement call and then segfaults.
void launch_jvm_in_proc(mrb_state *mrb, CreateJavaVM_t *createJavaVM, const char *java_main_class, const char **java_opts, int java_optsc, const char **v, int prgm_optsc) {
int i;
JavaVM *jvm;
JNIEnv *env;
//...
jclass j_class_string = (*env)->FindClass(env, "java/lang/String");
jstring j_string_arg = (*env)->NewStringUTF(env, "");
jobjectArray main_args = (*env)->NewObjectArray(env, prgm_optsc, j_class_string, j_string_arg);
for (i = 0; i < prgm_optsc; i++) {
j_string_arg = (*env)->NewStringUTF(env, (char *) prgm_opts[i]);
if (!j_string_arg) {
mrb_raise(mrb, E_ARGUMENT_ERROR, "NewStringUTF() failed");
}
(*env)->SetObjectArrayElement(env, main_args, i, j_string_arg);
}
//...
}
There are also cases where the call is made to SetObjectArrayElement successfully, and then it consistently fails on the third iteration of the loop (when i=2). This happens when I consume this project a library in mjruby. I can't explain that either.
The complete project is on Github in mruby-jvm.
Error Details:
Problem signature:
Problem Event Name: APPCRASH
Application Name: mruby-jvm.exe
Application Version: 0.0.0.0
Application Timestamp: 55eb01a5
Fault Module Name: mruby-jvm.exe
Fault Module Version: 0.0.0.0
Fault Module Timestamp: 55eb01a5
Exception Code: c0000005
Exception Offset: 0003fff2
OS Version: 6.1.7601.2.1.0.256.4
Locale ID: 1033
Additional Information 1: 0a9e
Additional Information 2: 0a9e372d3b4ad19135b953a78882e789
Additional Information 3: 0a9e
Additional Information 4: 0a9e372d3b4ad19135b953a78882e789
Is there a way to collect more information on the error?
It works perfectly on Linux and Mac.
I've included instructions on how to reproduce the problem in this Github issue.
EDIT
I should clarify that I've examined this every way I can. I've checked that the various args are not NULL. I even get condensed almost the entire program to this:
static void
jvm_wtf(const char *java_dl, const char *jli_dl) {
JavaVM *jvm;
JNIEnv *env;
JavaVMInitArgs jvm_init_args;
CreateJavaVM_t* createJavaVM = NULL;
jvm_init_args.nOptions = 0;
jvm_init_args.version = JNI_VERSION_1_4;
jvm_init_args.ignoreUnrecognized = JNI_FALSE;
#if defined(_WIN32) || defined(_WIN64)
disable_folder_virtualization(GetCurrentProcess());
HMODULE jvmdll = LoadLibrary(java_dl);
createJavaVM = (CreateJavaVM_t*) GetProcAddress(jvmdll, "JNI_CreateJavaVM");
#elif defined(__APPLE__)
// jli needs to be loaded on OSX because otherwise the OS tries to run the system Java
void *libjli = dlopen(jli_dl, RTLD_NOW + RTLD_GLOBAL);
void *libjvm = dlopen(java_dl, RTLD_NOW + RTLD_GLOBAL);
createJavaVM = (CreateJavaVM_t*) dlsym(libjvm, "JNI_CreateJavaVM");
#else
void *libjvm = dlopen(java_dl, RTLD_NOW + RTLD_GLOBAL);
createJavaVM = (CreateJavaVM_t*) dlsym(libjvm, "JNI_CreateJavaVM");
#endif
printf("Begining\n");
createJavaVM(&jvm, (void**)&env, &jvm_init_args);
jclass main_class = (*env)->FindClass(env, "Main");
jmethodID main_method = (*env)->GetStaticMethodID(env, main_class, "main", "([Ljava/lang/String;)V");
jclass j_class_string = (*env)->FindClass(env, "java/lang/String");
jstring j_string_arg = (*env)->NewStringUTF(env, "");
printf("Checking for NULL\n");
if (!createJavaVM) { printf("createJavaVM is NULL\n");}
if (!main_class) { printf("main_class is NULL\n");}
if (!main_method) { printf("main_method is NULL\n");}
if (!j_class_string) { printf("j_class_string is NULL\n");}
if (!j_string_arg) { printf("j_string_arg is NULL\n");}
printf("Right before segfault\n");
jobjectArray main_args = (*env)->NewObjectArray(env, 1, j_class_string, j_string_arg);
printf("It won't get here\n");
(*env)->SetObjectArrayElement(env, main_args, 0, (*env)->NewStringUTF(env, "1"));
(*env)->CallStaticVoidMethod(env, main_class, main_method, main_args);
}
Now I get a segfault at NewObjectArray. Some googling has led me to believe that this may result from Windows terminating the program because it thinks the memory allocation by the JVM is malicious. How would I determine if this is true?
I have no idea why, but declaring this variable before the LoadLibrary call fixes the problem.
char stupid_var_that_means_nothing_but_makes_windows_work_i_dont_even[MAX_PATH];
HMODULE jvmdll = LoadLibrary(java_dl);
Commenting that line out causes the problem to start happening again. I've also tried adjusting it (changing the value in []) to no avail. I am completely stumped. I stumbled on this by accident after trying to add some code from jruby-launcher
Here is the full implementation of my JNI code.
I hate computers.

char* value changes after function return

Didn't find similar question, so opening a new one.
I'm trying to pass a Java object to JNI layer in my program and extract Java String field in it. The extracted field is set in the C struct.
I'm seeing a strange behavior. I'm able to extract the required fields successfully and memcpy it in the struct field. But when my utility function returns, the field set in struct get corrupted.
I suspected a memory leak and rechecked, but nothing looks suspicious (I'm not doing much dynamic memory stuff code..just one malloc and free.)
Here goes the code:
Header File:
typedef struct Job {
char* job_id;
} Job;
C File:
JNIEXPORT jint JNICALL Java_com.test.JobHandler__1submitJob(
JNIEnv *env, jobject this, jobject job) {
current_job = (Job *) malloc(sizeof(Job));
convert_job(env, job, current_job);
free(current_job);
}
void convert_job(JNIEnv *env, jobject javajob, Job *job) {
jclass cls = (*env)->GetObjectClass(env, javajob);
getVoidStringField(env, cls, javajob, job->job_id, "getJob_id");
//job->job_id gets corrupted here
}
void getVoidStringField(JNIEnv *env, jclass cls, jobject obj, char *jobStr, char *methodName) {
jmethodID mid = (*env)->GetMethodID(env, cls, methodName, "()Ljava/lang/String;");
jobject js = (*env)->CallObjectMethod(env, obj, mid);
const char *str = (*env)->GetStringUTFChars(env, js, 0);
int len = (*env)->GetStringUTFLength(env, js);
jobStr = (char*) malloc(len);
memcpy(jobStr, str, len);
jobStr[len] = '\0';
(*env)->ReleaseStringUTFChars(env, js, str);
//jobStr is fine till here.
}
I removed other stuff from my code and reduced to above version to simply debugging, still same issue.
If I pass modify getVoidStringField(...) a bit to accept Job object and then work on job->job_id, it works fine.
Curious to know. I doubt it has something to do with JNI.
Couple of things :
When you pass in job->job_id, you are passing in the VALUE of that pointer, not the pointer itself. That means when you do the jobStr = (char*) malloc(len);, you are not changing job->job_id, but just that function's local variable.
So, what you need to pass in is &(job->job_id), and the parameter should be char **jobstr
Your malloc would then be (not forgetting to allow for the null terminator):
*jobStr = (char*) malloc(len +1);
and, of course, the following statements should refer to *jobstr

Why is it evbuffer_add_printf will only accept static variables and not "dynamic" ones?

So far I have gotten my libev code to successfully return a static sting that says "OMP OMP", however when I write a function that returns a "static" string it never seems to work. (Sidenote: the idea is to turn that same function into a dynamic response but just for agile testing purposes I need this to work first). My code for the libev read callback is as the following...
void p2pserver_network_buf_read_callback(struct bufferevent *incoming, void *arg){
//Define function local variables
struct evbuffer *evreturn;
char *req;
//Begin function local logic
req = evbuffer_readline(incoming->input);
if (req == NULL){
return;
}
char *response;
parse_json_command(req, response);
//response = "OMP OMP";
g_print("PARSED");
evreturn = evbuffer_new();
evbuffer_add_printf(evreturn, "%s", response);
bufferevent_write_buffer(incoming,evreturn);
evbuffer_free(evreturn);
free(req);
g_print("%s", response);
}
The parse_json_command function is as the following...
void parse_json_command(char json_command, char *response){
//Define Local Variables
g_print("PARSING");
response = "YOU KNOW";
//Print out the recieved message....
//g_message("%s", json_command);
/**
* TODO: check if the JSON is valid before parsing
* to prevent "Segmentation Defaults"
* and its good sanity checks.
**/
//Parse JSON incomming
/*json_object * jobj = json_tokener_parse(json_command);
enum json_type type;
json_object_object_foreach(jobj, key, val){
g_print("%s\n", key);
if(g_utf8_collate(key, "cmd") >= 0){
//Looks like the user has sent a "cmd" (command), lets analyze the "val" (value) of that command to see what the caller/client needs to be attending to...
//Is the client requesting an "Identity Update" (Pings server: if this is the first time ping, the server and client will exachange keys if the relationship exists the server just accepts the encrypted "ping" packet update)
type = json_object_get_type(val);
if(type == json_type_string){
char* cmd_value;
cmd_value = json_object_get_string(val);
//g_print("VALUE:%d\n", g_utf8_collate(cmd_value, "identupdate"));
if(g_utf8_collate(cmd_value, "identupdate") == 0){
//Call "Identity Update Response"
//char return_response = p2pserver_json_identupdate_response(json_command);
}
}
}
}
*/
return;
}
If you want to see the complete code (only a couple of pages big at the time of this writing) you can go to the source code at the following link: https://github.com/Xenland/P2PCrypt-Server
Thanks for your time and help!
c passes arguments by value, not by reference. You problem is here:
void parse_json_command(char json_command, char *response){
[...]
response = "YOU KNOW";
[...]
}
char *response;
parse_json_command(req, response);
response is a uninitialized pointer to a string. You are assigning a pointer to a static string to the response pointer in the function, but that does not modify response outside of the function, it just changes response within the function. There are different ways to fix this. Probably the easiest one for a quick fix would be to change the function's prototype to return a char * instead of void:
char * parse_json_command(char json_command){
char *response;
[...]
response = "YOU KNOW";
[...]
return response;
}
char *response;
response = parse_json_command(req);
Also, the json_command argument should probably be a char * or const char *, not just a single char, if you want to pass more than a single byte there.

jni callback works for java types, but not c types

I have followed the advice at
registering java function as a callback in C function and can callback with "simple" types such as integer and string, e.g.:
jstring js = (*env)->NewStringUTF(env, "hello");
(*env)->CallStaticVoidMethod(env, cls, methodid, js);
However, if I am trying to do the same with C datatypes which have been wrapped with SWIG, I am only getting null pointers in Java. In the C part they are definitely not 0. Do they need to be treated differently?
[EDIT:]
Some more information:
As stated above, char*/string is working for me as well. I am looking for a solution for C struct's, which have been wrapped by SWIG and have been allocated in Java.
E.g.:
typedef struct {
unsigned short length;
unsigned short value;
} lv_t;
is getting wrapped by SWIG, so I can use it in Java:
lv_t lv;
lv = modulename.modulename_new_lv();
lv.setLength(1);
lv.setValue(2);
Then I will give this struct from Java to C:
modulename.send(lv);
C will send it over the network, receive some reply and change the values in lv. Now, this should give the modified lv back to Java.
void jni_call_received_hook(lv_t* lv){
JNIEnv* m_env;
(*m_vm)->AttachCurrentThread(m_vm, (void**) &m_env, NULL );
jclass cls = (*m_env)->FindClass( m_env, "gui/StateMachine" );
jmethodID mid = (*m_env)->GetStaticMethodID(m_env, cls, "callReceivedEvent", "(Lcom/something/modulename/jni/lv_t;)V");
if (mid == 0){
log(E, "Unable to find method for callback");
return;
}
// what to do here to create a jobject?
jobject lv_j = ...;
(*m_env)->CallStaticVoidMethod(m_env, cls, mid, lv_j);
}
Which calls:
public static void messageHandler(lv_t lv) {
System.out.println("messageHandler().");
System.out.println("lv " + lv);
}
Sorry but I'm not able to comment in your question yet, so this is more a comment rather than an answer. Anyway, I've recently done something like that.
My callback works and is implemented as:
void jni_call_received_hook(char* username){
JNIEnv* m_env;
(*m_vm)->AttachCurrentThread(m_vm, (void**) &m_env, NULL );
jclass cls = (*m_env)->FindClass( m_env, "gui/StateMachine" );
jmethodID mid = (*m_env)->GetStaticMethodID(m_env, cls, "callReceivedEvent", "(Ljava/lang/String;)V");
if (mid == 0){
log(E, "Unable to find method for callback");
return;
}
(*m_env)->CallStaticVoidMethod(m_env, cls, mid, (*m_env)->NewStringUTF(m_env, username));
}
The variable m_vm is an instance of the JVM I've kept upon calling a method that registered this callback, like this:
JNIEXPORT void JNICALL Java_gui_StateMachine_setCallReceivedCallback(JNIEnv *e, jobject o){
(*e)->GetJavaVM(e, &m_vm );
set_call_received_hook(jni_call_received_hook);
}
Maybe your missing something. Let me know if this isn't clear enough. Hope it helps.

Preventing too many LoadLibrary/FreeLibrary

I'm writing a proxy library (called Library A) that is just an interface to another DLL (called Library B) that may be present or not on the system.
The idea is that a program would link to this library A instead of the original library B ; and Library A would handle the errors if Library B wasn't installed on the system.
So a typical proxy function would look like this:
int function(int arg1, int arg2)
{
HINSTANCE hinstLib;
UINT errormode = SetErrorMode(SEM_FAILCRITICALERRORS);
SetErrorMode(errormode | SEM_FAILCRITICALERRORS);
hinstLib = LoadLibrary(TEXT(ORIGINAL_DLL));
SetErrorMode (errormode);
if (hinstLib != NULL)
{
ProcAdd = (void *) GetProcAddress(hinstLib, TEXT("function"));
if (NULL != ProcAdd)
{
return (ProcAdd) (arg1, arg2);
}
FreeLibrary(hinstLib);
}
return ERROR;
}
Now, I would do this for all original entries in Library B. There could be a lot of calls to it.
So loading / unloading the DLL so frequently is certainly going to have an impact speed-wise.
I was wondering if it would be acceptable to use a global variable hinstLib ; something like
HINSTANCE hinstLib = LoadLibrary(TEXT(ORIGINAL_DLL));
int function(int arg1, int arg2)
{
if (hinstLib != NULL)
{
ProcAdd = (void *) GetProcAddress(hinstLib, TEXT("function"));
if (NULL != ProcAdd)
{
return (ProcAdd) (arg1, arg2);
}
}
return ERROR;
}
And let Windows automatically unload the DLL when the program exits (assuming it does unload it).
Thanks in advance for your wise remarks...
Jean-Yves
Unless the unloading is particular use case, then this is fine. However I would implement some thread safety to ensure that there are no race conditions through this code using a Critical Section.
This is simillar example to the one from the wikipedia article
/* Sample C/C++, Windows, link to kernel32.dll */
#include <windows.h>
static CRITICAL_SECTION cs;
static HINSTANCE hinstLib = LoadLibrary(TEXT(ORIGINAL_DLL));
/* Initialize the critical section before entering multi-threaded context. */
InitializeCriticalSection(&cs);
void f() {
/* Enter the critical section -- other threads are locked out */
__try {
EnterCriticalSection(&cs);
if (hinstLib != NULL) {
ProcAdd = (void *) GetProcAddress(hinstLib, TEXT("function"));
} __finally {
LeaveCriticalSection(&cs);
}
if (NULL != ProcAdd) {
return (ProcAdd) (arg1, arg2);
}
}
.
.
.
/* Release system object when all finished -- usually at the end of the cleanup code */
DeleteCriticalSection(&cs);

Resources