joern icon indicating copy to clipboard operation
joern copied to clipboard

[Bug][JNI] Parse JNI functions in native layer

Open MC-box opened this issue 1 year ago • 7 comments

Describe the bug Fail to parse C code when a JNI function returns a non-JNI type

To Reproduce The C Code:

#include <string.h>

#include "engine.h"
#include "input.h"
#include "math.h"
#include "renderer.h"
JNIEXPORT void JNICALL Java_com_termux_x11_XrActivity_init(JNIEnv *env, jobject obj) {

    // Do not allow second initialization
    if (xr_initialized) {
        return;
    }

    // Set platform flags
    memset(&xr_engine, 0, sizeof(xr_engine));
    xr_engine.PlatformFlag[PLATFORM_CONTROLLER_QUEST] = true;
    xr_engine.PlatformFlag[PLATFORM_EXTENSION_PASSTHROUGH] = true;
    xr_engine.PlatformFlag[PLATFORM_EXTENSION_PERFORMANCE] = true;

    // Get Java VM
    JavaVM* vm;
    (*env)->GetJavaVM(env, &vm);

    // Init XR
    xrJava java;
    java.vm = vm;
    java.activity = (*env)->NewGlobalRef(env, obj);
    XrEngineInit(&xr_engine, &java, "termux-x11", 1);
    XrEngineEnter(&xr_engine);
    XrInputInit(&xr_engine, &xr_input);
    XrRendererInit(&xr_engine, &xr_renderer);
    XrRendererGetResolution(&xr_engine, &xr_renderer, &xr_params[4], &xr_params[5]);
    xr_initialized = true;
    ALOGV("Init called");
}

Steps to reproduce the behavior:

  1. Run joern: ./joern
  2. joern> importCode("path/to/c","name")
  3. joern> cpg.method.name.l

Expected behavior Joern should correctly recognize the function Java_com_termux_x11_XrActivity_init and list it in the output of cpg.method.name.l.

Output

val res3: List[String] = List("<global>", "<global>", "<operator>.assignment", "<operator>.arrayInitializer")

joern cannot recognize function: Java_com_termux_x11_XrActivity_init()

Desktop (please complete the following information):

  • Debian 5.16.18-1kali1
  • Joern Version: 4.0.67
  • Java Version: 17.0.10

Additional context Other non-JNI type such as primitive data types will get similar results

MC-box avatar Sep 14 '24 03:09 MC-box

You need to give c2cpg the path to the included files (--include <dir>) and/or you system header files location (--with-include-auto-discovery). Otherwise, definitions like JNICALL and such can not be resolved. That may lead to unparsable code.

max-leuthaeuser avatar Sep 14 '24 15:09 max-leuthaeuser

But joern can resolve JNI functions whose return value is JNI type without giving c2cpg any path to the included files:

#include <string.h>

#include "engine.h"
#include "input.h"
#include "math.h"
#include "renderer.h"

JNIEXPORT jfloatArray JNICALL Java_com_termux_x11_XrActivity_getAxes(JNIEnv *env, jobject obj) {
    XrPosef lPose = XrInputGetPose(&xr_input, 0);
    XrPosef rPose = XrInputGetPose(&xr_input, 1);
    XrVector2f lThumbstick = XrInputGetJoystickState(&xr_input, 0);
    XrVector2f rThumbstick = XrInputGetJoystickState(&xr_input, 1);
    XrVector3f lPosition = xr_renderer.Projections[0].pose.position;
    XrVector3f rPosition = xr_renderer.Projections[1].pose.position;
    XrVector3f angles = xr_renderer.HmdOrientation;

    int count = 0;
    float data[32];
    data[count++] = XrQuaternionfEulerAngles(lPose.orientation).x; //L_PITCH
    data[count++] = XrQuaternionfEulerAngles(lPose.orientation).y; //L_YAW
    data[count++] = XrQuaternionfEulerAngles(lPose.orientation).z; //L_ROLL
    data[count++] = lThumbstick.x; //L_THUMBSTICK_X
    data[count++] = lThumbstick.y; //L_THUMBSTICK_Y
    data[count++] = lPose.position.x; //L_X
    data[count++] = lPose.position.y; //L_Y
    data[count++] = lPose.position.z; //L_Z
    data[count++] = XrQuaternionfEulerAngles(rPose.orientation).x; //R_PITCH
    data[count++] = XrQuaternionfEulerAngles(rPose.orientation).y; //R_YAW
    data[count++] = XrQuaternionfEulerAngles(rPose.orientation).z; //R_ROLL
    data[count++] = rThumbstick.x; //R_THUMBSTICK_X
    data[count++] = rThumbstick.y; //R_THUMBSTICK_Y
    data[count++] = rPose.position.x; //R_X
    data[count++] = rPose.position.y; //R_Y
    data[count++] = rPose.position.z; //R_Z
    data[count++] = angles.x; //HMD_PITCH
    data[count++] = angles.y; //HMD_YAW
    data[count++] = angles.z; //HMD_ROLL
    data[count++] = (lPosition.x + rPosition.x) * 0.5f; //HMD_X
    data[count++] = (lPosition.y + rPosition.y) * 0.5f; //HMD_Y
    data[count++] = (lPosition.z + rPosition.z) * 0.5f; //HMD_Z
    data[count++] = XrVector3fDistance(lPosition, rPosition); //HMD_IPD

    jfloat values[count];
    memcpy(values, data, count * sizeof(float));
    jfloatArray output = (*env)->NewFloatArray(env, count);
    (*env)->SetFloatArrayRegion(env, output, (jsize)0, (jsize)count, values);
    return output;
}

Result:

val res1: List[String] = List(
  "<global>",
  "Java_com_termux_x11_XrActivity_getAxes",
  "<global>",
  "<operator>.assignment",
  "XrInputGetPose",
  "<operator>.addressOf",
  "XrInputGetJoystickState",
  "<operator>.fieldAccess",
  "<operator>.indirectIndexAccess",
  "<operator>.alloc",
  "<operator>.postIncrement",
  "XrQuaternionfEulerAngles",
  "<operator>.multiplication",
  "<operator>.addition",
  "XrVector3fDistance",
  "memcpy",
  "<operator>.sizeOf",
  "<operator>.pointerCall",
  "<operator>.indirectFieldAccess",
  "<operator>.indirection",
  "<operator>.cast"
)

So I suppose maybe it isn't an issue with the include-path?

MC-box avatar Sep 16 '24 06:09 MC-box

We simply use Eclipse CDT as a parser. If CDT is not able to parse it there is nothing we can do unfortunately. You could try a c2cpg standalone run with --log-problems to see these parse issues.

max-leuthaeuser avatar Sep 16 '24 06:09 max-leuthaeuser

I tried a c2cpg standalone run with --log-problems and found the following issue:

2024-09-18 22:42:25.906 INFO ParseProblemsLogger: Parse problem 'CASTProblem' occurred!
  Code: 'JNIEXPORT void JNICALL Java_com_termux_x11_XrActivity_teardown(JNIEnv *env, jobject obj) {
    if (!xr_initialized) {
        return;
    }

    XrRendererDestroy(&xr_engine, &xr_renderer);
    XrEngineLeave(&xr_engine);
    XrEngineDestroy(&xr_engine);

    memset(&xr_engine, 0, sizeof(xr_engine));
    memset(&xr_input, 0, sizeof(xr_input));
    memset(&xr_renderer, 0, sizeof(xr_renderer));
    xr_initialized = false;
    
}'
  File: '/home/kali/桌面/cfgtool/cfgtool/cfgtool_new/cfgtool_fix/c_termux-x11-master/android.c'
  Line: 8

It is too general to locate the bug. And I try to find information on 'CASTProblem' in joern's document but fail. I apologize for any inconvenience, but I would greatly appreciate it if you can provide some guidance.

MC-box avatar Sep 19 '24 02:09 MC-box

I am pretty sure the parser can't find the definitions for JNIEXPORT and JNICALL. What happens if you place these definitions directly into the file?

max-leuthaeuser avatar Sep 23 '24 08:09 max-leuthaeuser

I tried to place those into the file and it work! But is there a more elegant way to make Joern recognize these predefined elements?

MC-box avatar Jun 25 '25 02:06 MC-box

c2cpg accepts an --include <path> argument (can be used multiple times) were you are able to specify directories to look for includes. I guess in your case it would be easy to provide the path to your android SDK installation.

It also has --with-include-auto-discovery. It tries to figure out where the default system header files are located (using gcc on Linux/Mac or Visual Studio on Windows) and includes them automatically.

max-leuthaeuser avatar Jun 25 '25 05:06 max-leuthaeuser