-
-
Notifications
You must be signed in to change notification settings - Fork 721
fix(linter): prevent unsound use of Allocator across threads
#13032
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
fix(linter): prevent unsound use of Allocator across threads
#13032
Conversation
CodSpeed Instrumentation Performance ReportMerging #13032 will not alter performanceComparing Summary
Footnotes |
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.
Pull Request Overview
This PR addresses a thread-safety issue with the Allocator type by preventing unsound use across multiple threads. The main change introduces a MessageCloner struct that provides synchronized access to an Allocator for cloning Message objects safely in multi-threaded contexts.
- Introduces
MessageClonerwrapper with mutex-based synchronization for thread-safeAllocatoraccess - Updates function signatures to take
&mut Allocatorinstead of&Allocatorto enforce exclusive ownership - Replaces direct
clone_incalls withMessageCloner::clone_messagemethod calls
Reviewed Changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| crates/oxc_linter/src/tester.rs | Updates allocator to mutable reference and passes it to run_test_source |
| crates/oxc_linter/src/service/runtime.rs | Implements MessageCloner struct and updates methods to use synchronized message cloning |
| crates/oxc_linter/src/service/mod.rs | Updates method signatures to accept mutable allocator references |
| crates/oxc_language_server/src/linter/isolated_lint_handler.rs | Updates allocator usage to mutable references throughout the handler |
|
Removed myself as a reviewer because |
5589e02 to
1585688
Compare
|
@Sysix Thanks for the context. The linter is all new to me, so I can't say I understand any of this! It looks to me like the root cause might be that I've written up observations in #14563 so we can investigate further later on when we have time. For now, going to merge this. |
Merge activity
|
`Allocator` is currently `Sync`, but it shouldn't be - `Allocator` is not thread-safe and allocating into it in multiple threads can cause data corruption or, in worst case, writing out of bounds. I intend to remove the `impl Sync for Allocator` in a PR to follow. The code altered in this PR was relying on `Allocator` being `Sync`. It was *not* actually dangerous as it stood, because access to the `Allocator` was synchronized by always holding a lock on the `messages` `Mutex` while using it. However, I suspect this was more by accident by design, and it was rather fragile. This PR introduces a `MessageCloner` struct which provides a safe API which ensures access to the `Allocator` is correctly synchronized. It's unfortunate to introduce a 2nd `Mutex`, and the `Mutex` inside `MessageCloner` is accessed more than necessary - on each turn of the loop in the `map` loops, instead of just once at the top of the loop. However, I couldn't find a way to achieve that without resorting to fragile unsafe code. In any case, from what I can understand, the ultimate callers of both `run_source` and `run_test_source` convert the cloned `Message`s into other owning types (`DiagnosticReport` or `Report`) anyway. Therefore the double-conversion may be wasteful anyway, and we might be better off trying to convert to these owned types earlier, and avoid using an `Allocator` at all in these methods. I also am wondering in what circumstances error messages would contain arena-allocated strings, and whether `Message` could instead contain `Cow<'static, str>`s, in which case the `Allocator` becomes unnecessary, and cloning would be cheaper. Delving into the depths of the linter to understand all this is a bit beyond me, so I hope we can accept this imperfect solution for now, and refactor to remove/optimize it later on.
1585688 to
b0558a4
Compare

Allocatoris currentlySync, but it shouldn't be -Allocatoris not thread-safe and allocating into it in multiple threads can cause data corruption or, in worst case, writing out of bounds. I intend to remove theimpl Sync for Allocatorin a PR to follow.The code altered in this PR was relying on
AllocatorbeingSync. It was not actually dangerous as it stood, because access to theAllocatorwas synchronized by always holding a lock on themessagesMutexwhile using it. However, I suspect this was more by accident by design, and it was rather fragile.This PR introduces a
MessageClonerstruct which provides a safe API which ensures access to theAllocatoris correctly synchronized.It's unfortunate to introduce a 2nd
Mutex, and theMutexinsideMessageCloneris accessed more than necessary - on each turn of the loop in themaploops, instead of just once at the top of the loop. However, I couldn't find a way to achieve that without resorting to fragile unsafe code.In any case, from what I can understand, the ultimate callers of both
run_sourceandrun_test_sourceconvert the clonedMessages into other owning types (DiagnosticReportorReport) anyway. Therefore the double-conversion may be wasteful anyway, and we might be better off trying to convert to these owned types earlier, and avoid using anAllocatorat all in these methods.I also am wondering in what circumstances error messages would contain arena-allocated strings, and whether
Messagecould instead containCow<'static, str>s, in which case theAllocatorbecomes unnecessary, and cloning would be cheaper.Delving into the depths of the linter to understand all this is a bit beyond me, so I hope we can accept this imperfect solution for now, and refactor to remove/optimize it later on.