Skip to content

Commit da531e5

Browse files
JoeMattclaude
andcommitted
fix: OOB read in tom_render_*_scanline (closes #127)
ASAN on PR #126 surfaced a global-buffer-overflow read 1 byte past tomRam8 in tom_render_16bpp_cry_scanline (src/tom/tom.c:625). Same pattern in all five render variants: the per-pixel loop walks current_line_buffer (= tomRam8[0x1800], 10240 bytes max) based on tomWidth, which can be set by display registers to a value larger than the line buffer holds. Fix: new helper tom_clamp_line_buffer_width() that, given the current cursor, the requested width, the per-iteration source-byte cost (2 for 16bpp, 4 for 24bpp), and the pwidth_scale, returns a clamped width that guarantees the loop won't read past the end of tomRam8. Applied to all five render entry points: - tom_render_16bpp_cry_rgb_mix_scanline - tom_render_16bpp_cry_scanline (the one ASAN caught) - tom_render_24bpp_scanline - tom_render_16bpp_direct_scanline - tom_render_16bpp_rgb_scanline Closes #127. With this in, the sanitizers CI job should go fully clean -- candidate to flip continue-on-error: true to false in a follow-up. Co-Authored-By: Claude Opus 4.7 <[email protected]>
1 parent c18c2d4 commit da531e5

1 file changed

Lines changed: 28 additions & 0 deletions

File tree

src/tom/tom.c

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -548,6 +548,29 @@ uint16_t TOMGetMEMCON1(void)
548548
}
549549

550550
#define LEFT_BG_FIX
551+
552+
/* Clamp `width` so the per-pixel loop in a tom_render_*_scanline()
553+
* cannot walk past the end of tomRam8 via current_line_buffer.
554+
* Catches an ASAN-reported OOB read (#127): when display registers
555+
* request a width larger than the on-chip line buffer holds
556+
* (10240 bytes at tomRam8[0x1800..0x3FFF]).
557+
*
558+
* bytes_per_iter: source bytes consumed per loop iter (2 for 16bpp
559+
* variants, 4 for 24bpp).
560+
* pwidth_scale: backbuffer pixels produced per loop iter. */
561+
static uint16_t tom_clamp_line_buffer_width(
562+
const uint8_t *current_line_buffer, uint16_t width,
563+
unsigned bytes_per_iter, uint8_t pwidth_scale)
564+
{
565+
const uint8_t *lb_end = &tomRam8[sizeof(tomRam8)];
566+
unsigned long bytes_left;
567+
unsigned long safe_width;
568+
if (current_line_buffer >= lb_end) return 0;
569+
bytes_left = (unsigned long)(lb_end - current_line_buffer);
570+
safe_width = (bytes_left / bytes_per_iter) * pwidth_scale;
571+
return (width > safe_width) ? (uint16_t)safe_width : width;
572+
}
573+
551574
// 16 BPP CRY/RGB mixed mode rendering
552575
void tom_render_16bpp_cry_rgb_mix_scanline(uint32_t * backbuffer)
553576
{
@@ -579,6 +602,7 @@ void tom_render_16bpp_cry_rgb_mix_scanline(uint32_t * backbuffer)
579602
backbuffer += 2 * startPos, width -= startPos;
580603
#endif
581604

605+
width = tom_clamp_line_buffer_width(current_line_buffer, width, 2, pwidth_scale);
582606
while (width >= pwidth_scale)
583607
{
584608
uint16_t color = (*current_line_buffer++) << 8;
@@ -620,6 +644,7 @@ void tom_render_16bpp_cry_scanline(uint32_t * backbuffer)
620644
backbuffer += 2 * startPos, width -= startPos;
621645
#endif
622646

647+
width = tom_clamp_line_buffer_width(current_line_buffer, width, 2, pwidth_scale);
623648
while (width >= pwidth_scale)
624649
{
625650
uint16_t color = (*current_line_buffer++) << 8;
@@ -661,6 +686,7 @@ void tom_render_24bpp_scanline(uint32_t * backbuffer)
661686
backbuffer += 2 * startPos, width -= startPos;
662687
#endif
663688

689+
width = tom_clamp_line_buffer_width(current_line_buffer, width, 4, pwidth_scale);
664690
while (width >= pwidth_scale)
665691
{
666692
uint32_t b;
@@ -687,6 +713,7 @@ void tom_render_16bpp_direct_scanline(uint32_t * backbuffer)
687713
uint8_t pwidth = ((GET16(tomRam8, VMODE) & PWIDTH) >> 9) + 1;
688714
uint8_t pwidth_scale = (pwidth >= 8) ? (pwidth / 4) : 1;
689715

716+
width = tom_clamp_line_buffer_width(current_line_buffer, width, 2, pwidth_scale);
690717
while (width >= pwidth_scale)
691718
{
692719
uint16_t color = (*current_line_buffer++) << 8;
@@ -729,6 +756,7 @@ void tom_render_16bpp_rgb_scanline(uint32_t * backbuffer)
729756
backbuffer += 2 * startPos, width -= startPos;
730757
#endif
731758

759+
width = tom_clamp_line_buffer_width(current_line_buffer, width, 2, pwidth_scale);
732760
while (width >= pwidth_scale)
733761
{
734762
uint32_t color = (*current_line_buffer++) << 8;

0 commit comments

Comments
 (0)