From dbedc1cb2ccd8e6b94b4d8f575722551af00705a Mon Sep 17 00:00:00 2001 From: Paul Kirth Date: Fri, 7 Nov 2025 10:51:39 -0800 Subject: [PATCH 1/5] [clang] Fix possible nullptr deref in BuildCXXNestedNameSpecifier There is a possible nullptr deref in BuildCXXNestedNameSpecifier when calling ExtendNestedNameSpecifier or using isa<>. This initially showed up as a crash in clangd, that didn't manifest in when compiling w/ clang. The reduced test case added in this patch, however does expose the issue in clang. Testing locally shows that both this test case and the original clangd issue are fixed by checking the validity of the pointer before trying to dispatch. Since all code paths require the pointer to be valid (usually by virtue of a dyn_cast or isa<> check), there should be no functional difference. Fixes #166843 --- clang/lib/Sema/SemaCXXScopeSpec.cpp | 39 +++++++++++++++-------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/clang/lib/Sema/SemaCXXScopeSpec.cpp b/clang/lib/Sema/SemaCXXScopeSpec.cpp index c52fc5bf815af..29e697d9eb029 100644 --- a/clang/lib/Sema/SemaCXXScopeSpec.cpp +++ b/clang/lib/Sema/SemaCXXScopeSpec.cpp @@ -779,25 +779,26 @@ bool Sema::BuildCXXNestedNameSpecifier(Scope *S, NestedNameSpecInfo &IdInfo, } if (!Found.empty()) { - const auto *ND = Found.getAsSingle(); - if (::ExtendNestedNameSpecifier(*this, SS, ND, IdInfo.IdentifierLoc, - IdInfo.CCLoc)) { - const Type *T = SS.getScopeRep().getAsType(); - Diag(IdInfo.IdentifierLoc, diag::err_expected_class_or_namespace) - << QualType(T, 0) << getLangOpts().CPlusPlus; - // Recover with this type if it would be a valid nested name specifier. - return !T->getAsCanonical(); - } - if (isa(ND)) { - ParsedType SuggestedType; - DiagnoseUnknownTypeName(IdInfo.Identifier, IdInfo.IdentifierLoc, S, &SS, - SuggestedType); - } else { - Diag(IdInfo.IdentifierLoc, diag::err_expected_class_or_namespace) - << IdInfo.Identifier << getLangOpts().CPlusPlus; - if (NamedDecl *ND = Found.getAsSingle()) - Diag(ND->getLocation(), diag::note_entity_declared_at) - << IdInfo.Identifier; + if (const auto *ND = Found.getAsSingle()) { + if (::ExtendNestedNameSpecifier(*this, SS, ND, IdInfo.IdentifierLoc, + IdInfo.CCLoc)) { + const Type *T = SS.getScopeRep().getAsType(); + Diag(IdInfo.IdentifierLoc, diag::err_expected_class_or_namespace) + << QualType(T, 0) << getLangOpts().CPlusPlus; + // Recover with this type if it would be a valid nested name specifier. + return !T->getAsCanonical(); + } + if (isa(ND)) { + ParsedType SuggestedType; + DiagnoseUnknownTypeName(IdInfo.Identifier, IdInfo.IdentifierLoc, S, &SS, + SuggestedType); + } else { + Diag(IdInfo.IdentifierLoc, diag::err_expected_class_or_namespace) + << IdInfo.Identifier << getLangOpts().CPlusPlus; + if (NamedDecl *ND = Found.getAsSingle()) + Diag(ND->getLocation(), diag::note_entity_declared_at) + << IdInfo.Identifier; + } } } else if (SS.isSet()) Diag(IdInfo.IdentifierLoc, diag::err_no_member) << IdInfo.Identifier From be21d3d0f40b7d0e6e6938785547383da2ced784 Mon Sep 17 00:00:00 2001 From: Paul Kirth Date: Fri, 7 Nov 2025 11:19:51 -0800 Subject: [PATCH 2/5] Prefer early return to avoid excessive diff --- clang/lib/Sema/SemaCXXScopeSpec.cpp | 41 +++++++++++++++-------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/clang/lib/Sema/SemaCXXScopeSpec.cpp b/clang/lib/Sema/SemaCXXScopeSpec.cpp index 29e697d9eb029..81ff2ef69ef37 100644 --- a/clang/lib/Sema/SemaCXXScopeSpec.cpp +++ b/clang/lib/Sema/SemaCXXScopeSpec.cpp @@ -779,26 +779,27 @@ bool Sema::BuildCXXNestedNameSpecifier(Scope *S, NestedNameSpecInfo &IdInfo, } if (!Found.empty()) { - if (const auto *ND = Found.getAsSingle()) { - if (::ExtendNestedNameSpecifier(*this, SS, ND, IdInfo.IdentifierLoc, - IdInfo.CCLoc)) { - const Type *T = SS.getScopeRep().getAsType(); - Diag(IdInfo.IdentifierLoc, diag::err_expected_class_or_namespace) - << QualType(T, 0) << getLangOpts().CPlusPlus; - // Recover with this type if it would be a valid nested name specifier. - return !T->getAsCanonical(); - } - if (isa(ND)) { - ParsedType SuggestedType; - DiagnoseUnknownTypeName(IdInfo.Identifier, IdInfo.IdentifierLoc, S, &SS, - SuggestedType); - } else { - Diag(IdInfo.IdentifierLoc, diag::err_expected_class_or_namespace) - << IdInfo.Identifier << getLangOpts().CPlusPlus; - if (NamedDecl *ND = Found.getAsSingle()) - Diag(ND->getLocation(), diag::note_entity_declared_at) - << IdInfo.Identifier; - } + const auto *ND = Found.getAsSingle(); + if(!ND) + return true; + if (::ExtendNestedNameSpecifier(*this, SS, ND, IdInfo.IdentifierLoc, + IdInfo.CCLoc)) { + const Type *T = SS.getScopeRep().getAsType(); + Diag(IdInfo.IdentifierLoc, diag::err_expected_class_or_namespace) + << QualType(T, 0) << getLangOpts().CPlusPlus; + // Recover with this type if it would be a valid nested name specifier. + return !T->getAsCanonical(); + } + if (isa(ND)) { + ParsedType SuggestedType; + DiagnoseUnknownTypeName(IdInfo.Identifier, IdInfo.IdentifierLoc, S, &SS, + SuggestedType); + } else { + Diag(IdInfo.IdentifierLoc, diag::err_expected_class_or_namespace) + << IdInfo.Identifier << getLangOpts().CPlusPlus; + if (NamedDecl *ND = Found.getAsSingle()) + Diag(ND->getLocation(), diag::note_entity_declared_at) + << IdInfo.Identifier; } } else if (SS.isSet()) Diag(IdInfo.IdentifierLoc, diag::err_no_member) << IdInfo.Identifier From 5796fc8f877878f69babf9c163242895761a13f1 Mon Sep 17 00:00:00 2001 From: Paul Kirth Date: Fri, 7 Nov 2025 11:22:00 -0800 Subject: [PATCH 3/5] Add missing test case that wasn't uploaded --- clang/test/Sema/PR166843.cpp | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 clang/test/Sema/PR166843.cpp diff --git a/clang/test/Sema/PR166843.cpp b/clang/test/Sema/PR166843.cpp new file mode 100644 index 0000000000000..9160407677ab0 --- /dev/null +++ b/clang/test/Sema/PR166843.cpp @@ -0,0 +1,7 @@ +// RUN: %clang_cc1 -fsyntax-only %s -verify +namespace a { +template +void c() { + ((::c::)); // expected-error {{expected unqualified-id}} +} +} From 1f6e1b64809cb756f3c43e4a0d4338b4995d57d7 Mon Sep 17 00:00:00 2001 From: Paul Kirth Date: Fri, 7 Nov 2025 11:26:01 -0800 Subject: [PATCH 4/5] Fix formatting --- clang/lib/Sema/SemaCXXScopeSpec.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/Sema/SemaCXXScopeSpec.cpp b/clang/lib/Sema/SemaCXXScopeSpec.cpp index 81ff2ef69ef37..926511afe1450 100644 --- a/clang/lib/Sema/SemaCXXScopeSpec.cpp +++ b/clang/lib/Sema/SemaCXXScopeSpec.cpp @@ -780,7 +780,7 @@ bool Sema::BuildCXXNestedNameSpecifier(Scope *S, NestedNameSpecInfo &IdInfo, if (!Found.empty()) { const auto *ND = Found.getAsSingle(); - if(!ND) + if (!ND) return true; if (::ExtendNestedNameSpecifier(*this, SS, ND, IdInfo.IdentifierLoc, IdInfo.CCLoc)) { From 710b26202c06df7edf7b36a07f2c4cff3f75ce0c Mon Sep 17 00:00:00 2001 From: Paul Kirth Date: Fri, 7 Nov 2025 13:46:52 -0800 Subject: [PATCH 5/5] Ensure correct diagnostic still gets used --- clang/lib/Sema/SemaCXXScopeSpec.cpp | 5 ++++- clang/test/Sema/PR166843.cpp | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/clang/lib/Sema/SemaCXXScopeSpec.cpp b/clang/lib/Sema/SemaCXXScopeSpec.cpp index 926511afe1450..17ae7ca5627a9 100644 --- a/clang/lib/Sema/SemaCXXScopeSpec.cpp +++ b/clang/lib/Sema/SemaCXXScopeSpec.cpp @@ -780,8 +780,11 @@ bool Sema::BuildCXXNestedNameSpecifier(Scope *S, NestedNameSpecInfo &IdInfo, if (!Found.empty()) { const auto *ND = Found.getAsSingle(); - if (!ND) + if (!ND) { + Diag(IdInfo.IdentifierLoc, diag::err_expected_class_or_namespace) + << IdInfo.Identifier << getLangOpts().CPlusPlus; return true; + } if (::ExtendNestedNameSpecifier(*this, SS, ND, IdInfo.IdentifierLoc, IdInfo.CCLoc)) { const Type *T = SS.getScopeRep().getAsType(); diff --git a/clang/test/Sema/PR166843.cpp b/clang/test/Sema/PR166843.cpp index 9160407677ab0..5a6223bccc27e 100644 --- a/clang/test/Sema/PR166843.cpp +++ b/clang/test/Sema/PR166843.cpp @@ -2,6 +2,6 @@ namespace a { template void c() { - ((::c::)); // expected-error {{expected unqualified-id}} + ((::c::x)); // expected-error {{'c' is not a class, namespace, or enumeration}} } }