Repository navigation
[iOS] Network enumeration can spin indefinitely when sysctl queries are denied #135098
Description
Description
System.Net.NetworkInformation.Functional.Tests hangs on physical iOS 27 devices under both CoreCLR and NativeAOT, eventually reaching the 4,500-second timeout. Device logs contain repeated sandbox denials for net.inet.udp.pcblist or net.inet.tcp.pcblist.
Reported by @matouskozak in #133575; initial investigation reply.
The shared BSD native implementation ignores the initial sysctlbyname size-query failure and retries all subsequent failures. A denied query can leave the size at zero, producing an endless 0 -> 0 -> 0 allocation/retry loop. In a controlled native reproduction, the syscall itself returns promptly; our retry loop is what spins.
This affects the implementation behind these public APIs, not just the test harness:
IPGlobalProperties.GetActiveTcpConnections()IPGlobalProperties.GetActiveTcpListeners()IPGlobalProperties.GetActiveUdpListeners()
Ordinary socket, HTTP, and DNS operations do not depend on these enumeration queries.
Reproduction Steps
Observed device configuration: run System.Net.NetworkInformation.Functional.Tests in the physical-iOS 27 configuration reported in the linked PR. To exercise an individual affected path, call one of the enumeration APIs from an iOS app using the affected runtime, for example:
using System.Net.NetworkInformation;
var properties = IPGlobalProperties.GetIPGlobalProperties();
_ = properties.GetActiveUdpListeners();
// Exercise GetActiveTcpConnections() and GetActiveTcpListeners() separately.The permission denial is environment-dependent: the stock iOS 27 simulator tested locally allowed all four TCP/UDP pcbcount/pcblist queries, so this is not a reliable simulator reproduction of the device restriction.
Controlled macOS reproduction: in a disposable child process, explicitly deny one real sysctl name with a sandbox profile, for example:
(version 1)
(allow default)
(deny sysctl-read (sysctl-name "net.inet.udp.pcblist"))Calling the UDP enumeration API then exercises the denied size-query path without also denying the preliminary count query. Equivalent TCP and count-query cases are in the prototype regression test.
A standalone Objective-C probe also reproduced the failure mechanism using real kernel denials and a copy of the existing retry loop. This is a controlled macOS policy reproduction, not evidence that the stock simulator enforces the same policy as a device. The diagnostic sandbox API is deprecated and requires an unsandboxed host; its suitability for permanent CI coverage needs consideration.
Expected behavior
A failed sysctl query should return promptly through the native layer and produce a meaningful managed exception. A permission denial must not be retried as a buffer-sizing problem or reported as a successful empty result.
This issue is about handling unavailable information correctly, not bypassing Apple's restrictions or restoring system-wide socket visibility.
Actual behavior
The device suite times out after 4,500 seconds while logging repeated sandbox denials.
Representative output from the controlled macOS native reproduction:
RAW SIZE net.inet.tcp.pcblist: ret=-1 errno=1 (Operation not permitted) size=0->0 elapsed=0.008ms
RAW DATA net.inet.tcp.pcblist: ret=-1 errno=1 (Operation not permitted) size=0->0 elapsed=0.002ms
GUARD STOP net.inet.tcp.pcblist: retries=1000 size=0 errno=1 elapsed=1.209ms
The guard belongs to the diagnostic probe, not the runtime. An unbounded probe continued for millions of retries until externally terminated. UDP reproduced the same zero-size retry behavior. A nonexistent-node negative control returned ENOENT immediately and likewise exposed the unconditional-retry defect.
The preliminary pcbcount helpers also ignore errors and can return an uninitialized count, so not every failure must manifest as the same hang.
Regression?
The start of the Apple restriction and the affected .NET release range have not been established. We do not have a controlled passing baseline on an older physical-iOS configuration.
Apple's iOS 26.1 advisory for CVE-2025-43413 describes restricting a sandboxed app's ability to observe system-wide network connections. It is consistent with this investigation, but does not identify these sysctl nodes or establish when this particular denial began.
#133960 is related but distinct: on macOS 27, @wfurt observed successful queries filtered to the querying process's sockets. This issue concerns hard query failures turning into an infinite retry loop.
Known Workarounds
Avoid these enumeration APIs in affected applications. A managed timeout around a synchronous call does not stop the native loop; abandoning the task can leave it consuming CPU.
No workaround to restore denied enumeration on physical iOS has been verified.
Configuration
- Reported failure: physical iOS 27 devices, arm64, both CoreCLR and NativeAOT, in the CI investigation for [Test] Run Apple device smoke tests on scouting queues #133575. The analyzed CI source revision was
22946b4bc9853e2ce2eebf8a0d4f808f597c87be. - Controlled native reproduction and library validation: macOS 26.7.1, arm64, Xcode 27.0.
- Simulator comparison: iOS 27.0 arm64, build
24A434; the stock simulator permitted the real queries. - The faulty loops live in shared
System.NativeBSD code, rather than a CoreCLR- or NativeAOT-specific implementation. - The prototype has not been executed on a physical iOS device or on native FreeBSD/OpenBSD.
Other information
Root cause
In the investigated source:
GetEstimatedSizeignores the syscall result and returns a length initially set to zero.- The TCP and UDP paths retry every data-query failure. Doubling zero makes no progress;
malloc(0)returned non-null in the Darwin reproduction. - The same sections show the unchecked count queries. These need explicit error handling as well.
Suggested fix direction
- Check initial size/count-query results and propagate failures immediately.
- Retry data queries only for insufficient-buffer errors (
ENOMEM). Keep allocated capacity separate from the length updated bysysctlbyname, and require overflow-checked, positive growth. - Preserve errno across cleanup and capture it in the interop declarations. A reasonable mapping is
NetworkInformationException(rawErrno)for operational failures, retainingPlatformNotSupportedExceptionforENOTSUP. - Handle legitimate empty results and explicitly report insufficient caller capacity. Update the ICMPv6 caller of the size helper to respect the revised error contract.
- Preserve other BSD platforms: FreeBSD exposes UDP
pcblist, but not UDPpcbcount. Making every count failure terminal without addressing this would prevent supported enumeration. Estimate UDP capacity from thepcblistsize on FreeBSD instead, with checked conversion. - Add regression coverage for denied count/size/data queries, zero-length results, and
ENOMEMthat changes the output length. Keep iOS support-policy/test-expectation changes separate from the generic native error-handling fix.
This follows the existing Windows pattern of retrying only insufficient-buffer errors and reporting other errors as NetworkInformationException. Linux also wraps failures to read its connection tables in NetworkInformationException. Whether some iOS configurations should instead be explicitly classified as unsupported, as Android is, needs a separate support-policy decision.
Prototype and evidence
A candidate implementation is available in commit 7ed53f929e7, on simonrozsival-pr-failure-analysis. No fix PR has been opened. This is a starting point for discussion, not a claim of device validation.
- Six macOS sandbox-denial regression cases failed against the unfixed library and passed with the prototype.
- Latest local native build passed:
./dotnet.sh build src/native/libs/build-native.proj. - Latest functional run passed 252/252, including those six cases:
./dotnet.sh build /t:Test src/libraries/System.Net.NetworkInformation/tests/FunctionalTests/System.Net.NetworkInformation.Functional.Tests.csproj(default non-outer-loop suite). - Local deterministic native checks passed 17/17. Seven additional checks of the actual FreeBSD sizing function with host SDK layouts and mocked sysctl responses failed before the sizing correction and passed afterward. These harnesses are investigation artifacts, not committed regression tests or native FreeBSD execution.
Stopping the hang will make success-expecting iOS tests fail promptly when access is denied; it will not by itself make that device suite pass. Device validation, appropriate test expectations, and the long-term suitability of the deprecated macOS sandbox test mechanism remain open.
Note
This issue was prepared with GitHub Copilot assistance.
Activity
dotnet-policy-service commentedon Oct 2, 2026
Tagging subscribers to 'os-ios': @vitek-karas, @kotlarmilos, @steveisok, @akoeplinger
See info in area-owners.md if you want to be subscribed.
dotnet-policy-service commentedon Oct 2, 2026
Tagging subscribers to this area: @karelz, @dotnet/ncl
See info in area-owners.md if you want to be subscribed.
[-]BSD network enumeration can spin indefinitely when sysctl queries are denied (observed on iOS 27)[/-][+][iOS] Network enumeration can spin indefinitely when sysctl queries are denied[/+]wfurt commentedon Oct 2, 2026
do you plan to fix it @simonrozsival or should we take a look? The #133960 (comment) is related by macOS does not fail but it seems like the phone jail has stronger protection.
simonrozsival commentedon Oct 5, 2026
@wfurt I don't plan to look into it in near future. If you or somebody on your team can take a look, it would be awesome.