Skip to content

[Unsafe] Opt tooling into updated C# memory-safety rules - #13078

Open
simonrozsival wants to merge 4 commits into
simonrozsival-csharp-memory-safetyfrom
simonrozsival-other-project-memory-safety
Open

simonrozsival wants to merge 4 commits into
simonrozsival-csharp-memory-safetyfrom
simonrozsival-other-project-memory-safety

Conversation

@simonrozsival

@simonrozsival simonrozsival commented Oct 12, 2026 •

Copy link
Copy Markdown
Member

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, and tools/tmt using LangVersion=preview and $(Features);updated-memory-safety-rules.

Move pinned encoding and path comparison into local unsafe blocks, explicitly classify the marshalled libc rename import as safe, and retain the caller-unsafe contract on Xamarin.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 64bd762488aad1e819a38fbd8840a9c8d97a3d3d on October 13, 2026, producing merge commit 7fde6986eb5655dcfd87dd6214a64d9b5a629154 on top of the previous tooling head df941cecb63afc7a4cc1e7586ff5131bced40e92. 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.101 and Debug configuration. Results from the older SDK/parent are not treated as validation of this merge.

Command Result
make prepare Passed with a fresh isolated local SDK root; the first in-place install attempt was blocked by immediate macOS SIGKILL of the SDK host (exit 137), resolved by preserving the old directory and provisioning a fresh one. The obsolete local SDK directory was subsequently removed.
make leeroy Passed; rebuilt the solution, API 37/37.1/37.2, and local workload setup with the inherited cleanup and Java.Interop contracts
./dotnet-local.sh build src/Microsoft.Android.Build.Tasks/Microsoft.Android.Build.Tasks.csproj -c Debug -v minimal Passed
./dotnet-local.sh build src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.csproj -c Debug -v minimal Passed
./dotnet-local.sh build tools/tmt/tmt.csproj -c Debug -v minimal Passed
./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 703 passed, 1 skipped, 0 failed
./dotnet-local.sh build src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Xamarin.Android.Build.Tests.csproj -c Debug -v minimal Passed
./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 5 passed, 0 failed: 3 existing MIBC cases and both CoreCLR/NativeAOT build-only resource-import cases
./dotnet-local.sh bin/Debug/bin/typemap-tool/tmt.dll --help Passed

The skipped test, RealLinkedTypeMapUsesImplementationIdentities, requires ILLinkFrameworkDirectory in addition to ILLinkPath. 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 MemorySafetyRulesAttribute and the helper emitted System.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.md workflow documentation, and in particular should include:

  • Useful description of why the change is necessary.
  • Links to issues fixed
  • Unit tests

Copilot AI balanced review requested due to automatic review settings October 12, 2026 09:18
@simonrozsival
simonrozsival added this pull request to stack #13079 October 12, 2026 09:18

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟢 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 rename as 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

@simonrozsival

Copy link
Copy Markdown
Member Author

@dalexsoto review

<RootNamespace>Microsoft.Android.Tasks</RootNamespace>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<LangVersion>preview</LangVersion>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should we file an issue somewhere to remove preview when this feature is stable?

@simonrozsival simonrozsival changed the title [Build] Opt tooling into updated C# memory-safety rules [Unsafe] Opt tooling into updated C# memory-safety rules Oct 12, 2026

@dalexsoto dalexsoto left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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.

simonrozsival and others added 3 commits October 13, 2026 11:19
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>
Merge parent 64bd762 normally, preserving tooling commits and current main cleanup.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

@dalexsoto dalexsoto left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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.

This branch has not been deployed

No deployments
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.

4 participants