Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot access SAFEARRAY elements from WMI query #1083

Closed
Coalado opened this issue Apr 12, 2019 · 8 comments
Closed

Cannot access SAFEARRAY elements from WMI query #1083

Coalado opened this issue Apr 12, 2019 · 8 comments

Comments

@Coalado
Copy link

Coalado commented Apr 12, 2019

The following code queries WMI. The Type of Win32_DiskDrive.Capabilities is uint16[] and result.getValue returns a SAFEARRAY Instance.

System.out.println("Var Type(3 expected): " + value.getVarType().intValue()); returns randomly 0 or 3 if I start the process several times.
System.out.println("Size (>0 expected): " + (value.getUBound(0) - value.getLBound(0)));
is correct, but
Object el = value.getElement(0); fails.
value.accessData(); returns null which is unexpected as well, so I cannot use OaIdlUtil#toPrimitiveArray (Nullpointer)

OS: Windows10 64Bit (JDK 1.8* 32 or 64 bit)

enum Win32_DiskDrive_Values {
        Caption,
        Capabilities
    }

    public static void main(String[] args) throws IOException, InterruptedException {
        try {
            WmiQuery<Win32_DiskDrive_Values> serialNumberQuery = new WmiQuery<Win32_DiskDrive_Values>("Win32_DiskDrive", Win32_DiskDrive_Values.class);
            Ole32.INSTANCE.CoInitializeEx(null, Ole32.COINIT_MULTITHREADED);
            WmiResult<Win32_DiskDrive_Values> result = serialNumberQuery.execute();
            for (int i = 0; i < result.getResultCount(); i++) {
                System.out.println(result.getValue(Win32_DiskDrive_Values.Caption, i));
                SAFEARRAY value = (SAFEARRAY) result.getValue(Win32_DiskDrive_Values.Capabilities, i);
                // According to https://docs.microsoft.com/en-us/windows/desktop/cimwin32prov/win32-diskdrive, the type of Capabilities
                // should be uint16[] which should be Variant.VT_I2 (2-byte integer)
                // however, it is not constant. sometimes it is 0, sometimes Variant.VT_I2 (3);
                System.out.println("Var Type(3 expected): " + value.getVarType().intValue());
                System.out.println("Size (>0 expected): " + (value.getUBound(0) - value.getLBound(0)));
                Object el = value.getElement(0);
                System.out.println("Element 0 (!=null expected): " + el);
                Pointer pointer = value.accessData();
                System.out.println("pointer (!=null expected): " + pointer);
            }
        } catch (Throwable e) {
            e.printStackTrace();
        } finally {
            Ole32.INSTANCE.CoUninitialize();
        }
    }
@matthiasblaesing
Copy link
Member

I don't understand, why noone reads the issue template. This is not a bug. Or it might be, but please close this and ask on the mailing list, as explicitly stated in the issue template.

@matthiasblaesing
Copy link
Member

Ok - long story short: It does not work, because the bindings don't support SAFEARRAY access. @dbwiddis do you want to have a look at this? This is basicly an use-after-free. If I'm not mistaken, this query hits WebmcliUtil, line 344, the "unimplemented" type. Maybe it would be better to raise an InvalidArgumentException at that point?!

@Coalado
Copy link
Author

Coalado commented Apr 12, 2019

I'm sorry for asking at the wrong place,
The issue template says

Only file GitHub issues for bugs and feature requests

To me, this whole issue sounds either like a bug, or a missing feature. I read several old issues (like #522) and I was assuming that SAFEARRAY access should actually work.

Seems like I was wrong. In any case, an exception would be better than returning a SAFEARRAY object.

@matthiasblaesing
Copy link
Member

For your concrete usecase here is a sample:

    public static void main(String[] args) throws IOException, InterruptedException {
        Ole32.INSTANCE.CoInitializeEx(null, Ole32.COINIT_MULTITHREADED);
        
        // Connect to the server
        Wbemcli.IWbemServices svc = WbemcliUtil.connectServer("ROOT\\CIMV2");

        // Send query
        try {
            Wbemcli.IEnumWbemClassObject enumerator = svc.ExecQuery("WQL", "SELECT Caption, Capabilities, CapabilityDescriptions FROM Win32_DiskDrive",
                    Wbemcli.WBEM_FLAG_FORWARD_ONLY | Wbemcli.WBEM_FLAG_RETURN_IMMEDIATELY, null);

            try {
                IWbemClassObject[] result;
                VARIANT.ByReference pVal = new VARIANT.ByReference();
                IntByReference pType = new IntByReference();
                IntByReference plFlavor = new IntByReference();
                while(true) {
                    result = enumerator.Next(0, 1);
                    if(result.length == 0) {
                        break;
                    }
                    COMUtils.checkRC(result[0].Get("Caption", 0, pVal, pType, plFlavor));
                    System.out.println("---------" + pVal.getValue() + "-------------");
                    OleAuto.INSTANCE.VariantClear(pVal);
                    COMUtils.checkRC(result[0].Get("CapabilityDescriptions", 0, pVal, pType, plFlavor));
                    SAFEARRAY safeArray = (SAFEARRAY) pVal.getValue();
                    for(int i = safeArray.getLBound(0); i<=safeArray.getUBound(0); i++) {
                        System.out.println("\t" + safeArray.getElement(i));
                    }
                    OleAuto.INSTANCE.VariantClear(pVal);
                    COMUtils.checkRC(result[0].Get("Capabilities", 0, pVal, pType, plFlavor));
                    safeArray = (SAFEARRAY) pVal.getValue();
                    for(int i = safeArray.getLBound(0); i<=safeArray.getUBound(0); i++) {
                        System.out.println("\t" + safeArray.getElement(i));
                    }
                    OleAuto.INSTANCE.VariantClear(pVal);
                    result[0].Release();
                }
            } finally {
                // Cleanup
                enumerator.Release();
            }
        } finally {
            // Cleanup
            svc.Release();
        }

        Ole32.INSTANCE.CoUninitialize();
    }

@dbwiddis
Copy link
Contributor

// According to https://docs.microsoft.com/en-us/windows/desktop/cimwin32prov/win32-diskdrive, the type of Capabilities should be uint16[] which should be Variant.VT_I2 (2-byte integer)

uint16[] is an array, where C would return a pointer, while VT_I2 is a 16-bit value. Your description of return values of 0 (VT_EMPTY) and 3 (VT_I4) are consistent with WMI telling you you have either an empty array, or giving you a 32-bit pointer to the array. However, the enumerated result is released before you try to access it so you lose access to the value.

The existing code is not set up to handle by-reference values such as arrays. @matthiasblaesing has posted code showing how you can access the array before releasing the WMI result iterator.

@Coalado
Copy link
Author

Coalado commented Apr 13, 2019

Thanks for your help. I honestly did not expect to get help that fast and that accurate. You are the best.
From my persepctive this issue could get closed, but maybe WmiQuery should get extended?

@matthiasblaesing
Copy link
Member

Please let it stay open - your confusion is a sign, that there should be an exception raised. Especially, as it is causes out-of-bound access.

@matthiasblaesing
Copy link
Member

#1084 contains the guard, that will prevent access to unsupported types.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants