Repository navigation
[Unsafe] Opt tooling into updated C# memory-safety rules - #13078
simonrozsival wants to merge 4 commits into
Conversation
There was a problem hiding this comment.
🟢 Approval recommended
The safety migration is internally consistent and validated, with only a non-blocking suggestion to cover metadata from both task assemblies.
1 open finding
What changed in this PR
Enables C# 15’s updated memory-safety rules across Android build tooling while preserving explicit caller obligations for borrowed unmanaged resources.
Changes:
- Enables preview memory-safety rules in tooling and modern tests.
- Localizes implementation-owned unsafe operations and classifies
renameas safe. - Documents, audits, and tests the borrowed embedded-resource stream contract.
| File | Description |
|---|---|
tools/tmt/tmt.csproj |
Enables updated memory-safety rules. |
src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.csproj |
Enables rules for legacy build tasks. |
src/Xamarin.Android.Build.Tasks/Utilities/TypeMapHelper.cs |
Scopes unsafe encoding locally. |
src/Xamarin.Android.Build.Tasks/Utilities/PathUtil.cs |
Scopes unsafe pointer comparison locally. |
src/Xamarin.Android.Build.Tasks/Utilities/MetadataExtensions.cs |
Documents and retains the borrowed-stream contract. |
src/Xamarin.Android.Build.Tasks/Utilities/FileUtil.cs |
Marks marshalled rename import safe. |
src/Xamarin.Android.Build.Tasks/Tasks/ResolveLibraryProjectImports.cs |
Audits borrowed-stream callsites. |
src/Microsoft.Android.Build.Tasks/Tests/Microsoft.Android.Build.Tasks.Tests.csproj |
Enables rules for modern tests. |
src/Microsoft.Android.Build.Tasks/Tests/Microsoft.Android.Build.Tasks.Tests/MetadataExtensionsTests.cs |
Tests stream behavior and safety metadata. |
src/Microsoft.Android.Build.Tasks/Microsoft.Android.Build.Tasks.csproj |
Enables rules for modern build tasks. |
🧠 Review effort: Balanced
|
@dalexsoto review |
| <RootNamespace>Microsoft.Android.Tasks</RootNamespace> | ||
| <Nullable>enable</Nullable> | ||
| <AllowUnsafeBlocks>true</AllowUnsafeBlocks> | ||
| <LangVersion>preview</LangVersion> |
There was a problem hiding this comment.
Should we file an issue somewhere to remove preview when this feature is stable?
dalexsoto
left a comment
There was a problem hiding this comment.
The complete eight-file static correctness and integration review found no high-confidence blocker. All three borrowed-resource paths consume their streams before the owning PEReader is disposed, including exception paths and downstream ZIP/copy/hash consumers. The independently compiled legacy and modern task assemblies and tmt retain their local feature opt-ins and caller-safety boundaries; encoding, path comparison and platform rename execution remain unchanged.
The intentionally removed new tests and later removal of preview are accepted scope/follow-up decisions. This is a source-review approval, not a claim of green CI or reviewer-executed tests: the observed Gradle/JcwGen prerequisite failures have no established underlying cause, and CI used a later main merge.
Opt in the two build-task assemblies, their focused host tests, and the typemap tool. Localize encoding and path traversal unsafe operations while retaining the borrowed PE resource stream caller contract. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Remove the new path comparison and encoding tests for unchanged behavior. Retain the resource-stream memory-safety contract tests. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Remove MetadataExtensionsTests and the companion test-project compiler settings added solely for those tests, as requested in review. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
99812ec to
df941ce
Compare
Merge parent 64bd762 normally, preserving tooling commits and current main cleanup. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
dalexsoto
left a comment
There was a problem hiding this comment.
The complete eight-file tooling delta against its actual parent preserves all three project-local feature opt-ins and caller-safety boundaries. All three borrowed-resource paths consume their streams within the owning PEReader scope, including downstream copy/hash/ZIP operations and exceptional exits. The intentionally removed new tests and preview-removal follow-up remain accepted scope decisions. This approves the tooling delta, not the separate unresolved findings on its parent PR.

