#101

kbts_FontIsValid returns true for data kb cannot parse, then the next call dereferences a NULL blob

Opendimenusopened 18d ago

Tested with commit cc63806 (v2.28e), Apple clang 21, macOS arm64.

kbts_LoadFont returns KBTS_LOAD_FONT_ERROR_NONE when the data does not start with a header it knows (0x00010000, OTTO, ttcf). It does not set Font->Error. So kbts_FontFromMemory returns a font with Blob == NULL, and kbts_FontIsValid says the font is valid. The next call that uses the font dereferences the NULL blob.

The docs for kbts_LoadFont say: "If we could not find any useful font data, the [return value] is KBTS_LOAD_FONT_ERROR_INVALID_FONT."

Two real files hit this as well as junk data:

  • An Apple TrueType font with the true header: Zycon.ttf from HarfBuzz's tests
  • A Mac resource-fork font: DFONT.dfont

Direct API

// cc -g invalid_font.c -o invalid_font && ./invalid_font [font file]
#define KB_TEXT_SHAPE_IMPLEMENTATION
#include "kb_text_shape.h"
#include "common.h"

int main(int argc, char **argv)
{
    static char junk[64] = "not a font";
    int size = sizeof(junk);
    void *data = argc > 1 ? read_file(argv[1], &size) : junk;

    kbts_font font = kbts_FontFromMemory(data, size, 0, 0, 0);
    printf("kbts_FontIsValid: %d, font.Blob: %p\n", kbts_FontIsValid(&font), (void *)font.Blob);
    fflush(stdout);

    kbts_shape_config *config = kbts_CreateShapeConfig(&font, KBTS_SCRIPT_LATIN, KBTS_LANGUAGE_DONT_KNOW, 0, 0);
    printf("kbts_CreateShapeConfig: %p\n", (void *)config);
    return 0;
}

common.h only has a read_file helper. Output with -fsanitize=address, the same for no argument, Zycon.ttf and DFONT.dfont:

kbts_FontIsValid: 1, font.Blob: 0x0
==ERROR: AddressSanitizer: SEGV on unknown address 0x00000000004c
    #0 kbts__BlobTableData kb_text_shape.h:17817
    #1 kbts__PlaceShapeConfig kb_text_shape.h:24320
    #2 kbts_CreateShapeConfig2 kb_text_shape.h:24896
    #3 kbts_CreateShapeConfig kb_text_shape.h:24909

Context API

// cc -g invalid_font_context.c -o invalid_font_context && ./invalid_font_context
#define KB_TEXT_SHAPE_IMPLEMENTATION
#include "kb_text_shape.h"
#include <stdio.h>

int main(void)
{
    static char junk[64] = "not a font";
    kbts_shape_context *context = kbts_CreateShapeContext(0, 0);
    kbts_font *font = kbts_ShapePushFontFromMemory(context, junk, sizeof(junk), 0);
    printf("kbts_ShapePushFontFromMemory: %p\n", (void *)font);
    fflush(stdout);

    kbts_ShapeBegin(context, KBTS_DIRECTION_DONT_KNOW, KBTS_LANGUAGE_DONT_KNOW);
    kbts_ShapeUtf8(context, "abc", 3, KBTS_USER_ID_GENERATION_MODE_CODEPOINT_INDEX);
    kbts_ShapeEnd(context);
    printf("kbts_ShapeError: %d\n", (int)kbts_ShapeError(context));
    kbts_run run;
    while (kbts_ShapeRun(context, &run)) {
        kbts_glyph *glyph;
        while (kbts_GlyphIteratorNext(&run.Glyphs, &glyph)) { printf("glyph id %d\n", glyph->Id); }
    }
    return 0;
}

Without ASan, kbts_ShapePushFontFromMemory returns a non-NULL font and the program crashes later in kbts_ShapeRun. With -fsanitize=address it crashes inside the push, on a wild address, so the blob pointer may not be initialized on this path:

==ERROR: AddressSanitizer: SEGV on unknown address 0x5847d7d9d7ea
    #0 kbts__BlobTableData kb_text_shape.h:17817
    #1 kbts_GetVariationInfo kb_text_shape.h:25099
    #2 kbts__InitializeContextFont kb_text_shape.h:25489
    #3 kbts_ShapePushFontFromMemory2 kb_text_shape.h:25623

Expected: kbts_LoadFont returns KBTS_LOAD_FONT_ERROR_INVALID_FONT and sets Font->Error, so kbts_FontIsValid returns 0 and kbts_ShapePushFontFromMemory returns 0.

(I found this with help from Claude, with a harness that runs HarfBuzz's shaping test suite through kb and through HarfBuzz and compares them. I checked each repro by hand.)

Activity

0

No activity yet.