Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion iosMath/lib/MTMathListBuilder.m
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,15 @@ - (BOOL) hasCharacters
// gets the next character and moves the pointer ahead
- (unichar) getNextCharacter
{
NSAssert([self hasCharacters], @"Retrieving character at index %d beyond length %lu", _currentChar, (unsigned long)_length);
if (![self hasCharacters]) {
// Defense-in-depth: all current call sites pre-check hasCharacters,
// but guard unconditionally so a future missed check cannot produce
// an out-of-bounds heap read in release builds (NS_BLOCK_ASSERTIONS
// compiled out NSAssert, leaving the old code unprotected).
[self setError:MTParseErrorInternalError
message:@"Retrieving character beyond the end of the input"];
return 0;
}
return _chars[_currentChar++];
}

Expand Down
22 changes: 22 additions & 0 deletions iosMathTests/MTMathListBuilderTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -2866,4 +2866,26 @@ - (void)testProgrammaticBothRowsSerializeNested
XCTAssertEqualObjects([MTMathListBuilder mathListToString:list], @"\\underset{b}{\\overset{a}{X}}");
}

// FUN-5: getNextCharacter bounds check (defense-in-depth)
//
// The fix in FUN-5 replaces the NSAssert in getNextCharacter with an
// unconditional guard. Because every call site already pre-checks
// hasCharacters before calling getNextCharacter, the new guard is dead code
// on any currently-reachable path — there is no public API input that
// exercises the past-end branch. This test therefore serves as a regression
// check: it exercises the hot path of getNextCharacter heavily (via \frac
// parsing, which calls getNextCharacter many times) and confirms that the
// unconditional guard does not alter normal parsing behavior.
- (void)testGetNextCharacterBoundsGuardDoesNotAffectValidParsing
{
// A multi-token expression that forces getNextCharacter through many
// iterations, including inside nested fraction arguments.
NSError *error = nil;
MTMathList *list = [MTMathListBuilder buildFromString:@"\\frac{1+x}{2}" error:&error];
XCTAssertNil(error, @"Valid \\frac expression must parse without error");
XCTAssertNotNil(list, @"Valid \\frac expression must produce a non-nil list");
XCTAssertEqual(list.atoms.count, (NSUInteger)1);
XCTAssertEqual(((MTMathAtom *)list.atoms[0]).type, kMTMathAtomFraction);
}

@end
Loading