Search code examples
cjvmjava-native-interfacememset

JNI: call to a function where memset happens causes jvm-fetch to crash in the second call


This is the function which first does some memset operations and then calls the function which basically create/ fetch JVM and then communicate with the JVM.

int
acCreateHeader(acInputParms *acCreate, acReturnParms *acRet3){

   int     Debug;
    int     rc;
    acInputParms        acInp;
    kc0InputParms       kc0Inp;
    kc0ReturnParms      kc0Ret3;

    initACReturnParms(acRet3);

    rc = invokeKc000(kc0Inp, &kc0Ret3); // JNI Operations

    return rc;
}

The expansion of initACReturnParms(acRet3);is below:

void initACReturnParms (acReturnParms *in) {

    memset(in->msgId,0,sizeof(in->msgId));
    memset(in->msgText,0,sizeof(in->msgText));
    memset(in->returnHdrTrl,0,sizeof(in->returnHdrTrl));
    memset(in->returnPaySrc,0,sizeof(in->returnPaySrc));
    memset(in->returnPayLoc,0,sizeof(in->returnPayLoc));
    return;
}

So, when I execute the whole program, the first JNI operation happens just fine but the logic of the program is built in such a way that the function acCreateHeader is called twice, and thus the program freezes at the function (**jvm)->GetEnv(*jvm, (void**)&env, JNI_VERSION_1_4); (which basically tries to fetch the already created JVM) in the second call.

Sorry, it is almost impossible to paste all of my codes here as the program is very large.

But the catch is here:

when I copy all logic of the function initACReturnParms and paste it into the function acCreateHeader by commenting the call of initACReturnParms (shown below), the program doesn't crash at all, even after second JNI operation and the whole program works as I expect.

int
acCreateHeader(acInputParms *acCreate, acReturnParms *acRet3){

   int     Debug;
    int     rc;
    acInputParms        acInp;
    kc0InputParms       kc0Inp;
    kc0ReturnParms      kc0Ret3;

    memset(acRet3->msgId,0,sizeof(acRet3->msgId));
    memset(acRet3->msgText,0,sizeof(acRet3->msgText));
    memset(acRet3->returnHdrTrl,0,sizeof(acRet3->returnHdrTrl));
    memset(acRet3->returnPaySrc,0,sizeof(acRet3->returnPaySrc));
    memset(acRet3->returnPayLoc,0,sizeof(acRet3->returnPayLoc));
    //initACReturnParms(acRet3);

    rc = invokeKc000(kc0Inp, &kc0Ret3); // JNI Operations

    return rc;
}

So, why is this function initACReturnParms call the culprit here ? Please help me to understand.

Please let me know if I you want me to provide more information/ code.

UPDATE:

acRet3 is a local variable in the function acCreateHeader and is passed as an argument from the below function:

void writeLSHeader() {

  int rc;

  fillHdrTrl();

  rc=acCreateHeader(&acCreate,&acRet);

  //rest of the codes
}

And acRet is initialized in the below shown function:

int fillHdrTrl() {
    //Codes here
    memset(&acRet,0,sizeof(acReturnParms));
    memset(&acRet.returnHdrTrl[0],' ',AC_HDR_TRL_SZ);
    //Codes here
}

acRet is though a global variable and the memory is allocated on the stack:

acReturnParms acRet;

And acReturnParms looks like this:

struct acReturnParms {
    char msgId[9];
    char msgText[61];
    char returnHdrTrl[351];
    char returnPaySrc[9];
    char returnPayLoc[3];
}

And the macro used above appears in the header file like this:

#define AC_HDR_TRL_SZ            350

UPDATE 2

Here is the codes of JNI operations, which might be the reason for this undefined behavior:

#include "jni_funct.h"
#include<stdio.h>
#include<string.h>
#include<stdlib.h>

int invokeKc000(kc0InputParms kc0inp, kc0ReturnParms *kc0ret)
{
    JavaVM *jvm;
    JNIEnv *env;
    kc0InputParms input;
    kc0ReturnParms *kc000out;
    char *kc910Path, *kc910Lib;
    int retcode;
    retcode = 8;

    kc910Path = getenv("KC910_BIN");
    kc910Lib = getenv("KC910_LIB");
    if(kc910Path == NULL || strlen(kc910Path) <= 0){
        return -1;
    }

    if(kc910Lib == NULL || strlen(kc910Lib) <= 0){
        return -1;
    }
    env = createKc000Vm(&jvm, kc910Path, kc910Lib);
    if(env == NULL){
        printf("\n JNIEnv is NULL");
        return -2;
    }
    input = kc0inp;
    kc000out = kc0ret;
    communicateKc000(env, input, &kc000out, &retcode);
    return retcode;
}

JNIEnv* createKc000Vm(JavaVM **jvm, char *kc910Path, char *kc910lib)
{
        JNIEnv* env;
        JavaVMInitArgs args;
        JavaVMOption options;
        args.version = JNI_VERSION_1_4;
        args.nOptions = 1;
        char *classPath = "shared/misc/kc000";
        char *javaClassPathOption = "-Djava.class.path";
        char javaOptionString[2000];
        char kc910Lib[1000], kc910Bin[1000];

        memset(kc910Lib, '\0', sizeof(kc910Lib));
        memset(kc910Bin, '\0', sizeof(kc910Bin));
        memset(javaOptionString, '\0', sizeof(javaOptionString));
        strncpy(javaOptionString, javaClassPathOption, strlen(javaClassPathOption));
        strncpy(kc910Lib, kc910lib, strlen(kc910lib));
        strncpy(kc910Bin, kc910Path, strlen(kc910Path));
        strcat(javaOptionString, "=");
        strcat(javaOptionString, kc910Lib);
        strcat(javaOptionString, "/com.ibm.ws.ejb.thinclient_8.5.0.jar:");
        strcat(javaOptionString, kc910Lib);
        strcat(javaOptionString, "/com.ibm.ws.runtime.jar:");
        strcat(javaOptionString, kc910Lib);
        strcat(javaOptionString, "/com.ibm.ws.orb_8.5.0.jar:");
        strcat(javaOptionString, kc910Lib);
        strcat(javaOptionString, "/ojdbc6.jar:");
        strcat(javaOptionString, kc910Lib);
        strcat(javaOptionString, "/providerutil.jar:");
        strcat(javaOptionString, kc910Lib);
        strcat(javaOptionString, "/j2ee.jar:");
        strcat(javaOptionString, kc910Lib);
        strcat(javaOptionString, "/src.jar:");
        strcat(javaOptionString, kc910Lib);
        strcat(javaOptionString, "/junit.jar:");
        strcat(javaOptionString, kc910Lib);
        strcat(javaOptionString, "/ldap.jar:");
        strcat(javaOptionString, classPath);
        strcat(javaOptionString, ":");
        strcat(javaOptionString, kc910Path);
        strcat(javaOptionString, "/C2J2.jar");

        options.optionString = javaOptionString;
        args.options = &options;
        args.ignoreUnrecognized = 0;
        int rv;
        jsize buf, num;
        rv = JNI_CreateJavaVM(jvm, (void**)&env, &args);
        if (rv < 0 || !env){
                printf("\nUnable to launch KC000 JVM %d. Perhaps you are attempting to create JVM. Attempting to fetch existing JVM, if running",rv);
                JNI_GetCreatedJavaVMs(jvm, buf, &num);
                (**jvm)->GetEnv(*jvm, (void**)&env, JNI_VERSION_1_4);
        }
        else
                printf("\nLaunched kc000 JVM!");

        return env;
}

void communicateKc000(JNIEnv* env, kc0InputParms in, kc0ReturnParms **kc000outparms, int *retcode)
{
    jclass init_class;
    jmethodID init_method;
    jstring inString, outString;
    kc0ReturnParms **kc000out;
    char returnCodeString[9];
    int flag = 8, i = 0;
    kc000out = kc000outparms;
    inString = (*env)->NewStringUTF(env, in.inStream);

    init_class = (*env)->FindClass(env, "shared/misc/kc000/Kc000");
    init_method = (*env)->GetStaticMethodID(env, init_class, "init", "(Ljava/lang/String;)Ljava/lang/String;");
    outString = (*env)->CallStaticObjectMethod(env, init_class, init_method, inString);
    const char *rawString = (*env)->GetStringUTFChars(env, outString, 0);
    jclass newExcCls = (*env)->FindClass(env, "java/lang/Throwable");
    if(newExcCls == NULL)
    {
        /*printf("Exception not found\n");*/
    }
    else
    {
        (*env)->ExceptionDescribe(env);
    }   
    strcpy((*kc000out)->inStream, rawString);
    memset(returnCodeString, '\0', sizeof(returnCodeString));
    strncpy(returnCodeString, rawString, 8);
    printf("KC000 return code: [%s]\n", returnCodeString);
    if(strlen(returnCodeString) > 0){
        for(i = 0; i < 8; i++){
            if(returnCodeString[i] == ' '){
                flag = 0;
            }
            else{
                flag = 8;
            }
        }
        *retcode = flag;
    }
    else{
        printf("Nothing returned in the return code of KC000. Setting this as an error.\n");
        *retcode = 8;
    }
}

Solution

  • I found the answer to my question myself. Here is what I did to get rid of this undefined behavior:

    #include "jni_funct.h"
    #include<stdio.h>
    #include<string.h>
    #include<stdlib.h>
    
    JavaVM *jvm;
    JNIEnv *env;
    
    int invokeKc000(kc0InputParms kc0inp, kc0ReturnParms *kc0ret)
    {
        //JavaVM *jvm;
        //JNIEnv *env;
        kc0InputParms input;
        kc0ReturnParms *kc000out;
        char *kc910Path, *kc910Lib;
        int retcode;
        retcode = 8;
    
        //Rest of the codes
    

    I made JNIEnv and JavaVM as global variables instead of local.

    I still didn't understand why allocation of stack was causing this undefined behavior while allocation of data segment caused no such issue. I will post an another question regarding this change.

    Thanks @Martin Zabel for the hint though.