Thread-safe task analyzer (without IL decompiler)#13444
Merged
JanProvaznik merged 7 commits intodotnet:mainfrom Mar 30, 2026
Merged
Thread-safe task analyzer (without IL decompiler)#13444JanProvaznik merged 7 commits intodotnet:mainfrom
JanProvaznik merged 7 commits intodotnet:mainfrom
Conversation
Adds a Roslyn analyzer for MSBuild task thread safety with: Core analyzer (MultiThreadableTaskAnalyzer): - MSBuildTask0001: Critical APIs banned in all tasks (Console.*, Environment.Exit) - MSBuildTask0002: APIs requiring TaskEnvironment alternatives - MSBuildTask0003: File APIs requiring absolute paths - MSBuildTask0004: Potentially problematic APIs (Assembly.Load, etc.) Transitive analysis (TransitiveCallChainAnalyzer): - MSBuildTask0005: Source-level BFS call graph traces unsafe APIs through helpers Dual severity: Warning for IMultiThreadableTask, Info for plain ITask. Code fix provider for MSBuildTask0002/0003 with TaskEnvironment replacements. Build integration: - Added to MSBuild.slnx - Tasks.csproj: /p:BuildAnalyzer=true enables analyzer - 109 tests passing Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
39edd10 to
70b47ee
Compare
Removes Microsoft.NET.Test.Sdk, xunit.runner.visualstudio, and other packages that are implicitly referenced by the .NET SDK. These cause NETSDK1023 errors in the repo's SDK (10.0.104) which treats this as an error, not a warning. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
6842561 to
460144b
Compare
Adds a Roslyn analyzer (MSBuildTask0001-0005) that detects unsafe API usage in MSBuild task implementations for multithreaded execution. Analyzer project uses central package management. Test project opts out (ManagePackageVersionsCentrally=false) due to NuGet version conflicts in CodeAnalysis.Testing transitive dependencies. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
460144b to
3feb591
Compare
Member
Author
Analyzer Findings on MSBuild Tasks (
|
| File | Line | API | Fix |
|---|---|---|---|
| Copy.cs | 118 | Environment.GetEnvironmentVariable("MSBuildCopyParallelCount") |
TaskEnvironment.GetEnvironmentVariable() |
| Copy.cs | 123 | Environment.GetEnvironmentVariable("MSBUILDALWAYSRETRY") |
TaskEnvironment.GetEnvironmentVariable() |
| Copy.cs | 241 | Environment.GetEnvironmentVariable("MSBUILDALWAYSOVERWRITEREADONLYFILES") |
TaskEnvironment.GetEnvironmentVariable() |
| AssignTargetPath.cs | 85 | Path.GetFullPath(item) |
TaskEnvironment.GetAbsolutePath() |
| AssignTargetPath.cs | 146 | Path.GetFullPath(rootDir) |
TaskEnvironment.GetAbsolutePath() |
| FindUnderPath.cs | 76 | Path.GetFullPath(Path.Value) |
TaskEnvironment.GetAbsolutePath() |
| FindUnderPath.cs | 107 | Path.GetFullPath(item.ItemSpec) |
TaskEnvironment.GetAbsolutePath() |
| Unzip.cs | 179 | Path.GetFullPath(...) destination |
TaskEnvironment.GetAbsolutePath() |
| Unzip.cs | 189 | Path.GetFullPath(...) zip-slip check |
TaskEnvironment.GetAbsolutePath() |
MSBuildTask0003 — File API Requires Absolute Path (1)
| File | Line | API |
|---|---|---|
| GetFileHash.cs | 148 | File.OpenRead(filePath) — wrap with TaskEnvironment.GetAbsolutePath() |
MSBuildTask0005 — Transitive Unsafe API in Call Chain (4)
| File | Line | Chain | Unsafe API |
|---|---|---|---|
| Copy.cs | 209 | IsMatchingSizeAndTimeStamp → FileState.get_Length → FileDirInfo.ThrowFileInfoException |
new FileInfo(string) |
| Copy.cs | 261 | CopyFileWithLogging → FileState.Reset → FileDirInfo..ctor |
new DirectoryInfo(string) |
| Move.cs | 92 | Execute → MoveFileWithLogging → NativeMethods.MoveFileEx |
File.Move, File.Delete, File.GetAttributes |
Recommended Migration Order
- Copy.cs (High) — most-used task, already
IMultiThreadableTask - AssignTargetPath.cs (Medium) — 2
Path.GetFullPathcalls - FindUnderPath.cs (Medium) — 2
Path.GetFullPathcalls - Unzip.cs (Medium) — 2
Path.GetFullPathcalls - Move.cs (Low) — transitive, paths typically absolute
- GetFileHash.cs (Low) — 1
File.OpenRead, path typically absolute
- All diagnostics are Warning (removed dual Info/Warning severity) - Added WarningsNotAsErrors in Tasks.csproj so analyzer warnings don't break WarnAsError builds while issues are being fixed - Enabled /p:BuildAnalyzer=true in Linux Core Multithreaded Mode CI job - Removed issues.md (posted as PR comment instead) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR adds a new Roslyn analyzer + code fix package to help MSBuild task authors identify (and in some cases automatically migrate away from) APIs that are unsafe under multithreaded task execution (IMultiThreadableTask), including a transitive call-chain analyzer to surface unsafe APIs reached through helper methods.
Changes:
- Added
ThreadSafeTaskAnalyzeranalyzer project (direct API rules + shared helpers + transitive call-chain rule) and integrated it intoMSBuild.slnx. - Added a code fix provider for
MSBuildTask0002(TaskEnvironment replacements) andMSBuildTask0003(absolute path wrapping). - Added a new analyzer test project and enabled running the analyzer on
Microsoft.Build.Taskswhen/p:BuildAnalyzer=true(including CI).
Reviewed changes
Copilot reviewed 21 out of 21 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
| src/ThreadSafeTaskAnalyzer/TransitiveCallChainAnalyzer.cs | Implements compilation-wide call graph scan + BFS to report transitive unsafe API usage (MSBuildTask0005). |
| src/ThreadSafeTaskAnalyzer/SharedAnalyzerHelpers.cs | Shared banned API lookup + path-argument safety detection helpers. |
| src/ThreadSafeTaskAnalyzer/MultiThreadableTaskAnalyzer.cs | Direct per-type analyzer for MSBuildTask0001–0004, scoped via RegisterSymbolStartAction. |
| src/ThreadSafeTaskAnalyzer/MultiThreadableTaskCodeFixProvider.cs | Code fixes for MSBuildTask0002/0003. |
| src/ThreadSafeTaskAnalyzer/BannedApiDefinitions.cs | Defines banned API sets resolved via documentation IDs. |
| src/ThreadSafeTaskAnalyzer/DiagnosticDescriptors.cs | Defines diagnostic descriptors for MSBuildTask0001–0005. |
| src/ThreadSafeTaskAnalyzer/DiagnosticIds.cs | Adds public IDs for the analyzer rules. |
| src/ThreadSafeTaskAnalyzer/README.md | Documents rule set, scope, fixes, usage, and architecture. |
| src/ThreadSafeTaskAnalyzer/AnalyzerReleases.Unshipped.md | Tracks new analyzer rules for Roslyn release tracking. |
| src/ThreadSafeTaskAnalyzer/AnalyzerReleases.Shipped.md | Initializes shipped release tracking file. |
| src/ThreadSafeTaskAnalyzer/ThreadSafeTaskAnalyzer.csproj | New analyzer project definition + Roslyn package references. |
| src/ThreadSafeTaskAnalyzer.Tests/TestHelpers.cs | Test harness that compiles sources with framework stubs and runs analyzers. |
| src/ThreadSafeTaskAnalyzer.Tests/MultiThreadableTaskAnalyzerTests.cs | Large unit test suite validating direct analyzer behavior and safe patterns. |
| src/ThreadSafeTaskAnalyzer.Tests/TransitiveCallChainAnalyzerTests.cs | Unit tests validating MSBuildTask0005 transitive detection and chain formatting. |
| src/ThreadSafeTaskAnalyzer.Tests/MultiThreadableTaskCodeFixProviderTests.cs | Code-fix transformation tests via CSharpCodeFixTest. |
| src/ThreadSafeTaskAnalyzer.Tests/WriteAllTextDetailedTest.cs | Regression-style test validating path diagnostic count for WriteAllText. |
| src/ThreadSafeTaskAnalyzer.Tests/ThreadSafeTaskAnalyzer.Tests.csproj | New test project configuration and package references. |
| src/Tasks/Microsoft.Build.Tasks.csproj | Adds conditional analyzer reference (BuildAnalyzer) + relaxes analyzer IDs from WarnAsError. |
| eng/dependabot/Directory.Packages.props | Adds Roslyn authoring package versions for central management. |
| MSBuild.slnx | Includes the new analyzer and analyzer test projects in the main solution. |
| .vsts-dotnet-ci.yml | Enables /p:BuildAnalyzer=true in the bootstrapped -mt CI leg. |
CI fixes: - SA1133: Split combined attributes in CodeFixProvider - SA1111: Move closing parens to last parameter lines - SA1508: Remove blank line before closing brace Review fixes: - Remove pinned LangVersion 12.0 (repo default is latest) - Update RS2001 suppression comment - Remove MSBuildTask0001 from WarningsNotAsErrors (it's Error, not Warning) - Make FindTaskTypes recursive for deeply nested types - Fix README: remove dual-severity claims, clarify performance notes Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
AR-May
approved these changes
Mar 27, 2026
Member
AR-May
left a comment
There was a problem hiding this comment.
Looks good to go. Most my comments are just suggestions.
src/ThreadSafeTaskAnalyzer.Tests/TransitiveCallChainAnalyzerTests.cs
Outdated
Show resolved
Hide resolved
- Clarify [MSBuildMultiThreadableTaskAnalyzed] attribute purpose in README - Simplify AnalyzerReleases.Shipped.md format - Improve MSBuildTask0003 message to mention shared working directory - Extract shared constants to WellKnownTypeNames.cs - Add doc comment explaining MaxCallChainDepth purpose - Deduplicate scope option reading into SharedAnalyzerHelpers - Convert 3 similar transitive tests to Theory with InlineData - Add scope option tests (multithreadable_only vs all) - Add TestAnalyzerConfigOptionsProvider for scope testing Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ng period Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
dfederm
pushed a commit
to dfederm/msbuild
that referenced
this pull request
Apr 9, 2026
## Thread-Safe Task Analyzer
Adds a Roslyn analyzer that detects unsafe API usage in MSBuild task
implementations, guiding task authors toward thread-safe patterns
required for multithreaded task execution (`IMultiThreadableTask`).
### Diagnostic Rules
| ID | Default Severity | Title |
|----|-----------------|-------|
| **MSBuildTask0001** | Error | API is never safe in MSBuild tasks
(Console.*, Environment.Exit, ThreadPool, etc.) |
| **MSBuildTask0002** | Warning / Info ¹ | API requires TaskEnvironment
alternative |
| **MSBuildTask0003** | Warning / Info ¹ | File system API requires
absolute path |
| **MSBuildTask0004** | Warning / Info ¹ | API may cause issues
(Assembly.Load, Activator, etc.) |
| **MSBuildTask0005** | Warning / Info ¹ | Transitive unsafe API in call
chain (source-level BFS) |
¹ Warning for `IMultiThreadableTask` / `[MSBuildMultiThreadableTask]`,
Info for plain `ITask`. Users can configure via `.editorconfig`.
### What's included
- **Core analyzer** (`MultiThreadableTaskAnalyzer`): Per-type scoped
analysis via `RegisterSymbolStartAction`
- **Transitive analyzer** (`TransitiveCallChainAnalyzer`): Source-level
BFS call graph traces unsafe APIs through helper methods
- **Code fix provider**: Auto-fixes for MSBuildTask0002 (TaskEnvironment
replacements) and MSBuildTask0003 (`GetAbsolutePath` wrapping)
- **Shared helpers** (`SharedAnalyzerHelpers`): Deduplicated path safety
analysis and banned API resolution
- **109 unit tests** passing
### Build integration
- Added to `MSBuild.slnx`
- `/p:BuildAnalyzer=true` enables analyzer on Tasks.csproj
- Produces 56 warnings on Tasks project (34 env + 12 path + 10
transitive), 0 false positives
### Safe pattern recognition (zero false positives verified)
- `ITaskItem.GetMetadata("FullPath")`
- `Path.Combine(absolutePath, ...)`
- `Path.GetDirectoryName(absolutePath)`
- `AbsolutePath` type (implicit conversion)
- `File.WriteAllText(path, contents)` — only path param flagged
### What's NOT included (preserved in `threadsafe-analyzer` branch)
- IL decompiler (`ILCallGraphExtender.cs`) — 1,011 lines of raw IL byte
parsing with 6 verified opcode bugs
- Demo project (~1,600 lines)
- Cross-assembly IL tests (~870 lines)
### Testing
- Stress-tested on MSBuild Tasks project, external task libraries, and
SDK-style task patterns
- Zero false positives across all test scenarios
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Thread-Safe Task Analyzer
Adds a Roslyn analyzer that detects unsafe API usage in MSBuild task implementations, guiding task authors toward thread-safe patterns required for multithreaded task execution (
IMultiThreadableTask).Diagnostic Rules
¹ Warning for
IMultiThreadableTask/[MSBuildMultiThreadableTask], Info for plainITask. Users can configure via.editorconfig.What's included
MultiThreadableTaskAnalyzer): Per-type scoped analysis viaRegisterSymbolStartActionTransitiveCallChainAnalyzer): Source-level BFS call graph traces unsafe APIs through helper methodsGetAbsolutePathwrapping)SharedAnalyzerHelpers): Deduplicated path safety analysis and banned API resolutionBuild integration
MSBuild.slnx/p:BuildAnalyzer=trueenables analyzer on Tasks.csprojSafe pattern recognition (zero false positives verified)
ITaskItem.GetMetadata("FullPath")Path.Combine(absolutePath, ...)Path.GetDirectoryName(absolutePath)AbsolutePathtype (implicit conversion)File.WriteAllText(path, contents)— only path param flaggedWhat's NOT included (preserved in
threadsafe-analyzerbranch)ILCallGraphExtender.cs) — 1,011 lines of raw IL byte parsing with 6 verified opcode bugsTesting