Skip to content

Commit 8ab3caf

Browse files
authored
[Clang][Parser] Don't always destroy template annotations at the end of a declaration (#89494)
Since [6163aa9](6163aa9#diff-3a7ef0bff7d2b73b4100de636f09ea68b72eda191b39c8091a6a1765d917c1a2), we have introduced an optimization that almost always destroys TemplateIdAnnotations at the end of a function declaration. This doesn't always work properly: a lambda within a default template argument could also result in such deallocation and hence a use-after-free bug while building a type constraint on the template parameter. This patch adds another flag to the parser to tell apart cases when we shouldn't do such cleanups eagerly. A bit complicated as it is, this retains the optimization on a highly templated function with lots of generic lambdas. Note the test doesn't always trigger a conspicuous bug/crash even with a debug build. But a sanitizer build can detect them, I believe. Fixes #67235 Fixes #89127
1 parent 304dfe1 commit 8ab3caf

File tree

4 files changed

+49
-1
lines changed

4 files changed

+49
-1
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,7 @@ Bug Fixes to C++ Support
560560
- Fix the Itanium mangling of lambdas defined in a member of a local class (#GH88906)
561561
- Fixed a crash when trying to evaluate a user-defined ``static_assert`` message whose ``size()``
562562
function returns a large or negative value. Fixes (#GH89407).
563+
- Fixed a use-after-free bug in parsing of type constraints with default arguments that involve lambdas. (#GH67235)
563564

564565
Bug Fixes to AST Handling
565566
^^^^^^^^^^^^^^^^^^^^^^^^^

clang/include/clang/Parse/Parser.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,15 @@ class Parser : public CodeCompletionHandler {
313313
/// top-level declaration is finished.
314314
SmallVector<TemplateIdAnnotation *, 16> TemplateIds;
315315

316+
/// Don't destroy template annotations in MaybeDestroyTemplateIds even if
317+
/// we're at the end of a declaration. Instead, we defer the destruction until
318+
/// after a top-level declaration.
319+
/// Use DelayTemplateIdDestructionRAII rather than setting it directly.
320+
bool DelayTemplateIdDestruction = false;
321+
316322
void MaybeDestroyTemplateIds() {
323+
if (DelayTemplateIdDestruction)
324+
return;
317325
if (!TemplateIds.empty() &&
318326
(Tok.is(tok::eof) || !PP.mightHavePendingAnnotationTokens()))
319327
DestroyTemplateIds();
@@ -329,6 +337,22 @@ class Parser : public CodeCompletionHandler {
329337
~DestroyTemplateIdAnnotationsRAIIObj() { Self.MaybeDestroyTemplateIds(); }
330338
};
331339

340+
struct DelayTemplateIdDestructionRAII {
341+
Parser &Self;
342+
bool PrevDelayTemplateIdDestruction;
343+
344+
DelayTemplateIdDestructionRAII(Parser &Self,
345+
bool DelayTemplateIdDestruction) noexcept
346+
: Self(Self),
347+
PrevDelayTemplateIdDestruction(Self.DelayTemplateIdDestruction) {
348+
Self.DelayTemplateIdDestruction = DelayTemplateIdDestruction;
349+
}
350+
351+
~DelayTemplateIdDestructionRAII() noexcept {
352+
Self.DelayTemplateIdDestruction = PrevDelayTemplateIdDestruction;
353+
}
354+
};
355+
332356
/// Identifiers which have been declared within a tentative parse.
333357
SmallVector<const IdentifierInfo *, 8> TentativelyDeclaredIdentifiers;
334358

clang/lib/Parse/ParseTemplate.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -733,7 +733,12 @@ NamedDecl *Parser::ParseTypeParameter(unsigned Depth, unsigned Position) {
733733
// we introduce the type parameter into the local scope.
734734
SourceLocation EqualLoc;
735735
ParsedType DefaultArg;
736+
std::optional<DelayTemplateIdDestructionRAII> DontDestructTemplateIds;
736737
if (TryConsumeToken(tok::equal, EqualLoc)) {
738+
// The default argument might contain a lambda declaration; avoid destroying
739+
// parsed template ids at the end of that declaration because they can be
740+
// used in a type constraint later.
741+
DontDestructTemplateIds.emplace(*this, /*DelayTemplateIdDestruction=*/true);
737742
// The default argument may declare template parameters, notably
738743
// if it contains a generic lambda, so we need to increase
739744
// the template depth as these parameters would not be instantiated

clang/test/Parser/cxx2a-constrained-template-param.cpp

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,22 @@ namespace temp
4949
template<C1 TT, C1 UU = test1> // expected-error{{use of class template 'test1' requires template arguments}}
5050
// expected-error@-1 2{{concept named in type constraint is not a type concept}}
5151
using A = TT<int>; // expected-error{{expected ';' after alias declaration}}
52-
}
52+
}
53+
54+
namespace PR67235 {
55+
56+
template <class T>
57+
concept C = true;
58+
59+
template <auto D>
60+
struct S {};
61+
62+
// Don't destroy annotation 'C' at the end of the lambda; else we'll run into a
63+
// use-after-free bug while constructing the type constraint 'C' on 'Default'.
64+
template <typename Ret, C Default = decltype([] { return Ret(); })>
65+
void func() {}
66+
67+
template <typename Ret, C Default = S<[] { return Ret(); }>>
68+
void func2() {}
69+
70+
}

0 commit comments

Comments
 (0)