wmclient.cc: GetFullWindowProperty(): programming error

Stanislav Maslovski <[email protected]> Thu, 29 Apr 2010 16:21:12 +0400
Newsgroups gmane.comp.window-managers.icewm.devel
Message-ID <[email protected]>
Hello,

The file wmclient.cc in the current CVS contains the following code
(below) which in the past was found responsible for crashes when
loading large icon data. I see that CVS incorporated a patch from
Debian that was meant to prevent those crashes.

I believe that this patch is wrong (and actually I have seen the same
crashes even with this patch). The reason for the crash is not that
some applications provide icons of unbelivably huge sizes (like the
comment suggests), but a rather simple programming mistake.

The documentation on XGetWindowProperty() describes the meaning of the
4th parameter of this function as:

long_offset  Specifies the offset in the specified property (in 32-bit
             quantities) where the data is to be retrieved.

This parameter is always set to 0 in the code below. Therefore, for a
property with a total size greater than the requested long_length
(currently, 16384*32; in the past this value grew up several times),
the first and _all_ subsequent calls of XGetWindowProperty() read the
_same_ chunk of property data that starts at offset 0 and ends around
16384*32. In the past, this reading continued indefinitely until a
crash; in the code below it continues until an artificial 2 Mb
boundary is reached (this logic also introduces a memleak, btw,
because XFree(property) gets skipped in this case). In any case, such
a behaviour is simply wrong.

In the icewm 1.2 tree that I maintain for quite some time for myself
(due to historical reasons; now the situation with icewm in Debian is
better and I am willing to switch to the official version) I have
corrected those segfaults as is shown in the second snipplet. This
works on a 32-bit machine, but corrections for 64-bits are obvious.
Sorry for not providing a direct patch!

/* snipplet from current CVS */
static void *GetFullWindowProperty(Display *display, Window handle, Atom propAtom, int &itemCount, int itemSize1)
{
    void *data = NULL;
    itemCount = 0;
    int itemSize = itemSize1;
    if (itemSize1 == 32)
        itemSize = sizeof(long) * 8;

    {
        Atom r_type;
        int r_format;
        unsigned long nitems;
        unsigned long bytes_remain;
        unsigned char *prop;

        while (XGetWindowProperty(display, handle,
                               propAtom, 0, 16384*32, False, AnyPropertyType,
                               &r_type, &r_format, &nitems, &bytes_remain,
                               &prop) == Success && prop && bytes_remain == 0)
        {
            if (r_format == itemSize1 && nitems > 0) {
                data = realloc(data, (itemCount + nitems) * itemSize / 8);

                // access to memory beyound 256MiB causes crashes! But anyhow, size
                // >>2MiB looks suspicious. Detect this case ASAP. However, if
                // the usable icon is somewhere in the beginning, it's okay to
                // return truncated data.
                if(itemCount * itemSize / 8 >= 2097152)
                   break;

                memcpy((char *)data + itemCount * itemSize / 8, prop, nitems * itemSize / 8);
                itemCount += nitems;
                XFree(prop);
                if (bytes_remain == 0)
                    break;
                continue;
            }
            XFree(prop);
            free(data);
            itemCount = 0;
            return NULL;
        }
    }
    return data;
}

/* sniplet from my own tree */

static void *GetFullWindowProperty(Display *display, Window handle, Atom propAtom, int &itemCount, int itemSize)
{
    void *data = NULL;
    itemCount = 0;

    {
        Atom r_type;
        int r_format;
        unsigned long nitems;
        unsigned long bytes_remain;
        unsigned char *prop;

        while (XGetWindowProperty(display, handle,
                               propAtom, (itemCount * itemSize) / 32, 1024*32, False, AnyPropertyType,
                               &r_type, &r_format, &nitems, &bytes_remain,
                               &prop) == Success && prop)
        {
            if (r_format == itemSize && nitems > 0) {
                data = realloc(data, (itemCount + nitems) * itemSize / 8);
                memcpy((char *)data + itemCount * itemSize / 8, prop, nitems * itemSize / 8);
                itemCount += nitems;
                XFree(prop);
                if (bytes_remain == 0)
                    break;
                continue;
            }
            XFree(prop);
            free(data);
            itemCount = 0;
            return NULL;
        }
    }
    return data;
}

-- 
Stanislav

------------------------------------------------------------------------------