COM/CORBA/DCOP (was: Hello people. I have some questions)

Neil Hodgson nhodgson at bigpond.net.au
Fri Sep 7 08:12:42 EDT 2001


Alex Martelli:

> Sure, it's the same as passing an invalid pointer, except that it must be
> diagnosed by a different HRESULT -- that's what E_POINTER is *FOR*, you
> know.  In either case, crashing (technically, "propagating an
exception" --
> remember "crashes" are really exceptions in NT's SEH model) is NOT
> permitted.

   This is a nice interpretation of the COM spec (referenced later in your
reply) but unfortunately it is an incorrect interpretation. Exception here
is referring to C++ exceptions rather than SEH or crashing. No COM server
code I've ever seen has verified pointer correctness in release builds
(sometimes pointer verification is done in debug build traces) as it would
impose too much of a performance impact.

   To demonstrate that this is the case for at least some COM servers, here
is a short program that causes some generally available Automation servers
to crash although there is some SEH to intercept the crash so we can see it.
To see it crash out completely just remove the __try and __except code
around the Invoke call:

// COM blowup demonstration program
#include <stdio.h>
#include "Windows.h"
int main(int, char *[]) {
    // Want to see the trace before crashing
    setvbuf(stdout, 0, _IONBF, 0);

    printf("Starting demo\n");
    HRESULT hr =S_OK;

    hr = ::CoInitialize(0);
    if (FAILED(hr)) {
        printf("Failed CoInitialize %x\n", hr);
        return 1;
    }

    // Find a class most people will have installed
    CLSID clsid;
    hr = ::CLSIDFromProgID(L"WScript.Shell.1", &clsid);
    //hr = ::CLSIDFromProgID(L"COMCTL.ProgCtrl.1", &clsid);
    //hr = ::CLSIDFromProgID(L"MSDDS.Icon", &clsid);
    if (FAILED(hr)) {
        printf("Failed CLSIDFromProgID %x\n", hr);
        return 1;
    }
    IDispatch *pdisp = 0;
    hr = ::CoCreateInstance(clsid, 0,
        CLSCTX_INPROC_SERVER,
        IID_IDispatch, (void**)&pdisp);
    if (FAILED(hr)) {
        printf("Failed CoCreateInstance %x\n", hr);
        return 1;
    }
    IID iid = {0};
    hr = pdisp->Invoke(0,iid,0,0, 0, 0,0,0);
    if (FAILED(hr)) {
        printf("Failed Invoke with NULL params %x\n", hr);
    } else {
        printf("Succeeded Invoke with NULL params\n");
    }
    for (int i=0; i<10; i++) {    // Maybe we'll be lucky once...
        bool seh = false;
        hr = S_OK;
        DISPPARAMS *params =
            reinterpret_cast<DISPPARAMS *>(rand());
        __try {
            hr = pdisp->Invoke(0,iid,0,0, params, 0,0,0);
        } __except(EXCEPTION_EXECUTE_HANDLER) {
            seh = true;
        }
        if (seh) {
            printf("SEH\n");
        } else if (FAILED(hr)) {
            printf("Failed Invoke %x\n", hr);
            return 1;
        } else {
            printf("Succeeded Invoke with %x params\n",
                params);
        }
    }
    pdisp->Release();

    ::CoUninitialize();
    printf("Completed demo\n");
    return 0;
}

   The three mentioned ProgIDs were the first I tried and I'd expect every
server on the machine to crash with this provocation. You'll also see other
interfaces that take pointers will crash when fed random pointers.

>...  Say a client calls your Invoke with a standard
> dispatch-ID such as -4, just trying to find out whether your interface can
> be enumerated (==can supply an IEnumVariant*) -- this is perfectly OK
> behavior by an Automation client, by the way.  If you don't check the
> dispID, you may well end up addressing a vtable with an out-of-bounds
> negative index, and get a "random" (sort-of) procedure address to dispatch
> to.  ...

   I'm almost ready to agree on this one but the documentation doesn't
really give clients a lot of freedom: "Use GetIDsOfNames or the object's
documentation to obtain the dispatch identifier. ". Yes, it is bad
engineering to not validate dispatch IDs but an interface contract has two
sides. My habit of reading specs, particularly COM specs, extremely
literally, come from suffering far too much grief at the hands of code
(often written in Redmond) that works in an unreasonable but valid way. If
the spec says "should", then it didn't say "must" and so you should not rely
on that section being followed.

   Neil






More information about the Python-list mailing list