TTF GLYF Work, and Wrapping malloc()

TTF Work

I've only had a bit of time to work on the DOS game lately, with Further Confusion over the weekend.

I did get a little done on the font loader, though. I'm now reading in Glyph outline data! This is super exciting. Soon It'll be time to try to rasterize this stuff. Once I've got actual font data coming in, getting characters mapped to glyph data, and rendering, I'll just need to go back and fill in other significant stuff like the different CMAP formats, figuring out character advance and kerning data, and others. Soon!

malloc() wrapper

I threw in a malloc() and free() wrapper because I know I'm going to need it. This is something I stumbled across in Ninkasi code. Some of the errors AFL found were due to truncated integers in malloc() calls. This is especially for arrays, where I might try to allocate some array like...

malloc(elementCount * sizeof(struct Something));

Keep in mind that Ninkasi is intended to work on 16-bit, 32-bit, and 64-bit machines, but uses 32-bit integers for everything internally (and has a 32-bit address space). What this meant was that arrays loaded from files could be larger than the maximum object size for a given platform.

On the real-mode DOS side, this meant size_t was 16-bits. malloc() takes a size_t as an argument. Passing 32-bit values into malloc() would get truncated to 16-bits without the program knowing (or the compiler giving a warning, grumblegrumble), and then the program happily writing off the end of the array regardless, because the number of bytes to read is still stored as that 32-bit value.

On the 32-bit side, we can still have an issue with this. because some 32-bit value times another 32-bit value can easily overflow without a warning, but leaving us with a truncated allocation.

So the solution is a malloc() wrapper that checks for an overflow. This is fairly simple...

// Note: nkuint32_t is our 32-bit unsigned int typedef, because we
// target platforms from before the existence of a proper 32-bit
// typedef.

void *mallocWrapper(nkuint32_t size)
{
    if(size > ~(size_t)0) {
        return NULL;
    }

    return malloc(size);
}

In fact, it's so simple that the compiler will often vomit out a warning at you that the test is always going to fail, if you're on a platform where size_t is 32-bits or greater. But at least this will protect the 16-bit build from impossible malloc() calls.

Now let's see that array-allocation wrapper...

void *mallocArray(nkuint32_t count, nkuint32_t size)
{
    // FIXME: See if this overflow check really works.
    if(count >= ~(nkuint32_t)0 / size) {
        return NULL;
    }

    return mallocWrapper(count * size);
}

This one will protect us from truncating the allocation size when we have an impossible size and count combination.

Probably.

Actually, I need to verify that that overflow check works, but I'll save that for another day.

Time to sleep!

Posted: 2020-01-20

Tags: dos, devlog