Constructing a HashMap<String, String[]> through JNI

-2

I have a Java class calling a native method ACLInfo(String path), this native method should return HashMap to be used in java. Here's the java code.

import java.util.*;

public class test
{
  native Map<String, String[]> ACLInfo(String s);
  
   static
   {
      System.loadLibrary("test");
   }
  
   static public void main(String args[])
   {
   
      test obj = new test();
      Map<String, String[]> map = obj.ACLInfo("C:/Windows/Boot/Resources/bootres.dll");

      for (Map.Entry<String, String[]> entry : map.entrySet())
      {
          String name = entry.getKey();
          System.out.println("Name:"+name);
          System.out.println("Properties are:");
          for(String text : entry.getValue())
          {
              if(text.equals("Full Control")){
                 System.out.println(text);
                 continue;
             }
              if(text.equals("Read"))
                 System.out.println(text);
             if(text.equals("Write"))
                 System.out.println(text);
             if(text.equals("Execute"))
                 System.out.println(text);
          }
          
      }
   }
}

The native code is.

#include <iostream>
#include <jni.h>
#include <string.h>
#include <windows.h>
#include <tchar.h>
#include <Lmcons.h>
#include "test.h"
#include "accctrl.h"
#include "aclapi.h"

using namespace std;

JNIEXPORT jobject JNICALL Java_test_ACLInfo(JNIEnv *env, jobject jobj, jstring s)
{
    jobjectArray ret;
    jstring jstr;
    PSID pSidOwner = NULL;
    BOOL bRtnBool = TRUE;
    DWORD dwRtnCode = 0;
    SID_NAME_USE eUse = SidTypeUnknown;
    HANDLE hFile;
    PSECURITY_DESCRIPTOR pSD = NULL;
    PACL pOldDACL = NULL;
    int i,aceNum;
    const char* test;
    std::string name,reslt;

    const char* nativeString = env->GetStringUTFChars(s, 0);
    LPCSTR file = nativeString;
    // Get the handle of the file object.
    hFile = CreateFile(
                  file,
                  GENERIC_READ,
                  FILE_SHARE_READ,
                  NULL,
                  OPEN_EXISTING,
                  FILE_ATTRIBUTE_NORMAL,
                  NULL);


    // Get the SID of the file.
    dwRtnCode = GetSecurityInfo(
                  hFile,
                  SE_FILE_OBJECT,
                  DACL_SECURITY_INFORMATION,
                  &pSidOwner,
                  NULL,
                  &pOldDACL,
                  NULL,
                  &pSD);

    PACL pAcl = pOldDACL;
    aceNum = pOldDACL->AceCount;

    ret = (jobjectArray)env->NewObjectArray(3, env->FindClass("java/lang/String"), env->NewStringUTF(""));

    jclass mapClass = env->FindClass("java/util/HashMap");      //HashMap class in java library
    if(mapClass == NULL)
    {
        return NULL;
    }
    jsize map_len = aceNum;
    jmethodID init = env->GetMethodID(mapClass, "<init>", "(I)V");  //constructor of HashMap class
    jobject hashMap = env->NewObject(mapClass, init, map_len);      //creating a new object for that class
    jmethodID put = env->GetMethodID(mapClass, "put", "(Ljava/lang/String;[Ljava/lang/String;)[Ljava/lang/String;");

    for (i = 0; i < aceNum; i++)
    {
    PACCESS_ALLOWED_ACE AceItem;
    ACE_HEADER *aceAddr = NULL;
    if (GetAce(pOldDACL, i, (LPVOID*)&AceItem) && GetAce(pOldDACL, i, (LPVOID*)&aceAddr))
        {
        LPTSTR AccountBuff = NULL, DomainBuff = NULL;
        DWORD AccountBufflength = 1, DomainBufflength = 1;
        PSID_NAME_USE peUse = new SID_NAME_USE;
        PSID Sid = &AceItem->SidStart;
        LookupAccountSid(NULL, Sid, AccountBuff, (LPDWORD)&AccountBufflength, DomainBuff, (LPDWORD)&DomainBufflength, peUse);

        AccountBuff = (LPSTR)malloc(AccountBufflength * sizeof(LPSTR));
        DomainBuff = (LPSTR)malloc(DomainBufflength * sizeof(LPSTR));

        LookupAccountSid(NULL, Sid, AccountBuff, &AccountBufflength, DomainBuff, &DomainBufflength, peUse);

        std::string acc=AccountBuff;
        std::string dom=DomainBuff;
        name = acc+"\\"+dom;

        ACCESS_MASK Mask = AceItem->Mask;
        if (((Mask & GENERIC_ALL) == GENERIC_ALL) || ((Mask & FILE_ALL_ACCESS) == FILE_ALL_ACCESS)){
         reslt="Full Control";
         test = reslt.c_str();
         env->SetObjectArrayElement(ret,i,env->NewStringUTF(test));
         env->CallObjectMethod(hashMap, put, env->NewStringUTF(name.c_str()), ret);
         continue;
        }
         int j=0;
        if (((Mask & GENERIC_READ) == GENERIC_READ) || ((Mask & FILE_GENERIC_READ) == FILE_GENERIC_READ))
            {
                reslt="Read";
                test = reslt.c_str();
                env->SetObjectArrayElement(ret,j++,env->NewStringUTF(test));
            }
        if (((Mask & GENERIC_WRITE) == GENERIC_WRITE) || ((Mask & FILE_GENERIC_WRITE) == FILE_GENERIC_WRITE))
            {
                reslt="Write";
                test = reslt.c_str();
                env->SetObjectArrayElement(ret,j++,env->NewStringUTF(test));
            }
        if (((Mask & GENERIC_EXECUTE) == GENERIC_EXECUTE) || ((Mask & FILE_GENERIC_EXECUTE) == FILE_GENERIC_EXECUTE))
            {
                reslt="Execute";
                test = reslt.c_str();
                env->SetObjectArrayElement(ret,j++,env->NewStringUTF(test));
            }
        env->CallObjectMethod(hashMap, put, env->NewStringUTF(name.c_str()), ret);
        }
    }
    return hashMap;
}

On running this java class it shows: A fatal error has been detected by the Java Runtime Environment: EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x000000006d6cd4ed, pid=19504, tid=0x0000000000003da0

What's the issue? I have made used of this stackoverflow post for creating hashmap How to create HashMap<String, String> through JNI then parse to java

java
c++
hashmap
java-native-interface
asked on Stack Overflow Aug 13, 2020 by Margie • edited Aug 14, 2020 by Botje

1 Answer

2

I have rewritten your code to get rid of the most egregious bugs:

  • Your signature for HashMap#put was wrong.
  • You have to create one string[] for every element you put in the hashmap. If you reuse the same object you will get N copies of the same string[].
  • You cannot create an array of size 3 up front and only fill in some slots. Better to use a temporary vector in C++ land and create the right array size later on.
  • You need to use PushLocalFrame and PopLocalFrame to ensure you do not exceed your local reference budget.

I have also removed a lot of the non-JVM cruft. Please try to keep your example code small and to the point.

JNIEXPORT jobject JNICALL Java_test_ACLInfo(JNIEnv *env, jobject jobj, jstring s)
{

    // removed stuff
    jclass mapClass = env->FindClass("java/util/HashMap");      //HashMap class in java library
    jclass stringClass = env->FindClass("java/lang/String");
    if(mapClass == NULL)
    {
        return NULL;
    }
    jsize map_len = aceNum;
    jmethodID init = env->GetMethodID(mapClass, "<init>", "(I)V");  //constructor of HashMap class
    jobject hashMap = env->NewObject(mapClass, init, map_len);      //creating a new object for that class
    jmethodID put = env->GetMethodID(mapClass, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");

    for (i = 0; i < aceNum; i++) {
        // removed stuff

        PACCESS_ALLOWED_ACE AceItem;
        ACE_HEADER *aceAddr = NULL;
        if (GetAce(pOldDACL, i, (LPVOID*)&AceItem) && GetAce(pOldDACL, i, (LPVOID*)&aceAddr)) {
            // removed more stuff
            std::string name = std::string(AccountBuff)+"\\"+std::string(DomainBuff);
            std::vector<std::string> perms;

            ACCESS_MASK Mask = AceItem->Mask;
            if (((Mask & GENERIC_ALL) == GENERIC_ALL) || ((Mask & FILE_ALL_ACCESS) == FILE_ALL_ACCESS)) {
                perms.emplace_back("Full Control");
            } else {
                if (((Mask & GENERIC_READ) == GENERIC_READ) || ((Mask & FILE_GENERIC_READ) == FILE_GENERIC_READ)) {
                    perms.emplace_back("Read");
                }
                if (((Mask & GENERIC_WRITE) == GENERIC_WRITE) || ((Mask & FILE_GENERIC_WRITE) == FILE_GENERIC_WRITE)) {
                    perms.emplace_back("Write");
                }
                if (((Mask & GENERIC_EXECUTE) == GENERIC_EXECUTE) || ((Mask & FILE_GENERIC_EXECUTE) == FILE_GENERIC_EXECUTE)) {
                    perms.emplace_back("Execute");
                }
            }

            env->PushLocalFrame(10);
            jobject ret = env->NewObjectArray(perms.size(), stringClass, nullptr);
            for (int i = 0; i < perms.size(); i++) {
                env->SetObjectArrayElement(ret, i, env->NewStringUTF(perms[i].c_str()));
            }
            env->CallObjectMethod(hashMap, put, env->NewStringUTF(name.c_str()), ret);
            env->PopLocalFrame(nullptr);
        }
    }
    return hashMap;
}
answered on Stack Overflow Aug 13, 2020 by Botje • edited Aug 13, 2020 by Botje

User contributions licensed under CC BY-SA 3.0