Skip to content

Commit 06106cb

Browse files
committed
gfx/drivers_font_renderer: fix OOM NULL-deref in bitmapfont atlas buffer and coretext bitmapData
Two NULL-deref bugs reachable under OOM in the font renderer backends. === bitmapfont.c (font_renderer_bmp_init) === font_renderer_bmp_init allocates a scaled atlas buffer and then rasterises 256 glyphs into it: handle->atlas.buffer = (uint8_t*)calloc( handle->atlas.width * handle->atlas.height, 1); for (i = 0; i < BMP_ATLAS_SIZE; i++) { ... char_to_texture(handle, i, x, y); ... } The calloc was unchecked. char_to_texture does uint8_t *target = handle->atlas.buffer + atlas_x + atlas_y * handle->atlas.width; ... dst[xo + yo * handle->atlas.width] = col; which NULL-derefs on OOM. Fixed with a NULL-check immediately after the calloc, freeing the already-allocated 'handle' and returning NULL. The font_renderer_bmp_free teardown path is NULL-safe so we could alternatively have fallen through to that, but the direct free(handle)+return NULL is a minimal-diff fix. === coretext.c (ct_font_renderer_get_glyph) === The per-glyph rendering path calloc's a scratch bitmap, passes it to CGBitmapContextCreate, draws the glyph via CoreText, then byte-wise copies the bitmap into the shared atlas: bitmapData = calloc(slot->glyph.height, slot->glyph.width); offscreen = CGBitmapContextCreate(bitmapData, ...); if (!offscreen) { free(bitmapData); return false; } ... src = (const uint8_t*)bitmapData; for (r = 0; r < slot->glyph.height; r++) for (c = 0; c < slot->glyph.width; c++) dst[r * handle->atlas.width + c] = src[r * slot->glyph.width + c]; CGBitmapContextCreate is NULL-tolerant in its 'data' parameter - when NULL it allocates its own backing store. So on OOM the flow was: 1. bitmapData = NULL. 2. CGBitmapContextCreate creates a valid context with its own backing store. 3. CoreText renders into that internal store. 4. The 'src' copy loop dereferences bitmapData (NULL) and segfaults. A subtle failure mode because the first two steps succeed and the third (internally) works - only the atlas copy crashes, which in a stack trace looks like a CoreText / GPU issue rather than an OOM. Fixed with a NULL-check on bitmapData before the CGBitmapContextCreate call. Returning false here matches the return-false behaviour on the !offscreen branch below - the caller handles missing glyphs gracefully (glyph slot stays uninitialised, rendering falls back to whitespace). === Swept-clean in the same pass === Verified via visual inspection of every calloc/malloc/realloc call in the renderer files that all other sites were already NULL-checked: - bitmapfont.c : 3 sites (lines 58, 67, 75) all guarded. - bitmapfont_6x10.c / bitmapfont_10x10.c: all sites guarded. - stb.c / stb_unicode.c: all sites guarded. - freetype.c : all sites guarded. - font_driver.c: single site guarded (prior fix, commit landed earlier). Thread-safety: both bugs are in one-shot init paths called from the main video thread at atlas creation time; no concurrency concerns. Reachability: OOM on a 512x512 calloc (bitmapfont atlas) or a much smaller per-glyph calloc (coretext bitmapData) is unlikely on desktop builds but realistic on memory-constrained embedded targets where RetroArch runs - especially the bitmapfont path which is the fallback when no TTF renderer is available.
1 parent 5b73ff7 commit 06106cb

2 files changed

Lines changed: 19 additions & 0 deletions

File tree

gfx/drivers_font_renderer/bitmapfont.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,17 @@ static void *font_renderer_bmp_init(const char *font_path, float font_size)
185185
handle->atlas.height = (BMP_ATLAS_PADDING + (FONT_HEIGHT * handle->scale_factor)) * BMP_ATLAS_ROWS;
186186
handle->atlas.buffer = (uint8_t*)calloc(handle->atlas.width * handle->atlas.height, 1);
187187

188+
/* NULL-check atlas.buffer: char_to_texture below writes into
189+
* handle->atlas.buffer[x + y * atlas.width] and would NULL-
190+
* deref on OOM. font_renderer_bmp_free NULL-safely frees
191+
* atlas.buffer via the free(NULL) no-op, so we can bail out
192+
* via free(handle) alone. */
193+
if (!handle->atlas.buffer)
194+
{
195+
free(handle);
196+
return NULL;
197+
}
198+
188199
for (i = 0; i < BMP_ATLAS_SIZE; i++)
189200
{
190201
unsigned x = (i % BMP_ATLAS_COLS) *

gfx/drivers_font_renderer/coretext.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,14 @@ static bool coretext_font_renderer_render_glyph(CTFontRef face, ct_font_renderer
291291

292292
/* Create bitmap context */
293293
bitmapData = calloc(slot->glyph.height, slot->glyph.width);
294+
/* NULL-check: CGBitmapContextCreate tolerates NULL (it will
295+
* allocate its own backing store), but the byte-wise copy
296+
* into the atlas at lines ~321-325 dereferences bitmapData
297+
* as 'src'. If we proceeded on NULL, CoreGraphics might
298+
* give us a valid context, we'd draw into it, and then
299+
* NULL-deref on the atlas copy step. Fail cleanly now. */
300+
if (!bitmapData)
301+
return false;
294302
offscreen = CGBitmapContextCreate(bitmapData, slot->glyph.width, slot->glyph.height,
295303
8, slot->glyph.width, NULL, kCGImageAlphaOnly);
296304

0 commit comments

Comments
 (0)