Depends on #13070. This PR targets its branch so the diff contains only the follow-up tooling migration.
The updated C# memory-safety model distinguishes implementation-owned unsafe operations from obligations that callers must satisfy. Enable it locally in
Xamarin.Android.Build.Tasks,Microsoft.Android.Build.Tasks, andtools/tmtusingLangVersion=previewand$(Features);updated-memory-safety-rules.Move pinned encoding and path comparison into local unsafe blocks, explicitly classify the marshalled
libcrename import as safe, and retain the caller-unsafe contract onXamarin.Android.Tasks.MetadataExtensions.GetEmbeddedResourceStream(): its returned stream borrows the reader's unmanaged image memory. Document that lifetime obligation with XML<safety>and audit all three resource-import callsites.No SDK/package bump introduced by this PR, global compiler opt-in, customer-shipped target changes, JNI behavior changes, or buffer copying. Java.Interop's migration remains unchanged by this tooling delta. Runtime/device and generated binding migrations are deferred to separate batches. No issue is closed by this PR. All newly added tests and their companion compiler settings were removed following review; existing tests provide the validation below.
Local validation
Normally merged the validated parent head
64bd762488aad1e819a38fbd8840a9c8d97a3d3don October 13, 2026, producing merge commit7fde6986eb5655dcfd87dd6214a64d9b5a629154on top of the previous tooling headdf941cecb63afc7a4cc1e7586ff5131bced40e92. No rebase, forced push, manual conflict resolution, PR retargeting, or native stack mutation was performed. The complete native stack remains #13070 → #13078 → #13104 → #13105.The tooling patch remains unchanged after excluding Git blob-ID lines: 8 files, 60 insertions, 18 deletions relative to the new parent. All 141 files deleted by the incoming parent remain deleted; no removed Cecil/native/report utilities were restored.
The following validation was run after this normal merge, using the parent's repository-local SDK
12.0.100-alpha.1.26509.101and Debug configuration. Results from the older SDK/parent are not treated as validation of this merge.make preparemake leeroy./dotnet-local.sh build src/Microsoft.Android.Build.Tasks/Microsoft.Android.Build.Tasks.csproj -c Debug -v minimal./dotnet-local.sh build src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.csproj -c Debug -v minimal./dotnet-local.sh build tools/tmt/tmt.csproj -c Debug -v minimal./dotnet-local.sh test src/Microsoft.Android.Build.Tasks/Tests/Microsoft.Android.Build.Tasks.Tests/Microsoft.Android.Build.Tasks.Tests.csproj -c Debug -v minimal -p:_NativeAotLlvmReadObjPath=/Users/simon/android-toolchain/ndk/toolchains/llvm/prebuilt/darwin-x86_64/bin/llvm-readobj --logger 'trx;LogFileName=normal-merge-modern.trx' --results-directory /Users/simon/.copilot/session-state/0cd101bb-a6c0-4d6b-a9f3-a629f26b9106/files/TestResults./dotnet-local.sh build src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Xamarin.Android.Build.Tests.csproj -c Debug -v minimal./dotnet-local.sh test bin/TestDebug/net10.0/Xamarin.Android.Build.Tests.dll --filter 'FullyQualifiedName~GenerateMibcProfileTests|Name~ResolveLibraryProjectImports' --logger 'trx;LogFileName=normal-merge-host.trx' --results-directory /Users/simon/.copilot/session-state/0cd101bb-a6c0-4d6b-a9f3-a629f26b9106/files/TestResults./dotnet-local.sh bin/Debug/bin/typemap-tool/tmt.dll --helpThe skipped test,
RealLinkedTypeMapUsesImplementationIdentities, requiresILLinkFrameworkDirectoryin addition toILLinkPath. No device operations or CI retries were performed; this delta changes host tooling only, and the import integration cases do not install applications.Before the ancestor refreshes, a temporary compile-only consumer probe confirmed that safe callers of the borrowed stream helper fail with CS9362, while an explicitly unsafe caller compiles. Both task assemblies emitted
MemorySafetyRulesAttributeand the helper emittedSystem.Diagnostics.CodeAnalysis.RequiresUnsafeAttribute; inherited feature switches were preserved. That earlier probe was not rerun during this merge; current-parent validation is the fresh build/test evidence above.Pull Request
title and
description
should follow the
commit-messages.mdworkflow documentation, and in particular should include: