尝试从指针读取字节数组时 JNA 无效的内存访问

JNA Invalid memory access when trying to read byte array from pointer

我正在尝试使用 JNA 从指针读取字节数组,但我不断收到:

Decompress with insz 11107, and outsize 65536
recieved 1
Decompression complete!
Decompress with insz 22112, and outsize 65536
recieved 1
Decompression complete!
Decompress with insz 22041, and outsize 65536
recieved 1
Decompression complete, final out size of 0!
Exception in thread "main" java.lang.Error: Invalid memory access
    at com.sun.jna.Native.read(Native Method)
    at com.sun.jna.Pointer.read(Pointer.java:149)
    at com.sun.jna.Pointer.getByteArray(Pointer.java:715)
    at me.TTARCHExtract.redo(TTARCHExtract.java:330)
    at me.TTARCHExtract.redo(TTARCHExtract.java:323)
    at me.TTARCHExtract.z_decompress(TTARCHExtract.java:313)

当我执行下面的代码时:

public static final Pointer toPointer(byte[] array, int length){
        Memory ret = new Memory(length);
        ret.write(0, array, 0, length);
        return ret;
    }
//code starts here
private byte[] z_decompress(byte[] in,int insize,byte[] out,int outsize) {
        if(in==null)return null;
        System.out.println("Decompress with insz "+insize+", and outsize "+outsize);
        Pointer inptr = toPointer(in, insize);
        Pointer outptr = toPointer(out, outsize);
        ZStream deflate = new ZStream();
        ZStream z = new ZStream();
        TTARCHHelper.load();
        ZlibLibrary lib = TTARCHHelper.ZLIB_LIBRARY;
        this.initz(lib, z, 15);
        this.initz(lib, deflate, -15);
        return this.redo(insize, outsize, z, lib, inptr, outptr, true, deflate);
    }

    private byte[] redo(int insize,int outsize,ZStream z,ZlibLibrary lib,Pointer inptr,Pointer outptr,boolean first,ZStream deflate) {
        lib.inflateReset(z);
        z.next_in=inptr;
        z.next_out=outptr;
        z.avail_in=insize;
        z.avail_out=outsize;
        int out = lib.inflate(z, ZlibLibrary.Z_FINISH);
        if(!first)System.out.println("recieved "+out);
        if(out != ZlibLibrary.Z_STREAM_END) {
            if(first)return this.redo(insize, outsize, deflate, lib, inptr, outptr, false, null);
            System.out.println("Compressed zlib/deflate input at offset "
                    + ""+dgboff+" ("+insize+" > "+outsize+") is wrong or complete");
            System.exit(-1);
            return null;
        }
        System.out.println("Decompression complete!");
        return z.next_out.getByteArray(0, outsize);
    }

private void initz(ZlibLibrary lib,ZStream z, int w) {
        lib.inflateInit2_(z, w, lib.zlibVersion(), z.size());
    }

getByteArray 是发生错误的地方。可能是什么原因造成的?

这个错误有时会发生,而且并不是所有的 zlib 输入流都会发生,所以它与输出大小有关吗?

代码来自C写的项目ttarchext

JNA 中的 Invalid Memory Access 错误表明您正在尝试访问尚未分配的内存。在本例中,next_out 指针的全长为 outsize。要对此进行调试,您需要查阅 API 以查看该函数是否希望您分配内存并将其传递给本机函数,或者本机函数本身是否会分配必要的内存。 (在后一种情况下,本机代码通常会告诉您如何在用完内存后释放内存。)对于这个 API,分配显然是在 inflateInit2() 调用中完成的,所以这是错误根本原因的提示。

输出具有指导意义,因为它表明使用较小的 insz 成功了一次,但使用较大的 insz 第二次失败了。崩溃的堆栈跟踪中的差异也很明显,表明递归调用发生在第二种情况(更大的输入)中,但可能没有出现在第一种情况中。 (要在调试中确认这一点,您应该添加更多输出。)

对于递归调用,唯一的变化是第三个参数从 ZStream z 改为 Zstream deflate(其中 null 作为可能的下一个值传递给迭代。)虽然将 z 更改为 deflate 似乎是正确的,但我看不到原始代码中应该有 null 的位置。这似乎是为了在完成之前作为“下一个”类型的迭代进行递归。 (这可能不是错误的原因,但很可疑。)

使用 deflate 参数而不是 zredo() 调用的唯一区别是调用 deflate 时窗口大小为 -15。这似乎与您映射的 inflateInit2_()documentation 相反:

The windowBits parameter shall be a base 2 logarithm of the maximum window size to use, and shall be a value between 8 and 15.

由于您移植的原始 C 代码也使用了 -15,这可能是正确的,但很明显不同的窗口大小对输出有影响。

我建议保留 deflate 而不是 null 作为递归调用的最后一个参数,并添加更多的输出语句,以便在递归时更深入地了解参数的值。


另一个可能导致错误的变量是 outsize 值。这似乎意味着完整的 outsize 值是可以读取的,如果您已经到达分配的末尾,情况可能并非如此。有可能 outsize 第一次是最小大小(也许 windowsize = 15 导致这是真的),但是当递归时(windowsize = -15 情况)不能依赖,并且您应该在最后一次迭代中从输出中读取更少的字节(查看原始来源建议 z.total_out。)