Skip to content

Conversation

@drodriguez
Copy link
Contributor

Upstream LLVM in llvm/llvm-project#139584 changed DiagnosticOptions from being a referenced counted object to just be a reference, not owned by the clang::DiagnosticEngine.

In 0981b71 (part of #82243), the usages of the Swift repository were adapted to the new memory model, but it introduced at least one use-after-free and a potential one around the usage of Clang in the Clang Importer.

This commit tries to fix the use-after-free in both cases, by returning a unique_ptr to the clang::DiagnosticOptions, which makes the lifetime of the DiagnosticOptions match the lifetime of the variable that uses it (normally a CompilerInvocation).

Other cases in 0981b71 should be safe because the lifetime of the DiagnosticOptions do not seem to propagate beyond the scope of the functions where they live (but I am not fully sure about the one in IDETool/CompilerInvocation.cpp completely).

This was causing compiler crashes during the test
Interop/Cxx/stdlib/unsupported-stdlib.swift which eventually uses createClangDriver and tries to emit a diagnostic, which in some cases was reading the memory from DiagnosticOptions when it was already out of scope.

…r rebranch

Upstream LLVM in llvm/llvm-project#139584 changed `DiagnosticOptions`
from being a referenced counted object to just be a reference, not owned
by the `clang::DiagnosticEngine`.

In 0981b71 (part of swiftlang#82243), the usages
of the Swift repository were adapted to the new memory model, but it
introduced at least one use-after-free and a potential one around the
usage of Clang in the Clang Importer.

This commit tries to fix the use-after-free in both cases, by returning
a `unique_ptr` to the `clang::DiagnosticOptions`, which makes the
lifetime of the `DiagnosticOptions` match the lifetime of the variable
that uses it (normally a `CompilerInvocation`).

Other cases in 0981b71 should be safe
because the lifetime of the `DiagnosticOptions` do not seem to propagate beyond
the scope of the functions where they live (but I am not fully sure
about the one in `IDETool/CompilerInvocation.cpp` completely).

This was causing compiler crashes during the test
`Interop/Cxx/stdlib/unsupported-stdlib.swift` which eventually uses
`createClangDriver` and tries to emit a diagnostic, which in some cases
was reading the memory from `DiagnosticOptions` when it was already out
of scope.
@drodriguez
Copy link
Contributor Author

@swift-ci please smoke test

Copy link
Contributor

@egorzhdan egorzhdan left a comment

Choose a reason for hiding this comment

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

Thank you for discovering and fixing this!

@drodriguez drodriguez merged commit 9eca612 into swiftlang:main Nov 12, 2025
3 checks passed
Copy link
Contributor

@jansvoboda11 jansvoboda11 left a comment

Choose a reason for hiding this comment

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

Thanks for fixing this. Left a comment on ClangImporter::createClangInvocation(), but the rest looks good.

std::unique_ptr<clang::CompilerInvocation> ClangImporter::createClangInvocation(
std::pair<std::unique_ptr<clang::CompilerInvocation>,
std::unique_ptr<clang::DiagnosticOptions>>
ClangImporter::createClangInvocation(
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think this is necessary. clangDiags is a local, so diagOpts being local should be fine. The returned CI doesn't hold on to either of these.

Comment on lines +554 to +555
/// Clang diagnostic options used to create the Clang invocation.
std::unique_ptr<clang::DiagnosticOptions> DiagnosticOptions;
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
/// Clang diagnostic options used to create the Clang invocation.
std::unique_ptr<clang::DiagnosticOptions> DiagnosticOptions;
/// Clang diagnostic options used to create the Clang driver.
std::unique_ptr<clang::DiagnosticOptions> DiagnosticOptions;

The invocation doesn't need external DiagnosticOptions.

@drodriguez
Copy link
Contributor Author

I will remove the pieces that touched createClangInvocation in a follow up diff. Thanks for reviewing. It probably means that the usage in IDETool/CompilerInvocation.cpp is fine, since the diags are used in a CompilerInvocation there too.

drodriguez added a commit to drodriguez/swift that referenced this pull request Nov 12, 2025
…swiftlang#85445)

Feedback in swiftlang#85445 after it merged pointed out that the changes around
`createClangInvocation` are not necessary because `CompilerInvocation`
do not hold a reference to `clang::DiagnosticOptions`, only the
`clang::driver::Driver` does.

These changes undo the modifications done there and return the code to
the previous state (but keeps the changes around `createClangDriver`
which was causing the use-after-free).
@jansvoboda11
Copy link
Contributor

It probably means that the usage in IDETool/CompilerInvocation.cpp is fine, since the diags are used in a CompilerInvocation there too.

That's right.

drodriguez added a commit that referenced this pull request Nov 13, 2025
…#85445) (#85457)

Feedback in #85445 after it merged pointed out that the changes around
`createClangInvocation` are not necessary because `CompilerInvocation`
do not hold a reference to `clang::DiagnosticOptions`, only the
`clang::driver::Driver` does.

These changes undo the modifications done there and return the code to
the previous state (but keeps the changes around `createClangDriver`
which was causing the use-after-free).
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