Skip to content

Commit c128fd9

Browse files
authored
[Clang] prevent crash on invalid nested name specifiers with a single colon (#169246)
Fixes #167905 --- This patch addresses an issue where invalid nested name specifier sequences containing a single colon (`a:c::`) could be treated during recovery as valid scope specifiers, which in turn led to a crash https://github.com/llvm/llvm-project/blob/c543615744d61e0967b956c402e310946d741570/clang/lib/Parse/ParseExprCXX.cpp#L404-L418 For malformed inputs like `a:c::`, the single colon recovery incorrectly triggers and produces an `annot_cxxscope`. When tentative parsing later runs https://github.com/llvm/llvm-project/blob/996213c6ea0dc2e47624c6b06c0833a882c1c1f7/clang/lib/Parse/ParseTentative.cpp#L1739-L1740 the classifier returns `Ambiguous`, which doesn't stop parsing. The parser then enters the https://github.com/llvm/llvm-project/blob/996213c6ea0dc2e47624c6b06c0833a882c1c1f7/clang/lib/Parse/ParseTentative.cpp#L1750-L1752 and consumes the invalid scope annotation, eventually reaching `EOF` and crashing.
1 parent d0f5a49 commit c128fd9

File tree

4 files changed

+20
-2
lines changed

4 files changed

+20
-2
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -742,6 +742,8 @@ Crash and bug fixes
742742
``[[assume(expr)]]`` attribute was enclosed in parentheses. (#GH151529)
743743
- Fixed a crash when parsing ``#embed`` parameters with unmatched closing brackets. (#GH152829)
744744
- Fixed a crash when compiling ``__real__`` or ``__imag__`` unary operator on scalar value with type promotion. (#GH160583)
745+
- Fixed a crash when parsing invalid nested name specifier sequences
746+
containing a single colon. (#GH167905)
745747

746748
Improvements
747749
^^^^^^^^^^^^

clang/include/clang/Lex/Token.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,13 +100,19 @@ class Token {
100100
/// is/isNot - Predicates to check if this token is a specific kind, as in
101101
/// "if (Tok.is(tok::l_brace)) {...}".
102102
bool is(tok::TokenKind K) const { return Kind == K; }
103-
bool isNot(tok::TokenKind K) const { return Kind != K; }
104103
template <typename... Ts> bool isOneOf(Ts... Ks) const {
105104
static_assert(sizeof...(Ts) > 0,
106105
"requires at least one tok::TokenKind specified");
107106
return (is(Ks) || ...);
108107
}
109108

109+
bool isNot(tok::TokenKind K) const { return Kind != K; }
110+
template <typename... Ts> bool isNoneOf(Ts... Ks) const {
111+
static_assert(sizeof...(Ts) > 0,
112+
"requires at least one tok::TokenKind specified");
113+
return (isNot(Ks) && ...);
114+
}
115+
110116
/// Return true if this is a raw identifier (when lexing
111117
/// in raw mode) or a non-keyword identifier (when lexing in non-raw mode).
112118
bool isAnyIdentifier() const {

clang/lib/Parse/ParseTentative.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1063,7 +1063,7 @@ Parser::isCXXDeclarationSpecifier(ImplicitTypenameContext AllowImplicitTypename,
10631063
return TPResult::False;
10641064
}
10651065

1066-
if (Next.isNot(tok::coloncolon) && Next.isNot(tok::less)) {
1066+
if (Next.isNoneOf(tok::coloncolon, tok::less, tok::colon)) {
10671067
// Determine whether this is a valid expression. If not, we will hit
10681068
// a parse error one way or another. In that case, tell the caller that
10691069
// this is ambiguous. Typo-correct to type and expression keywords and
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// RUN: %clang_cc1 -fsyntax-only -verify %s
2+
3+
namespace a { b c ( a:c::
4+
// expected-error@-1 {{unknown type name 'b'}}
5+
// expected-error@-2 {{unexpected ':' in nested name specifier; did you mean '::'?}}
6+
// expected-error@-3 {{no member named 'c' in namespace 'a'}}
7+
// expected-error@-4 {{expected ';' after top level declarator}}
8+
// expected-note@-5 {{to match this '{'}}
9+
// expected-error@+1 {{expected unqualified-id}} \
10+
// expected-error@+1 {{expected '}'}}

0 commit comments

Comments
 (0)