https://github.com/AZero13 updated https://github.com/llvm/llvm-project/pull/221102
>From 536b616a3df156247daa1db7e10d19c83f8584fc Mon Sep 17 00:00:00 2001
From: AZero13 <gfunni234 at gmail.com>
Date: Thu, 3 Sep 2026 19:44:13 -0400
Subject: [PATCH] [Clang][Parse] Fix heap-use-after-free when cleaning up
TemplateIds
When parsing deeply nested statement expressions, the preprocessor
may exit caching mode while the parser's current token is still
an `annot_template_id`. When `MaybeDestroyTemplateIds()` runs at
the end of a statement, it aggressively destroyed all template ID
annotations because caching mode was off. This left the current
token holding a dangling pointer to a freed `TemplateIdAnnotation`,
causing a crash during error recovery when extracting the
`TemplateNameLoc`.
This patch adds a check to ensure we don't destroy `TemplateIds` if
the parser is actively holding onto an `annot_template_id` in `Tok`.
Fixes #175702
---
clang/include/clang/Lex/Preprocessor.h | 6 +-
.../cxx-template-id-tentative-uaf-crash.cpp | 81 +++++++++++++++++++
2 files changed, 85 insertions(+), 2 deletions(-)
create mode 100644 clang/test/Parser/cxx-template-id-tentative-uaf-crash.cpp
diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h
index d94f3d2cbe8ed..c1c4e84771dcf 100644
--- a/clang/include/clang/Lex/Preprocessor.h
+++ b/clang/include/clang/Lex/Preprocessor.h
@@ -2051,9 +2051,11 @@ class Preprocessor {
void *AnnotationVal);
/// Determine whether it's possible for a future call to Lex to produce an
- /// annotation token created by a previous call to EnterAnnotationToken.
+ /// annotation token created by a previous call to EnterAnnotationToken,
+ /// or if the parser is currently holding an annotation token that was just
+ /// retrieved from the cache.
bool mightHavePendingAnnotationTokens() {
- return CurLexerCallback != CLK_Lexer;
+ return CurLexerCallback != CLK_Lexer || !CachedTokens.empty();
}
/// Update the current token to represent the provided
diff --git a/clang/test/Parser/cxx-template-id-tentative-uaf-crash.cpp b/clang/test/Parser/cxx-template-id-tentative-uaf-crash.cpp
new file mode 100644
index 0000000000000..3e7d309c7d312
--- /dev/null
+++ b/clang/test/Parser/cxx-template-id-tentative-uaf-crash.cpp
@@ -0,0 +1,81 @@
+// RUN: not %clang_cc1 -fsyntax-only -Wno-everything %s
+
+
+int main() {
+ typedef struct tree_el node i, sum = 0;
+ for (i = 1; i <= LAST; i++) {
+ sum += i;
+ }
+ printf("sum = %d\n", sum);
+ return printout(printf(printf(
+ i < cnt, cnt & (printout(printf(printf(
+ i < cnt,
+ 0x5b * printf(i < cnt,
+ 0x5b * (({ cnt & (printf(printf(
+ i < cnt,
+ 0x5b * (1 / (1 / (cnt & (printout(
+ printf(i < cnt, 0x5b * (1 / (({
+ if ("%f%f%f")
+ break;
+ -((0.0 / (0xaf >> 3 &
+ (((0xaf >> 3 &
+ (({ for (i = 0, bi = i < 10; i < length;
+ ++i, ++bi)
+ if (cnt < (0xaf >> 3 & 1) >> 3)
+ break;
+ cnt < (0xaf >> 3 & 1) >> 3;
+ })))
+ >> 0xb9 & 1)
+ >> 0xb9 & 1)))))
+ ;
+ })))
+ ) >>
+ 3 &
+ 1)))
+ )))) >>
+ 3 &
+ 1)))
+ ) >>
+ 3 &
+ 1);
+ return 0;
+ i;
+ return (node())->val;
+})))
+ ))),
+ sum) >>
+ 3 &
+ 1);
+ return ((((({ cnt & (printf(printf(
+ i < cnt,
+ 0x5b * (({ cnt & (printf(printf(
+ i < cnt,
+ 0x5b * (1 / (1 / (cnt & (printout(
+ printf(i < cnt, 0x5b * (1 / (({
+ if ("%f%f%f")
+ break;
+ -((0.0 / (0xaf >> 3 &
+ (((0xaf >> 3 &
+ (({ for (i = 0, bi = i < 10; i < length; ++i, ++bi)
+ if (cnt < (0xaf >> 3 & 1) >> 3)
+ break;
+ cnt < (0xaf >> 3 & 1) >> 3;
+ })))
+ >> 0xb9 & 1)
+ >> 0xb9 & 1)))))
+ ;
+ })))
+ ) >>
+ 3 &
+ 1)))
+ )))) >>
+ 3 &
+ 1)))
+ ) >>
+ 3 &
+ 1)))
+ ,
+ sum)) >>
+ 3 &
+ 1)));
+}