[Parse] Fix buffer overrun in advanceIfMultilineDelimiter

Make sure we don't scan off the end of the buffer, and scan for the
delimiter before attempting the lookahead.
This commit is contained in:
Hamish Knight
2025-12-07 15:15:48 +00:00
parent 8da3b62d7b
commit 0068438385
2 changed files with 35 additions and 19 deletions

View File

@@ -1379,14 +1379,27 @@ static bool delimiterMatches(unsigned CustomDelimiterLen, const char *&BytesPtr,
/// advanceIfMultilineDelimiter - Centralized check for multiline delimiter.
static bool advanceIfMultilineDelimiter(unsigned CustomDelimiterLen,
const char *&CurPtr,
const char *&CurPtr, const char *EndPtr,
DiagnosticEngine *Diags,
bool IsOpening = false) {
auto scanDelimiter = [&]() -> const char * {
// CurPtr here points to the character after `"`.
const char *TmpPtr = CurPtr;
if (*(TmpPtr - 1) == '"' &&
diagnoseZeroWidthMatchAndAdvance('"', TmpPtr, Diags) &&
diagnoseZeroWidthMatchAndAdvance('"', TmpPtr, Diags)) {
return TmpPtr;
}
return nullptr;
};
auto *DelimEnd = scanDelimiter();
if (!DelimEnd)
return false;
// Test for single-line string literals that resemble multiline delimiter.
const char *TmpPtr = CurPtr + 1;
const char *TmpPtr = DelimEnd - 1;
if (IsOpening && CustomDelimiterLen) {
while (*TmpPtr != '\r' && *TmpPtr != '\n') {
while (TmpPtr != EndPtr && *TmpPtr != '\r' && *TmpPtr != '\n') {
if (*TmpPtr == '"') {
if (delimiterMatches(CustomDelimiterLen, ++TmpPtr, nullptr)) {
return false;
@@ -1397,15 +1410,8 @@ static bool advanceIfMultilineDelimiter(unsigned CustomDelimiterLen,
}
}
TmpPtr = CurPtr;
if (*(TmpPtr - 1) == '"' &&
diagnoseZeroWidthMatchAndAdvance('"', TmpPtr, Diags) &&
diagnoseZeroWidthMatchAndAdvance('"', TmpPtr, Diags)) {
CurPtr = TmpPtr;
return true;
}
return false;
CurPtr = DelimEnd;
return true;
}
/// lexCharacter - Read a character and return its UTF32 code. If this is the
@@ -1450,8 +1456,8 @@ unsigned Lexer::lexCharacter(const char *&CurPtr, char StopQuote,
DiagnosticEngine *D = EmitDiagnostics ? getTokenDiags() : nullptr;
auto TmpPtr = CurPtr;
if (IsMultilineString &&
!advanceIfMultilineDelimiter(CustomDelimiterLen, TmpPtr, D))
if (IsMultilineString && !advanceIfMultilineDelimiter(
CustomDelimiterLen, TmpPtr, BufferEnd, D))
return '"';
if (CustomDelimiterLen &&
!delimiterMatches(CustomDelimiterLen, TmpPtr, D, /*IsClosing=*/true))
@@ -1587,9 +1593,8 @@ static const char *skipToEndOfInterpolatedExpression(const char *CurPtr,
if (!inStringLiteral()) {
// Open string literal.
OpenDelimiters.push_back(CurPtr[-1]);
AllowNewline.push_back(advanceIfMultilineDelimiter(CustomDelimiterLen,
CurPtr, nullptr,
true));
AllowNewline.push_back(advanceIfMultilineDelimiter(
CustomDelimiterLen, CurPtr, EndPtr, nullptr, true));
CustomDelimiter.push_back(CustomDelimiterLen);
continue;
}
@@ -1602,7 +1607,8 @@ static const char *skipToEndOfInterpolatedExpression(const char *CurPtr,
// Multi-line string can only be closed by '"""'.
if (AllowNewline.back() &&
!advanceIfMultilineDelimiter(CustomDelimiterLen, CurPtr, nullptr))
!advanceIfMultilineDelimiter(CustomDelimiterLen, CurPtr, EndPtr,
nullptr))
continue;
// Check whether we have equivalent number of '#'s.
@@ -1947,7 +1953,7 @@ void Lexer::lexStringLiteral(unsigned CustomDelimiterLen) {
assert((QuoteChar == '"' || QuoteChar == '\'') && "Unexpected start");
bool IsMultilineString = advanceIfMultilineDelimiter(
CustomDelimiterLen, CurPtr, getTokenDiags(), true);
CustomDelimiterLen, CurPtr, BufferEnd, getTokenDiags(), true);
if (IsMultilineString && *CurPtr != '\n' && *CurPtr != '\r')
diagnose(CurPtr, diag::lex_illegal_multiline_string_start)
.fixItInsert(Lexer::getSourceLoc(CurPtr), "\n");

View File

@@ -0,0 +1,10 @@
// RUN: %empty-directory(%t)
// RUN: echo '#"' > %t/main1.swift
// RUN: echo -n '#"' > %t/main2.swift
// RUN: env DYLD_INSERT_LIBRARIES=/usr/lib/libgmalloc.dylib not %target-swift-frontend -typecheck %t/main1.swift
// RUN: env DYLD_INSERT_LIBRARIES=/usr/lib/libgmalloc.dylib not %target-swift-frontend -typecheck %t/main2.swift
// guardmalloc is incompatible with ASAN
// REQUIRES: no_asan