joernio / joern

Open-source code analysis platform for C/C++/Java/Binary/Javascript/Python/Kotlin based on code property graphs. Discord https://discord.gg/vv4MH284Hc
https://joern.io/
Apache License 2.0
2.09k stars 287 forks source link

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

Open MC-box opened 1 month ago

MC-box commented 1 month ago

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):

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

max-leuthaeuser commented 1 month ago

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.

MC-box commented 1 month ago

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?

max-leuthaeuser commented 1 month ago

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.

MC-box commented 1 month ago

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.

max-leuthaeuser commented 1 month ago

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?