char* value changes after function return - c

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

Related

jni-wrapper crashes JVM on Windows

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.

jstring comparison in .c language what to do [duplicate]

This question already has an answer here:
Jstring comparison in.c language
(1 answer)
Closed 6 years ago.
How to compare two jstrings in c language.I need this for jni execution in Which the logic is written in .c file for comparing two names
** EDIT **
per #JonasVautherin comment:
While not causing a bug, this is one of the rare cases where use of strncmp can be replaced with strcmp without fear of reading out of bounds of the string.
The reason for that is native strings fetched from jvm are valid null terminated strings so no overflow will occur in this case, which is what strncmp guarantees.
This is also the reason why strncmp in this case will not cause a bug - it will terminate on \0 on either strings, or until the number of characters read is reached (reference: https://www.cplusplus.com/reference/cstring/strncmp/)
If you want to use pure native:
JNIEXPORT void JNICALL Java_ClassName_MethodName(JNIEnv *env, jobject obj, jstring javaString1, jstring javaString2)
{
const char *nativeString1 = (*env)->GetStringUTFChars(env, javaString1, 0);
const char *nativeString2 = (*env)->GetStringUTFChars(env, javaString2, 0);
/* Now you can compare nativeString1 with nativeString2*/
int res = strncmp(nativeString1, nativeString2, strlen(nativeString1);
/* And don't forget to release the handles */
(*env)->ReleaseStringUTFChars(env, javaString1, nativeString1);
(*env)->ReleaseStringUTFChars(env, javaString2, nativeString2);
}
If you want to use Java from native code (same answer as this one all credit goes to the original author):
JNIEXPORT void JNICALL Java_ClassName_MethodName(JNIEnv *env, jobject obj, jstring javaString1, jstring javaString2)
{
/* Get java class String handle */
jclass cls = (*env)->GetObjectClass(env, javaString1);
/* Get method ID equals from String handle */
jmethodID mID = (*env)->GetMethodID(env, cls, "equals", "(Ljava/lang/Object;)Z");
/* Compare both methods, store in jboolean which can be case to uint8_t or bool if you're using C99*/
jboolean equals = (*env)->CallBooleanMethod(env, javaString1, mID, javaString2);
}

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.

Bad arguments for libopus create encoder

currently I'm trying to build JNI-bindings for libopus. My current problem ist that I have a C-method like this:
JNIEXPORT jobject JNICALL
Java_de_akuz_android_libopus_OpusCodecFactory_encodercreate(JNIEnv * env, jint samplingRate, jint channels, jint application)
{
jclass resultClass = (*env)->FindClass(env,"de/akuz/android/libopus/OpusFactoryResult");
jobject result = (*env)->AllocObject(env,resultClass);
jfieldID pointerFieldID = (*env)->GetFieldID(env, resultClass,"pointer","J");
jfieldID errorFieldID = (*env)->GetFieldID(env, resultClass,"errorCode","I");
int error;
OpusEncoder* encoder = opus_encoder_create(samplingRate, channels, application, &error);
char buf[100];
sprintf(buf, "Pointer address: %p", encoder); // puts string into buffer
__android_log_print(ANDROID_LOG_INFO, "OpusNative", buf);
(*env)->SetLongField(env, result, pointerFieldID, encoder);
(*env)->SetIntField(env, result, errorFieldID, error);
return result;
}
When I call this method from JNI with the values 48000 for sample rate, 2 for channels and 2049 as id for application (taken from opus defines) a always get the error code -1 and null pointer back. I already tried casting the sample rate to an opus_int32 but it didn't change anything. Also I tried to pass the defined OPUS_APPLICATION_AUDIO directly instead if its integer value but it also didn't change anything. Unfortunately I'm more of a Java guy so I would appreciate any held on this problem(s) in my C code.
I'm really sorry, but obviously I'm blind (or stupid). My method declaration is all wrong
Java_de_akuz_android_libopus_OpusCodecFactory_encodercreate(JNIEnv * env, jint samplingRate, jint channels, jint application)
I have the parameter for the JNI environment but not the parameter for the calling object. It seems that C isn't interested in the amount of arguments of the native method in the Java code or the type of the arguments. Because of the missing jobject after JNIEnv all my parameters were shifted by one with samplingRate having the value of the pointer for the missing jobject.
The correct method declaration would be
Java_de_akuz_android_libopus_OpusCodecFactory_encodercreate(JNIEnv * env, jobject object, jint samplingRate, jint channels, jint application)
I hope that anyone running into the same problem will find my answer useful.

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.

Resources