fix(FUN-6): NULL-check malloc at three sites to prevent NULL-deref crashes#238
fix(FUN-6): NULL-check malloc at three sites to prevent NULL-deref crashes#238kostub wants to merge 1 commit into
Conversation
…ashes - MTMathListBuilder initWithString: guards malloc(_length*sizeof(unichar)) and degrades to empty input (instead of dereferencing NULL) on failure - MTGlyphConstructionDisplay and MTHorizontalGlyphAssemblyDisplay guard their glyph/position allocations; set _numGlyphs=0 on failure so draw: becomes a safe no-op (CTFontDrawGlyphs with count 0) - Add testEmptyInputParsesToEmptyList to lock in the empty-input contract (same terminal state as the parser OOM fallback); malloc-NULL branches are verified by inspection per solution.md Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Code Review
This pull request introduces safety checks for memory allocation failures and empty inputs in MTMathListBuilder and MTMathListDisplay, along with a new unit test for empty input parsing. The feedback suggests optimizing the memory allocation logic in MTMathListDisplay by wrapping the malloc calls in a check to ensure _numGlyphs is greater than zero, which avoids redundant allocations and potential implementation-defined behavior when the glyph count is zero.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| _glyphs = malloc(sizeof(CGGlyph) * _numGlyphs); | ||
| _positions = malloc(sizeof(CGPoint) * _numGlyphs); | ||
| if (_numGlyphs > 0 && (!_glyphs || !_positions)) { | ||
| free(_glyphs); _glyphs = NULL; | ||
| free(_positions); _positions = NULL; | ||
| _numGlyphs = 0; // nothing to draw; CTFontDrawGlyphs is a no-op for count 0 | ||
| } |
There was a problem hiding this comment.
Calling malloc when _numGlyphs is 0 is unnecessary and can be avoided entirely. malloc(0) has implementation-defined behavior (it may return NULL or a non-NULL pointer that should not be dereferenced but must be freed). Wrapping the allocations in a _numGlyphs > 0 check avoids these redundant calls and potential edge cases.
if (_numGlyphs > 0) {
_glyphs = malloc(sizeof(CGGlyph) * _numGlyphs);
_positions = malloc(sizeof(CGPoint) * _numGlyphs);
if (!_glyphs || !_positions) {
free(_glyphs); _glyphs = NULL;
free(_positions); _positions = NULL;
_numGlyphs = 0;
}
}| _glyphs = malloc(sizeof(CGGlyph) * _numGlyphs); | ||
| _positions = malloc(sizeof(CGPoint) * _numGlyphs); | ||
| if (_numGlyphs > 0 && (!_glyphs || !_positions)) { | ||
| free(_glyphs); _glyphs = NULL; | ||
| free(_positions); _positions = NULL; | ||
| _numGlyphs = 0; // nothing to draw; CTFontDrawGlyphs is a no-op for count 0 | ||
| } |
There was a problem hiding this comment.
Calling malloc when _numGlyphs is 0 is unnecessary and can be avoided entirely. malloc(0) has implementation-defined behavior (it may return NULL or a non-NULL pointer that should not be dereferenced but must be freed). Wrapping the allocations in a _numGlyphs > 0 check avoids these redundant calls and potential edge cases.
if (_numGlyphs > 0) {
_glyphs = malloc(sizeof(CGGlyph) * _numGlyphs);
_positions = malloc(sizeof(CGPoint) * _numGlyphs);
if (!_glyphs || !_positions) {
free(_glyphs); _glyphs = NULL;
free(_positions); _positions = NULL;
_numGlyphs = 0;
}
}|
EM-REVIEW v1 Code Review: FUN-6 NULL-check malloc at three sites. Scope reviewed: full diff plus surrounding source in MTMathListBuilder.m (initWithString:, dealloc, getNextCharacter) and both glyph-assembly initializers in MTMathListDisplay.m. Verdict: No blocking issues. The change is correct, minimal, and consistent with existing patterns. Correctness:
Minor / non-blocking:
Summary: All three NULL-checks are correct; edge cases (length 0, partial allocation failure, free-of-NULL in dealloc) are handled properly. Tests pass per the PR description. No changes required to merge. Not approving per review protocol. |
Summary
Fixes FUN-6 (issues.md#L233) — three
malloccalls wrote to the returned buffer immediately without a NULL check.MTMathListBuilder.m(parser, realistic risk):initWithString:now guardsmalloc(sizeof(unichar)*_length). On NULL, sets_length = 0and skipsgetCharacters:, degrading to an emptyMTMathListinstead of NULL-dereferencing.deallocalready doesfree(_chars);free(NULL)is a safe no-op.MTMathListDisplay.m—MTGlyphConstructionDisplay(low-risk, hardened for consistency): guards_glyphs/_positionsallocations; on failure sets_numGlyphs = 0and frees any partial allocation sodraw:becomes aCTFontDrawGlyphs(..., 0, ...)no-op.MTMathListDisplay.m—MTHorizontalGlyphAssemblyDisplay(same pattern): identical guard applied to the second glyph-assembly initializer.Net change: ~+16 LOC across 2 files, no API or architecture change.
Test plan
swift build— clean build, no warnings introducedswift test— 293 tests, 0 failurestestEmptyInputParsesToEmptyListadded toMTMathListBuilderTest— parses@"", asserts zero atoms and no error, locking in the "degrade to empty" contract that the malloc-failure fallback also producesmalloc-returns-NULL branches verified by code inspection (deterministically forcing malloc failure in XCTest requires a malloc interposer and is platform-fragile; documented in solution.md)🤖 Generated with Claude Code