Skip to content

Disable file locking by default on iOS and tvOS - #134060

Merged
vitek-karas merged 8 commits into
mainfrom
no-file-locking-on-mobile
Sep 21, 2026
Merged

vitek-karas merged 8 commits into
mainfrom
no-file-locking-on-mobile

Conversation

@rolfbjarne

Copy link
Copy Markdown
Member

Summary

Disables advisory file locking by default on iOS and tvOS (device and simulator), while leaving Mac Catalyst — and every other platform — unchanged. The behavior remains fully configurable: setting System.IO.DisableFileLocking=false (or DOTNET_SYSTEM_IO_DISABLEFILELOCKING=0) restores locking on those platforms.

Motivation

On iOS and tvOS, RunningBoard terminates a suspended app with exception code 0xdead10cc when the app still holds a file lock on a file outside its data container. .NET acquires such a lock implicitly: every FileStream opened on Unix takes an advisory flock to emulate Windows FileShare semantics, and a FileShare.Read open maps to a shared LOCK_SH.

A common way to hit this is stack trace symbolication: when the runtime opens a PDB from inside the read-only app bundle to produce line numbers for an exception, the resulting shared lock lives for as long as the handle does. If the user backgrounds the app while that handle is open, the system kills the process. The lock provides no real value here — the app bundle is read-only and the lock is only advisory between .NET processes — but the termination is fatal and hard to diagnose.

Implementation

SafeFileHandle.DisableFileLocking on Unix already consults the System.IO.DisableFileLocking / DOTNET_SYSTEM_IO_DISABLEFILELOCKING config switch; this change only alters the defaultValue passed to that lookup, so it becomes true on iOS and tvOS:

defaultValue: (OperatingSystem.IsIOS() && !OperatingSystem.IsMacCatalyst()) || OperatingSystem.IsTvOS()

Notes on the predicate:

  • OperatingSystem.IsIOS() returns true for Mac Catalyst as well, so Mac Catalyst is explicitly excluded — it runs on macOS, is not subject to RunningBoard suspension policy, and keeps its current locking behavior.
  • iOS and tvOS simulators report as iOS/tvOS, so they follow the device default. That keeps simulator test runs representative of device behavior rather than silently diverging.
  • Browser and WASI continue to force locking off unconditionally, as before; they are unaffected by this change.

Because this goes through AppContextConfigHelper, an explicit false still wins over the new default, so anyone depending on FileShare enforcement on iOS/tvOS can opt back in.

The observable consequence is that on iOS and tvOS, FileShare is no longer enforced between processes or handles: opening a file with FileShare.None no longer prevents a second open, and the IOException that previously signalled a sharing violation is no longer thrown. This matches the behavior that Browser and WASI have had for some time.

Test changes

Add a small iOS/tvOS-specific test asserting that file locking is in fact off by default, so a future regression in the predicate is caught directly rather than only as a cascade of sharing-violation test failures.

Most lock-dependent tests in System.IO.FileSystem were already conditioned on PlatformDetection.IsFileLockingEnabled, which reflects the runtime's DisableFileLocking value and therefore picks up the new default automatically. The second commit extends that same pattern to the remaining tests that depend on FileShare being enforced:

Area Change
System.Reflection GetAssemblyName_LockedFile now uses ConditionalFact on IsFileLockingEnabled
System.IO.IsolatedStorage OpenFile_PassesFileShare now uses ConditionalFact on IsFileLockingEnabled
System.IO.MemoryMappedFiles FileInUse_CreateFromFile_FailsWithExistingNoShareFile now uses ConditionalFact
System.ComponentModel.Composition The four ConstructorN_LockedFileAsCodeBaseArgument_ShouldThrowFileLoad tests use ConditionalFact

In two of these cases the new condition subsumes an existing [SkipOnPlatform(TestPlatforms.Browser, ...)] attribute — Browser was skipped precisely because it does not honor file locking — so the attribute is removed in favor of the more general check.

Validation

Built and ran locally on macOS:

  • ./build.sh clr+libs -rc checked
  • ./build.sh clr.corelib+clr.nativecorelib+libs.pretest -rc checked

Test results:

Suite Total Failed
System.IO.FileSystem.Tests 9,607 0
System.IO.FileSystem.Tests (locking disabled) 2,095 0
System.Reflection tests 1,778 0
System.IO.IsolatedStorage tests 198 0
System.IO.MemoryMappedFiles tests 937 0
System.ComponentModel.Composition tests 1,431 0

The "locking disabled" run exercises the new code path by forcing DisableFileLocking on, which is the configuration iOS and tvOS will now get by default.

Fixes #133697.

Note

This pull request description was generated with the assistance of GitHub Copilot.

Use the existing System.IO.DisableFileLocking configuration switch with a platform-specific default for iOS and tvOS. This avoids kernel-visible advisory locks that can cause suspended apps to be terminated, while preserving explicit configuration overrides and the existing Mac Catalyst behavior.\n\nAdd platform-specific coverage that verifies file locking is disabled by default on iOS and tvOS; existing lock-dependent tests already skip based on the same runtime setting.\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>\nCopilot-Session: 30b5cc74-ca79-43d5-bbe4-65cdbc5b273e
Condition tests that require FileShare enforcement on PlatformDetection.IsFileLockingEnabled. This extends the existing System.IO.FileSystem test pattern to reflection, isolated storage, memory-mapped files, and assembly catalog tests so they are skipped when file locking defaults to disabled on iOS and tvOS.\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>\nCopilot-Session: 30b5cc74-ca79-43d5-bbe4-65cdbc5b273e
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).
13 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @dotnet/area-system-io
See info in area-owners.md if you want to be subscribed.

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.

🟡 Changes recommended

The broad FileShare compatibility change remains unresolved, and opt-in plus Mac Catalyst coverage is missing.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Disables Unix advisory file locking by default on iOS and tvOS while preserving Mac Catalyst behavior and configuration overrides.

Changes:

  • Updates the platform-specific locking default.
  • Adds iOS/tvOS behavior coverage.
  • Gates lock-dependent tests on IsFileLockingEnabled.
File summaries
File Change
src/libraries/System.Runtime/tests/System.Reflection.Tests/AssemblyNameTests.cs Gates locked-file reflection testing.
src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/FileStream/ctor_str_fm_fa_fs.cs Tests disabled locking on iOS/tvOS.
src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs Changes platform locking defaults.
src/libraries/System.IO.MemoryMappedFiles/tests/MemoryMappedFile.CreateFromFile.Tests.cs Gates sharing-violation testing.
src/libraries/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/OpenFileTests.cs Gates FileShare enforcement testing.
src/libraries/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Hosting/AssemblyCatalogTests.cs Gates locked-file tests.
Review details

Suppressed comments (2)

src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs:41

  • The new coverage only checks the effective platform default. The existing DisabledFileLockingSwitchTests forces System.IO.DisableFileLocking=true, so it does not verify the advertised iOS/tvOS opt-in (System.IO.DisableFileLocking=false or DOTNET_SYSTEM_IO_DISABLEFILELOCKING=0) still re-enables sharing enforcement. Please add an isolated Apple-mobile test for that override so this changed default cannot accidentally make the opt-in ineffective.
                defaultValue: (OperatingSystem.IsIOS() && !OperatingSystem.IsMacCatalyst()) || OperatingSystem.IsTvOS());

src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/FileStream/ctor_str_fm_fa_fs.cs:127

  • The new assertion covers iOS/tvOS but not the compatibility-critical !OperatingSystem.IsMacCatalyst() branch. OperatingSystem.IsIOS() is also true for Mac Catalyst, so add a Mac Catalyst-specific assertion that file locking remains enabled; otherwise an accidental removal of this exclusion could pass the added tests.

[!NOTE]
This review comment was generated with GitHub Copilot.

        [Fact]
        [PlatformSpecific(TestPlatforms.iOS | TestPlatforms.tvOS)]
        public void FileLockingDisabledByDefault()
        {
            Assert.False(PlatformDetection.IsFileLockingEnabled);
  • Files reviewed: 6/6 changed files
  • Comments generated: 1
  • Review effort level: Lite

Add Mac Catalyst coverage to ensure its existing file-locking default remains enabled even though OperatingSystem.IsIOS also identifies Mac Catalyst.\n\nAdd a focused iOS and tvOS test project that explicitly sets System.IO.DisableFileLocking to false and verifies both the effective runtime setting and FileShare.None enforcement.\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>\nCopilot-Session: 30b5cc74-ca79-43d5-bbe4-65cdbc5b273e
@jkotas
jkotas requested a review from adamsitnik September 16, 2026 12:58
@jkotas jkotas added the os-ios Apple iOS label Sep 16, 2026
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to 'os-ios': @vitek-karas, @kotlarmilos, @steveisok, @akoeplinger
See info in area-owners.md if you want to be subscribed.

@adamsitnik adamsitnik 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.

@rolfbjarne the changes LGTM, but please take a look at my comment, as I think it would be nice to use src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/DisabledFileLockingTests/System.IO.FileSystem.DisabledFileLocking.Tests.csproj to keep testing file locking on iOS and tvOS.

Configure the existing disabled-file-locking test project to enable locking on iOS and tvOS, allowing its full FileStream sharing suite to continue exercising lock enforcement on those platforms.\n\nMove the platform-default assertions into the normal file-system test project so they continue validating the iOS, tvOS, and Mac Catalyst defaults without inheriting the override used by the specialized project. Remove the separate Apple-only override project added in the prior local commit.\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>\nCopilot-Session: 30b5cc74-ca79-43d5-bbe4-65cdbc5b273e
Keep ConfigSwitchIsHonored explicit about Windows always having file locking enabled while also accounting for the iOS and tvOS override used by the Unix-only specialized test project.\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>\nCopilot-Session: 30b5cc74-ca79-43d5-bbe4-65cdbc5b273e
Include iossimulator and tvossimulator in the specialized test project's Apple mobile override so the full file-locking suite enables lock enforcement in simulator CI as well as device target configurations.\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>\nCopilot-Session: 30b5cc74-ca79-43d5-bbe4-65cdbc5b273e
Copilot AI review requested due to automatic review settings September 16, 2026 13:58

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.

🔵 Needs a closer look

One or more issues must be addressed before approval.

Review details

Suppressed comments (1)

src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/FileLockingDefaults.cs:14

  • These assertions only inspect SafeFileHandle.DisableFileLocking through PlatformDetection; they never exercise the observable FileShare behavior. Because the existing sharing-violation tests are conditionally skipped when this flag is false, a regression where the flag reports false but a default iOS/tvOS open still takes flock would pass. Add an iOS/tvOS test that opens a FileShare.None handle and verifies a second open succeeds, while retaining the explicit-locking project for the opt-in path.
            Assert.False(PlatformDetection.IsFileLockingEnabled);
  • Files reviewed: 9/9 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

@rolfbjarne

Copy link
Copy Markdown
Member Author

/azp run runtime-ioslike,runtime-maccatalyst,runtime-ioslikesimulator

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).

@adamsitnik adamsitnik 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.

LGTM, thank you for providing the fix @rolfbjarne !

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.

🔵 Needs a closer look

It changes process-wide FileShare semantics on iOS and tvOS and warrants final human review of compatibility impact.

Review details
  • Files reviewed: 9/9 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

@vitek-karas

Copy link
Copy Markdown
Member

/azp run runtime-extra-platforms

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).

@vitek-karas

Copy link
Copy Markdown
Member

CI status analysis for the current PR head:

  • Android NativeAOT x64 and arm64 — pre-existing main/infrastructure break. Both fail compiling src/tasks/AndroidAppBuilder/Templates/monodroid-nativeaot.cs with CS9377/CS9389 related to unsafe/safe memory-safety annotations. This file is not touched by the PR, and the exact same failure occurs on main in runtime-extra-platforms build 1603402.
  • runtime Helix failures — infrastructure/deadletter failure. Four iOS smoke work items ended as exit -1 deadletters. Build Analysis matches these to #123796, an XHarness/devicectl installation failure. The same class of iOS deadletter failures appears on main build 1602939.
  • Android cryptography tests — known baseline failure. Both Android jobs fail DynamicChainTests.BuildChainForExpiredSelfSignedCertificate because NotTimeValid is not set. Build Analysis matches these to #134002 and #133314; the same failures occur on main build 1603402.
  • Apple Helix test failures — known infrastructure/test failures. Build Analysis matches the reported failures to existing issues, including #128758 for no-space-on-device, #133325 for Mac Catalyst hangs, #134222 for the NameResolution timeout, #133842 for the HTTP/3 cookie test, and #122522 for the HTTP metrics test.

Note

This comment was generated with GitHub Copilot.

@vitek-karas

Copy link
Copy Markdown
Member

/ba-g based on the analysis above - the only iOS related failure are all known issues already existing before this change.

@vitek-karas

Copy link
Copy Markdown
Member

@matouskozak Could you please review just the part about all failures being unrelate? The only change after the signoff from Steve and Adam has been a merge from main - not due to conflicts, but to bring in fix for Apple test build break to unblock parts of CI.

I'm rushing this a bit since I need to start the backport to 11 and have it ready today.

@adamsitnik adamsitnik 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.

LGTM

@vitek-karas

Copy link
Copy Markdown
Member

CI failure investigation for build 1602914: the failures are unrelated to the changes in this PR.

Android NativeAOT build

Both Android NativeAOT legs (android-arm64 Release NativeAOT and android-x64 Release NativeAOT) failed compiling src/tasks/AndroidAppBuilder/Templates/monodroid-nativeaot.cs with CS9377/CS9389. This is tracked by dotnet/runtime#134115. The PR does not modify AndroidAppBuilder or that template.

Android certificate-chain tests

The Android arm and arm64 legs failed BuildChainForExpiredSelfSignedCertificate with:

NotTimeValid should be set; status flags: NoError

This is tracked by dotnet/runtime#133314, which includes build 1602914. The PR does not modify the cryptography implementation or these tests.

MacCatalyst x64 DNS test

System.Net.NameResolution.Tests.Dns_GetHostEntryAsync_IPString_Ok timed out after 30 seconds with System.TimeoutException. No PR files overlap this test or the NameResolution implementation.

MacCatalyst arm64 NativeAOT work items

Microsoft.Extensions.Configuration.Functional.Tests and Microsoft.Extensions.Hosting.Unit.Tests both hit the XHarness 75-minute work-item timeout.

The AOT/Xcode build completed successfully (cmake and xcodebuild exited 0), and the app connected to XHarness and emitted Test execution started. The timeout occurred afterward: the open process did not terminate or produce test completion/results within 4500 seconds, so XHarness killed it with exit code 137. XHarness then reported Could not find pid in mtouch output and Test run never launched. There is no individual test assertion, managed exception, or crash report in the logs, so the exact internal reason the launched test runner failed to complete cannot be determined from this run.

This failure signature also occurred on an unrelated main-branch run: build 1604459, completed on September 21, 2026 at 09:14 UTC (11:14 CEST) from refs/heads/main. That run reported the same XHarness/mtouch symptoms (Run timed out, Could not find pid in mtouch output, and Test run never launched). The pattern is tracked in dotnet/runtime#133325, which also records the same signature in other unrelated runs. Related Apple NativeAOT startup/hang failures are tracked in dotnet/runtime#126697.

The prior main-branch reproductions used CoreCLR work items and a three-hour timeout rather than these exact NativeAOT assemblies and 75-minute timeout, but they establish that the same MacCatalyst XHarness failure mode occurs independently of this PR. The PR changes file-locking behavior for iOS and tvOS and explicitly leaves MacCatalyst file locking enabled; none of the affected test areas are modified by the PR.

Note

This comment was generated with GitHub Copilot.

@vitek-karas

Copy link
Copy Markdown
Member

/ba-g All issues unrelated

@vitek-karas
vitek-karas merged commit 824279d into main Sep 21, 2026
184 of 188 checks passed
@vitek-karas
vitek-karas deleted the no-file-locking-on-mobile branch September 21, 2026 11:52
@vitek-karas

Copy link
Copy Markdown
Member

/backport to release/11.0

@github-actions

Copy link
Copy Markdown
Contributor

Started backporting to release/11.0 (link to workflow run)

steveisok pushed a commit that referenced this pull request Sep 21, 2026
Backport of #134060 to release/11.0

/cc @vitek-karas @rolfbjarne

## Customer Impact

- [x] Customer reported
- [ ] Found internally

Application on iOS which happens to take a lock on a file can be killed
by the OS if it goes to background. For details look at the customer
reported issue: #133697

## Regression

- [x] Yes
- [ ] No

This worked on mono, since mono actually didn't lock files in the file
system on iOS (effectively what this PR does)

## Testing

Manual validation of the customer scenario. Full CI run on extra
platforms.

## Risk

Low - there will be one difference from mono behavior. mono implemented
locking for files from the same process (and from .NET), with this
change CoreCLR doesn't do that. This is considered very unlikely to be
relied upon by customers. We will document this change as a breaking
change for .NET 11.

**IMPORTANT**: If this backport is for a servicing release, please
verify that:

- For .NET 8 and .NET 9: The PR target branch is `release/X.0-staging`,
not `release/X.0`.
- For .NET 10+: The PR target branch is `release/X.0` (no `-staging`
suffix).

## Package authoring no longer needed in .NET 9

**IMPORTANT**: Starting with .NET 9, you no longer need to edit a NuGet
package's csproj to enable building and bump the version.
Keep in mind that we still need package authoring in .NET 8 and older
versions.

Co-authored-by: Rolf Bjarne Kvinge <rolf@xamarin.com>
Co-authored-by: Vitek Karas <10670590+vitek-karas@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@dotnet-milestone-bot dotnet-milestone-bot Bot added this to the 12.0-preview1 milestone Sep 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

iOS: app killed with 0xdead10cc — StackTraceSymbols holds a shared flock on the PDB inside the app bundle

6 participants