https://github.com/ChenMiaoi created https://github.com/llvm/llvm-project/pull/230937
When processing Microsoft `__pragma(mark ...)`, the preprocessor uses a captured token stream, so no character lexer is available. `HandlePragmaMark()` previously assumed that a character lexer was always present and attempted to read to the end of the line, causing the following code to crash: ``` cc __pragma(mark foo) ``` This patch ignores the mark text when no character lexer is available, consumes the remaining pragma arguments, and returns, avoiding an assertion failure or a null pointer dereference. Split by #228990 >From ea4d4fd036aa20989e3b86f48d7701514fb2f25a Mon Sep 17 00:00:00 2001 From: Chen Miao <[email protected]> Date: Sun, 11 Oct 2026 15:16:20 +0800 Subject: [PATCH] [clang] Handle __pragma(mark) without a character lexer --- clang/docs/ReleaseNotes.md | 3 +++ clang/lib/Lex/Pragma.cpp | 7 ++++++- clang/test/Lexer/pragma-mark.c | 14 ++++++++++++++ clang/unittests/Lex/PPCallbacksTest.cpp | 16 ++++++++++++++++ 4 files changed, 39 insertions(+), 1 deletion(-) diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md index 77a1e73bc8af6..bd54f70c32dc8 100644 --- a/clang/docs/ReleaseNotes.md +++ b/clang/docs/ReleaseNotes.md @@ -947,6 +947,9 @@ features cannot lower the translation-unit ABI level; #### Miscellaneous Clang Crashes Fixed +- Fixed a crash when processing Microsoft `__pragma(mark ...)` expressions + without a character lexer. + - Fixed a crash when Microsoft extensions were enabled and an unterminated `__identifier` expression reached the end of a preprocessing directive or source file. (#GH222310) diff --git a/clang/lib/Lex/Pragma.cpp b/clang/lib/Lex/Pragma.cpp index 078842ab08c5e..6ab33fc80811b 100644 --- a/clang/lib/Lex/Pragma.cpp +++ b/clang/lib/Lex/Pragma.cpp @@ -427,7 +427,12 @@ void Preprocessor::HandlePragmaOnce(Token &OnceTok) { } void Preprocessor::HandlePragmaMark(Token &MarkTok) { - assert(CurPPLexer && "No current lexer?"); + // __pragma uses a captured token stream, so there is no lexer from which to + // read the uninterpreted text. Ignore the mark and consume its arguments. + if (!CurLexer) { + DiscardUntilEndOfDirective(); + return; + } SmallString<64> Buffer; CurLexer->ReadToEndOfLine(&Buffer); diff --git a/clang/test/Lexer/pragma-mark.c b/clang/test/Lexer/pragma-mark.c index e58b2267ddbce..3f4f6f29fd89a 100644 --- a/clang/test/Lexer/pragma-mark.c +++ b/clang/test/Lexer/pragma-mark.c @@ -1,4 +1,6 @@ // RUN: %clang_cc1 %s -fsyntax-only -verify +// RUN: %clang_cc1 %s -fsyntax-only -fms-extensions -DTEST_MS_PRAGMA -verify +// RUN: %clang_cc1 %s -E -fms-extensions -DTEST_MS_PRAGMA -verify | FileCheck %s // expected-no-diagnostics // Lexer diagnostics shouldn't be included in #pragma mark. @@ -10,3 +12,15 @@ X("mark foo ' bar") int i; +#ifdef TEST_MS_PRAGMA +// __pragma has already lexed and expanded its arguments. It must consume the +// captured tokens without requiring a character lexer, including annotations +// introduced by nested pragmas. +__pragma(mark) +__pragma(mark foo) int after_mark; +__pragma(mark foo _Pragma("weak foobar")) int after_nested_mark; +int use_after_marks(void) { return after_mark + after_nested_mark; } +// CHECK: int after_mark; +// CHECK: int after_nested_mark; +// CHECK: int use_after_marks(void) { return after_mark + after_nested_mark; } +#endif diff --git a/clang/unittests/Lex/PPCallbacksTest.cpp b/clang/unittests/Lex/PPCallbacksTest.cpp index 169734f80df47..8a04fe9f78909 100644 --- a/clang/unittests/Lex/PPCallbacksTest.cpp +++ b/clang/unittests/Lex/PPCallbacksTest.cpp @@ -554,6 +554,22 @@ TEST_F(PPCallbacksTest, CollectMarks) { ASSERT_EQ(" - trivia", Marks[3].Trivia); } +TEST_F(PPCallbacksTest, CollectMarksWithMicrosoftPragmas) { + LangOpts.MicrosoftExt = 1; + const char *Source = + "#pragma mark - before\n" + "__pragma(mark ignored)\n" + "__pragma(mark _Pragma(\"mark inner's trivia\") ignored)\n" + "#pragma mark - after\n"; + + auto Marks = PragmaMarkCall(Source); + + ASSERT_EQ(3u, Marks.size()); + EXPECT_EQ(" - before", Marks[0].Trivia); + EXPECT_EQ(" inner's trivia", Marks[1].Trivia); + EXPECT_EQ(" - after", Marks[2].Trivia); +} + TEST_F(PPCallbacksTest, DirectiveExprRanges) { const auto &Results1 = DirectiveExprRange("#if FLUZZY_FLOOF\n#endif\n"); EXPECT_EQ(Results1.size(), 1U); _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
