-
Notifications
You must be signed in to change notification settings - Fork 15.1k
[clang] Fix possible nullptr deref in BuildCXXNestedNameSpecifier #166995
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
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 llvm#166843
|
@llvm/pr-subscribers-clang Author: Paul Kirth (ilovepi) ChangesThere 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 Full diff: https://github.com/llvm/llvm-project/pull/166995.diff 1 Files Affected:
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<NamedDecl>();
- 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<TagType>();
- }
- if (isa<TemplateDecl>(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<NamedDecl>())
- Diag(ND->getLocation(), diag::note_entity_declared_at)
- << IdInfo.Identifier;
+ if (const auto *ND = Found.getAsSingle<NamedDecl>()) {
+ 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<TagType>();
+ }
+ if (isa<TemplateDecl>(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<NamedDecl>())
+ Diag(ND->getLocation(), diag::note_entity_declared_at)
+ << IdInfo.Identifier;
+ }
}
} else if (SS.isSet())
Diag(IdInfo.IdentifierLoc, diag::err_no_member) << IdInfo.Identifier
|
mizvekov
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for working on this.
Please include test case.
clang/lib/Sema/SemaCXXScopeSpec.cpp
Outdated
| if (NamedDecl *ND = Found.getAsSingle<NamedDecl>()) | ||
| Diag(ND->getLocation(), diag::note_entity_declared_at) | ||
| << IdInfo.Identifier; | ||
| if (const auto *ND = Found.getAsSingle<NamedDecl>()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please bail out early to reduce the diff.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done.
Thanks for pointing that out. I forgot to add it to git 🤦 |
mizvekov
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, Thanks.
|
✅ With the latest revision this PR passed the C/C++ code formatter. |
clang/test/Sema/PR166843.cpp
Outdated
| namespace a { | ||
| template <class b> | ||
| void c() { | ||
| ((::c::)); // expected-error {{expected unqualified-id}} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks like we aren't actually diagnosing the error? I'd expect something like error: 'c' is not a class, namespace, or enumeration, which is what we generate in the non-template case.
What happens if you instead write the following?
((::c::x));
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks like we aren't actually diagnosing the error? I'd expect something like
error: 'c' is not a class, namespace, or enumeration, which is what we generate in the non-template case.
Yeah, that was something I wasn't sure about.
What happens if you instead write the following?
((::c::x));
Looks like we report nothing, the way I've structured it. That version still causes the crash w/o the nullptr check, so let me update the check to make sure it works in the expected way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i've updated the early exit by duplicating the diagnostic. I didn't see an obvious way to avoid doing that. Happy to amend if this still isn't quite right.
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