Skip to content

[ClangImporter] Break cycle in IAM extensions #71027

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

Merged
merged 1 commit into from
Jan 22, 2024

Conversation

beccadax
Copy link
Contributor

Under a set of circumstances that I cannot fully characterize, it was possible for ClangImporter::Implementation::importDeclContextOf() to end up recursing until stack overflow while trying to import a global as a member.

The cycle was caused by addExtension() using a newly-created extension’s lazy member loader to load its members before the extension had been added to ClangImporter’s extensionPoints table. Loading the members would try to import the IAM members again, which would cause importDeclContextOf() to try to create another extension to contain them, which addExtension() would force to load those members. Repeat until stack overflow.

I have fixed this bug by registering the extension in the extensionPoints map before its member loader is set. This ensures that if importDeclContextOf() gets called for any reason after that point, it will return the existing extension instead of creating a new one, avoiding the cycle.

Unfortunately, I was not able to figure out the exact conditions that were necessary to reproduce this bug, so I have not included a test. However, I have tested with the affected project locally and confirmed that this change lets it build. All existing tests pass.

(In fact, there appears to be a hole in the test suite around the entire code path for lazily loading the members of these extensions—I can deliberately break it and all tests will still pass. But I haven't yet been able to write a new test that exercises it, and the affected project has been waiting very patiently for their code to start building again.)

Fixes rdar://120854030.

Under a set of circumstances that I cannot fully characterize, it was possible for `ClangImporter::Implementation::importDeclContextOf()` to end up recursing until stack overflow while trying to import a global as a member.

The cycle was caused by `addExtension()` using a newly-created extension’s lazy member loader to load its members before the extension had been added to ClangImporter’s `extensionPoints` table. Loading the members would try to import the IAM members again, which would cause `importDeclContextOf()` to try to create another extension to contain them, which `addExtension()` would force to load those members. Repeat until stack overflow.

I have fixed this bug by registering the extension in the `extensionPoints` map before its member loader is set. This ensures that if `importDeclContextOf()` gets called for any reason after that point, it will return the existing extension instead of creating a new one, avoiding the cycle.

Unfortunately, I was not able to figure out the exact conditions that were necessary to reproduce this bug, so I have not included a test. However, I have tested with the affected project locally and confirmed that this change lets it build. All existing tests pass.

Fixes rdar://120854030.
@beccadax
Copy link
Contributor Author

@swift-ci please test

@beccadax beccadax marked this pull request as ready for review January 19, 2024 22:24
@beccadax beccadax merged commit e63a887 into swiftlang:main Jan 22, 2024
Copy link
Member

@DougGregor DougGregor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good. I can see why it's so hard to trigger this particular code path in a test

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants