Skip to content

Handle read-only /tmp and missing NUMA/robust-mutex on HarmonyOS - #132827

Open
springmin wants to merge 5 commits into
dotnet:mainfrom
springmin:pr/ohos-sandbox-fixes
Open

Handle read-only /tmp and missing NUMA/robust-mutex on HarmonyOS#132827
springmin wants to merge 5 commits into
dotnet:mainfrom
springmin:pr/ohos-sandbox-fixes

Conversation

@springmin

@springmin springmin commented Aug 27, 2026

Copy link
Copy Markdown

Summary

HarmonyOS (OpenHarmony) app sandboxes differ from a plain Linux environment in
three ways that break .NET at startup or at runtime. This PR adds the
TARGET_OPENHARMONY platform guard and the three runtime fixes required to run on
HarmonyOS. All changes are no-ops on existing platforms (see "Impact" below).

This is the second PR of the series adding linux-ohos (HarmonyOS) support to
the runtime (tracking issue: #132866). It depends on the build infrastructure
PR #132953, which defines TargetOpenHarmony / TARGET_OPENHARMONY; until that
lands, this PR is a compile-time no-op on every platform. The split keeps the
sandbox fixes reviewable independently of the build infrastructure.

Changes

1. Skip the GC NUMA probe on HarmonyOS (numasupport.cpp)

get_mempolicy/mbind are blocked by the HarmonyOS seccomp policy, so the NUMA
probe SIGSYS-crashes the process at startup. The syscalls are compiled out for
TARGET_OPENHARMONY; the GC falls back to single-node, which is correct for phones.

2. Honor TMPDIR for shared-memory files on HarmonyOS (SharedMemoryManager.Unix.cs)

/tmp is mounted read-only in the HarmonyOS app sandbox, so shared-memory files
(named mutexes, memory-mapped files) must not be placed under a hardcoded
/tmp/. On TARGET_OPENHARMONY the shared-memory files directory is now derived from
Path.GetTempPath(), which honors TMPDIR and matches how the rest of the
runtime resolves the temp directory. All other platforms keep the existing
hardcoded /tmp/ behavior unchanged.

3. Fall back from pthread mutexes for NamedMutex (NamedMutex.Unix.cs)

The HarmonyOS sysroot's pthread lacks robust-mutex support. NamedMutex falls
back to the shared-memory-file implementation (already the path for
OpenBSD/Haiku).

Supporting: OperatingSystem.IsOpenHarmony() + TARGET_OPENHARMONY

  • OperatingSystem.IsOpenHarmony()internal, compile-time TARGET_OPENHARMONY, mirrors
    IsHaiku(). No public API change in this PR.
  • System.Private.CoreLib.Shared.projitemsTARGET_OPENHARMONY define constant from
    TargetOpenHarmony (mirroring TargetsAndroid/TARGET_ANDROID).

Tests (MutexTests.cs)

The NamedMutex_* shared-memory tests derive the global shared-memory directory
the same way the runtime does ({SharedFilesPath}/.dotnet/shm/global), mirroring
the platform-conditional selection in change 2, so they stay in sync with the
runtime on every platform.

Impact on existing platforms

None:

  • TARGET_OPENHARMONY is only defined when TargetsLinuxOhos == 'true', which no
    existing build sets.
  • IsOpenHarmony() returns false on every existing platform.
  • numasupport.cpp guards are additive (&& !defined(TARGET_OPENHARMONY)); with
    TARGET_OPENHARMONY undefined the behavior is byte-identical.
  • The shared-memory files directory on non-HarmonyOS platforms is unchanged
    (/tmp/); the TMPDIR-honoring path is compiled in only for TARGET_OPENHARMONY.

Validation

  • Full clr.native+libs+host+packs -os linux-ohos -arch arm64 --cross cross-build
    succeeds: 0 Warning(s) 0 Error(s).
  • libcoreclr.so for linux-ohos-arm64 contains zero references to
    get_mempolicy/mbind (verified via objdump).
  • NativeAOT IntermediatesDir path fix verified in the full build.
  • CI: the first run failed MutexTests.NamedMutex_* on the Unix legs because the
    shared-memory path change was not yet scoped to TARGET_OPENHARMONY; that is fixed by
    the latest push (the runtime path on non-HarmonyOS platforms is /tmp/ again,
    which the tests target). On the re-run, all previously failing Unix legs pass.
    The remaining failures are unrelated to this PR: CompositeMLDsa* on the
    Windows legs (the Helix Windows queues' CNG provider reports "The requested
    operation is not supported" for Composite ML-DSA; the same tests pass on the
    osx-arm64-NativeAOT leg in this run) and a browser-wasm WasmTestOnChrome
    time-out on the LibraryTests_EAT leg (an infrastructure flake — a different
    test work item failed on each run).

Notes for reviewers

  • The build infrastructure (-os linux-ohos, RID graph, NDK toolchain
    plumbing) is intentionally not in this PR — it will follow so each PR is
    independently reviewable. Without it, TARGET_OPENHARMONY is simply never defined.
  • HarmonyOS targets are not built in CI yet; the follow-up infra PR will add
    linux-ohos cross legs mirroring linux-bionic.

Note

This PR was authored with AI assistance (Copilot/agent tooling) under the
repository owner's direction.

HarmonyOS (OpenHarmony) app sandboxes differ from a plain Linux environment in
three ways that break .NET at startup or at runtime:

1. get_mempolicy/mbind are blocked by the seccomp policy, so the GC's NUMA
   probe SIGSYS-crashes the process. Compile the NUMA syscalls out for
   TARGET_OHOS; the GC falls back to single-node, which is correct for phones.

2. /tmp is mounted read-only. Shared-memory files (named mutexes, memory-mapped
   files) now live under Path.GetTempPath() instead of the hardcoded /tmp/,
   honoring TMPDIR like the rest of the runtime already does.

3. The sysroot's pthread lacks robust-mutex support, so NamedMutex falls back
   to the shared-memory-file implementation (already the path for
   OpenBSD/Haiku).

Adds internal OperatingSystem.IsOhos() (compile-time TARGET_OHOS, mirroring
IsHaiku()) and the TARGET_OHOS managed define. All changes are no-ops on
existing platforms.
@dotnet-policy-service dotnet-policy-service Bot added the community-contribution Indicates that the PR has been added by a community member label Aug 27, 2026
@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.

@springmin

Copy link
Copy Markdown
Author

@dotnet-policy-service agree

@springmin

Copy link
Copy Markdown
Author

CI failure classification (run 2)

The latest push fixes the only PR-caused failures: the four MutexTests.NamedMutex_* tests that failed on the Unix legs in the first run. They failed because the shared-memory files path change (/tmp/ -> Path.GetTempPath()) was not yet scoped to TARGET_OHOS: the tests hardcode /tmp/.dotnet/shm/global, while the runtime followed TMPDIR in the Helix environment. The change is now compiled out on every platform except TARGET_OHOS, and the tests mirror the runtime's path derivation, so the runtime and the tests agree on all platforms. All previously failing Unix legs now pass.

Three legs still fail, all unrelated to this PR:

  1. windows-x64 / windows-x86 CoreCLR_AllSubsetsCompositeMLDsaCngTests / CompositeMLDsaFactoryTests (126 failures each) fail with CryptographicException: The requested operation is not supported. The CNG provider on those Helix queues does not support Composite ML-DSA. This PR does not touch any crypto code, and the same tests pass on the osx-arm64 NativeAOT leg in this run, confirming this is environment-specific rather than caused by this PR.
  2. browser-wasm linux Release LibraryTests_EAT — a single WasmTestOnChrome work item times out (first run: System.IO.Compression.ZipFile.Tests, this run: System.Drawing.Primitives.Tests) — a known browser-automation flake.

Build Analysis and the aggregate runtime check fail as a consequence of the above.

Note

This comment was drafted with AI assistance (Copilot/agent tooling) under the repository owner's direction.

@springmin
springmin marked this pull request as ready for review August 27, 2026 22:57
@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.

@springmin

Copy link
Copy Markdown
Author

Tracking issue for the overall OpenHarmony (HarmonyOS) porting effort: #132866

Note

This comment was drafted with AI assistance (Copilot/agent tooling) under the repository owner's direction.

@springmin

Copy link
Copy Markdown
Author

This PR is ready for review. It is the first PR of a series adding OpenHarmony (HarmonyOS) support to the runtime (tracking issue: #132866). The changes are guarded by TARGET_OHOS, which no existing build sets, so they are no-ops on all currently supported platforms.

The PR spans three areas, so I'm pinging the respective owners:

  • @dotnet/area-system-io / @jeffhandleySharedMemoryManager.Unix.cs: on TARGET_OHOS, shared-memory files now honor TMPDIR via Path.GetTempPath() (the sandbox mounts /tmp read-only). All other platforms keep the existing /tmp/ behavior; the MutexTests.NamedMutex_* tests mirror the runtime's path derivation.
  • @dotnet/area-system-threading / @JulieLeeMSFT / @VSadovNamedMutex.Unix.cs: fall back to the shared-memory-file implementation on TARGET_OHOS (the sysroot's pthread lacks robust mutexes), plus the matching test update in MutexTests.cs.
  • @dotnet/gc / @anicka-netnumasupport.cpp: compile out the NUMA probe syscalls (get_mempolicy/mbind) on TARGET_OHOS; they are blocked by the sandbox's seccomp policy and SIGSYS-crash the process at startup.
  • Supporting: OperatingSystem.IsOhos() (internal, mirrors IsHaiku()) and the TARGET_OHOS define in System.Private.CoreLib.Shared.projitems.

CI status: the only PR-caused failures (four MutexTests.NamedMutex_* tests on the Unix legs) were fixed in the latest push and all Unix legs now pass. The remaining failures — CompositeMLDsa* on two Windows legs and a browser-wasm WasmTestOnChrome time-out — are unrelated to this PR (details in my earlier comment).

Happy to split the GC change into a separate PR if preferred.

Note

This comment was drafted with AI assistance (Copilot/agent tooling) under the repository owner's direction.

@jkoritzinsky

Copy link
Copy Markdown
Member

Is HarmonyOS only used in tablet/mobile/IOT scenarios or is it used in desktop scenarios as well? Would it be reasonable to use the in-process-only NamedMutex implementation like we do for Android, iOS, and MacCatalyst?

Comment thread src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems Outdated
@springmin

springmin commented Aug 28, 2026

Copy link
Copy Markdown
Author

Good questions. HarmonyOS NEXT is used across phones, tablets, and IoT devices, but it also targets desktop-class devices (HarmonyOS NEXT for PC), so cross-process synchronization is a real scenario for applications ported from Linux/Windows. Now,I am porting the dotnet runtime to harmonyos by AI in HarmonyOS PC.
A few considerations on the in-process-only option:

  • The shared-memory-file fallback (same path as OpenBSD/Haiku) preserves cross-process named-mutex semantics. An in-process-only implementation would silently reduce named mutexes to process-local on HarmonyOS, which would change behavior for ported desktop apps without any signal to the caller.
  • Shared memory files are not only used by NamedMutex: the wait subsystem (e.g., cross-process named EventWaitHandle) relies on the same SharedFilesPath directory, so the TMPDIR change in SharedMemoryManager.Unix.cs is needed regardless of the NamedMutex approach. (Note that the TMPDIR path is only compiled in under TARGET_OHOS, so existing platforms are unaffected.)

If the maintainers prefer the in-process-only NamedMutex for the initial OHOS milestone, we can switch — it would drop one of the three runtime fixes from this PR, while the TMPDIR change would remain.

Note

This comment was drafted with AI assistance (Copilot/agent tooling) under the repository owner's direction.

springmin added a commit to springmin/runtime-ohos that referenced this pull request Aug 28, 2026
Reflects jkotas' TargetsLinuxOhos -> TargetsOhos rename (already applied in
602a5b1/955126211cc) and records the reviewer feedback from
dotnet#132827 (jkotas rename + jkoritzinsky TMPDIR scoping).
springmin added a commit to springmin/runtime-ohos that referenced this pull request Aug 28, 2026
602a5b1 renamed the property in 7 files but missed the three most
critical ones: RuntimeIdentifier.props (property definition + TargetsLinuxGlibc
exclusion), Subsets.props (DefaultSubsets + _BuildAnyCrossArch), and
liveBuilds.targets (CoreCLRArtifactsPath). Without these, TargetsOhos would be
defined but never consumed. Renames complete the jkotas feedback from
dotnet#132827.
springmin added a commit to springmin/runtime-ohos that referenced this pull request Aug 28, 2026
Rebases the remaining OHOS porting work (30 files) into 3 PRs after the
sandbox-fix PR dotnet#132827 (tracking issue dotnet#132866). Includes exact file
inventories per PR, the TargetsOhos naming convention (jkotas feedback),
no-op guarantees, validation checklist, and A→C→B submission rationale.
@jkoritzinsky

Copy link
Copy Markdown
Member

There are no named event wait handles on non-Windows, your AI assessment is incorrect there.

Also, it looks like HarmonyOS is shifting to its own kernel instead of Linux. Can we change the RID to be ohos instead of linux-ohos since it's not guaranteed to be Linux based?

@springmin

Copy link
Copy Markdown
Author

Thanks for the feedback — you're right on both points. I've verified in the code that there are no named event wait handles or named semaphores on non-Windows (both throw PlatformNotSupportedException on Unix), so shared memory files are used exclusively by the cross-process NamedMutex path — my earlier statement was incorrect.

On the RID: HarmonyOS PC currently ships with two kernel variants — linux-ohos (Linux kernel) and harmony-ohos (the HarmonyOS native kernel) — across both arm and x86. Since the kernel is not guaranteed to be Linux-based, I agree ohos is the right RID going forward. The property and define names in this PR (TargetsOhos / TARGET_OHOS) are already kernel-agnostic, and the follow-up build-infrastructure PR will use the ohos RID.

On NamedMutex: The primary target for this port right now is HarmonyOS PC — running the .NET runtime on desktop-class devices to develop .NET applications for Harmony devices. The current design references Apple's macOS implementation: cross-process named mutexes backed by shared memory files, with the shared directory resolved via TMPDIR because the HarmonyOS sandbox mounts /tmp read-only.

Inspired by this discussion, I've refined the design to also cover non-PC devices that don't need cross-process mutexes: the cross-process path becomes selectable per device class through the existing build configuration (FeatureCrossProcessMutex, the same mechanism that gates iOS/Android). PC builds keep the macOS-style shared-memory-file implementation; builds for non-PC devices simply leave the feature disabled, which automatically excludes the shared-memory files code from compilation and falls back to the in-process named-mutex implementation — exactly like iOS and Android.

Concretely, this is a one-line condition on FeatureCrossProcessMutex in System.Private.CoreLib.Shared.projitems plus a TargetsOhosMobile property set by the build for non-PC targets (e.g. the ohos-mobile-arm64 RID). No other mobile behaviors (such as AssemblyDependencyResolver or PosixSignalRegistration) are affected, and the current PR itself stays unchanged — it provides the PC path.

Does this design sound reasonable? If you'd prefer not to have this variability, I can fall back to disabling the cross-process path for OHOS entirely (the simpler, iOS-aligned approach).

@am11

am11 commented Aug 30, 2026

Copy link
Copy Markdown
Member

You can ask your AI model to analyze the merge commits of OpenBSD PRs, which we have recently ported: https://github.com/dotnet/runtime/pulls?q=is:pr+label:os-openbsd. Then ask it to start porting the code. Once you have full set of changes in a branch; cross building, infra, coreclr, tools, r2r, aot, libraries, corehost, installer, you can create smaller branches with substantial work then upstream them sequentially like infra+coreclr changes in one PR, each library changes in a separate PR, dotnet/arcade upstreamed there and so on.

This way reviewer can make sense of what's going on, we can test what's being upstreamed etc. which is better than starting off of a random point and using undefined stuff like TARGET_OHOS.

@springmin

Copy link
Copy Markdown
Author

@am11

Thanks for the guidance — the OpenBSD port's pattern (e.g. #130761 CI leg, #129906 NativeAOT stubs, #130478 exepath — small, focused PRs) is exactly the model we intend to follow, and it's how this effort is already structured.

The full OHOS integration is staged on a dedicated branch (43 files in the runtime repo + 19 in the SDK repo) and split into sequential upstream PRs, tracked in #132866:

  1. This PR — runtime sandbox fixes, a no-op on all existing platforms
  2. Build infrastructure PR — -os linux-ohos + NDK toolchain, which defines TargetsOhos / TARGET_OHOS (16 files)
  3. Sysroot compile fixes + NativeAOT support (13 files)
  4. Remaining changes split per library into separate PRs
  5. SDK repo changes (RID graph, codesign) upstreamed separately

On the RID: both OpenHarmony and HarmonyOS currently ship Linux-based (linux-ohos) in their latest releases, and my porting branch (feature/ohos-cross-runtime) still uses linux-ohos. That said, I agree with jkoritzinsky that ohos is the more future-proof definition, and I'm waiting for his final decision. If the RID remains linux-ohos, I'll split and submit the complete PR series right after his decision; if it moves to ohos, I'll need some time to switch both the runtime and SDK repos from linux-ohos to ohos and re-verify end-to-end before submitting the complete series.

On "undefined TARGET_OHOS": it is a compile-time no-op until the infrastructure PR lands — we verified the builds are byte-identical on every existing platform and CI is green — and the ordering is deliberate so each PR stays independently reviewable; the infra PR simply flips the switch. That said, if you'd prefer the infra PR to land first so TARGET_OHOS is defined from the start, we can reorder the series.

Note

This comment was drafted with AI assistance (Copilot/agent tooling) under the repository owner's direction.

@am11

am11 commented Aug 30, 2026

Copy link
Copy Markdown
Member

The point was this is not the good first PR from "ordering" perspective of new platform port. The first PR for a new platform is normally expected to be touching places like eng/ dir or max eng/+src/coreclr dirs and precisely updating eng/native/, eng/common/{native,cross}/, eng/Subsets.props, code changes in coreclr etc. not some random project under src/libraries when nothing about this platform is defined in the repo.

The full OHOS integration is staged on a dedicated branch (43 files in the runtime repo + 19 in the SDK repo) and split into sequential upstream PRs, tracked in #132866:

You could use stacked PR approach for this port, it would help the future platforms port: https://docs.github.com/pull-requests/how-tos/stacked-pull-requests When we started OpenBSD port few months ago, stacked PR concept didn't exist. I'd have definitely opted for it.

Comment thread src/coreclr/gc/unix/numasupport.cpp Outdated
#include <minipal/utils.h>

#if defined(TARGET_LINUX) && !defined(TARGET_ANDROID)
#if defined(TARGET_LINUX) && !defined(TARGET_ANDROID) && !defined(TARGET_OHOS)

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.

TARGET_OPENHARMONY is more readable and it's 16 chars. We have a 16 chars precedence with an architecture macro TARGET_LOONGARCH64, so it shouldn't be a problem.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Done — renamed to TARGET_OPENHARMONY in the latest push (cc9ccb3).

/// Indicates whether the current application is running on HarmonyOS (OpenHarmony).
/// </summary>
[NonVersionable]
internal static bool IsOhos() =>

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.

Similarly here: IsOpenHarmony.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Done — renamed to IsOpenHarmony in the latest push (cc9ccb3).

<DefineConstants Condition="'$(Targetsillumos)' == 'true'">$(DefineConstants);TARGET_ILLUMOS</DefineConstants>
<DefineConstants Condition="'$(TargetsSolaris)' == 'true'">$(DefineConstants);TARGET_SOLARIS</DefineConstants>
<DefineConstants Condition="'$(TargetsHaiku)' == 'true'">$(DefineConstants);TARGET_HAIKU</DefineConstants>
<DefineConstants Condition="'$(TargetsOhos)' == 'true'">$(DefineConstants);TARGET_OHOS</DefineConstants>

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.

This would become TargetOpenHarmony for MSBuild scripts.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Done — renamed to TargetOpenHarmony in the latest push (cc9ccb3).

@springmin

Copy link
Copy Markdown
Author

@am11

Thanks — I've applied all three renames in the latest push (cc9ccb3): TARGET_OHOS -> TARGET_OPENHARMONY, IsOhos() -> IsOpenHarmony(), and TargetsOhos -> TargetOpenHarmony (MSBuild). The follow-up infrastructure now uses the same naming, and the PR description is updated.

On the ordering: agreed. The build infrastructure PR is now up as #132953 (-os linux-ohos, NDK toolchain, defines TargetOpenHarmony / TARGET_OPENHARMONY) — it is the first PR of the series. This PR (#132827, sandbox fixes) is the second, and it depends on #132953 so that TARGET_OPENHARMONY is defined from the start; I'll rebase it onto the updated main once #132953 merges. The remaining series (sysroot compile fixes + NativeAOT support, RID/packs, per-library changes, SDK repo) follows the same split, tracked in #132866.

Since this is a fork-based contribution, the PRs are submitted sequentially (each against main) rather than as GitHub stacked PRs, with the dependency noted in each description; after each PR merges I rebase the next one on the updated main so each diff stays small.

Note

This comment was drafted with AI assistance (Copilot/agent tooling) under the repository owner's direction.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-System.IO community-contribution Indicates that the PR has been added by a community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants