Skip to content

Memory leak in iOS NetworkExtension #25346

@giovannisch

Description

@giovannisch

Apple platform

iOS

Framework version

net10.0-*

Affected platform version

.NET 10.0.203 - iOS 26.4.2 (as SupportedOSPlatformVersion I set 16.1)

Description

I'm working on an iOS application that enables users to establish a VPN connection using a custom protocol.
I initially built it using .NET 8 (8.0.100) and it worked just fine, but after upgrading it to .NET 10 it seems like the NetworkExtension part (which is required in iOS) always exceeds the memory limit (which is set to 50 MB for iOS Network Extensions).

I built the same application for Android and it works also in .NET 10, so I excluded a problem in the protocol.

When I worked on the .NET 8 version for iOS, I had various memory leak problems that I managed to solve by using NSAutoreleasePool and manually disposing all the packets read from (and sent through) the PacketFlow - I took inspiration from this repo for the solution https://github.com/enclave-alistair/dotnet-ios-netextension (which is linked to the resolution of this other #17408).

I tried also different variations (calling ReadPackets, ReadPacketObjects and their async variations), I tried different ways to enclose the code in the NSAutoreleasePool, but they all led to memory leak when handling new incoming packets.

Steps to Reproduce

Right now I can't provide a reproduction repo, as it is code owned by my organization.

A modified snippet of TUN code is the following:

public class IOSTun
{
    private NEPacketTunnelFlow? packetFlow;
    private volatile bool isReading = false;

    public void BeginRead()
    {
        isReading = true;
        packetFlow.ReadPackets(HandleReadPackets);
    }
    
    private void HandleReadPackets(NSData[] packets, NSNumber[] _)
    {
        if (!isReading)
            return;

        using (var pool = new NSAutoreleasePool())
        {
            foreach (var packet in packets)
            {
                // Handle packets
            }
            Array.ForEach(packets, x => x?.Dispose());
            // I already tried disposing also the protocols array
            // Array.ForEach(_, x => x?.Dispose());
        }
        
        if (isReading)
            packetFlow!.ReadPackets(HandleReadPackets);
    }

    public void WritePackets(byte[] payload)
    {
        using var pool = new NSAutoreleasePool();
        using var data = NSData.FromArray(payload);
        // 2 -> AF_INET code
        using var packet = new NEPacket(data, 2);
        packetFlow?.WritePacketObjects([packet]);
    }

    public void StopReading()
    {
        isReading = false;
    }
}

You can also try to migrate this project to .NET 10 (follow also the guidance given in the #17408 to make it run).

Did you find any workaround?

No

Relevant logs

This is what is printed in the debugger (I managed to attach to the NetworkExtension via Rider).
EXC_RESOURCE (RESOURCE_TYPE_MEMORY: high watermark memory limit exceeded) (limit=50 MB)

And this is a part of the system crash logs that I got from the test iPhone 15 (note that in this test I was using ReadPacketObjects() method)

Thread 16 name:  tid_6d0b Dispatch queue: NEPacketTunnelFlow queue
Thread 16 Crashed:
0   libsystem_kernel.dylib        	      0x242d6d0cc __pthread_kill + 8
1   libsystem_pthread.dylib       	      0x1f2113810 pthread_kill + 267
2   libsystem_c.dylib             	      0x1a0f40f64 abort + 123
3   ...nterVPNApp.NetworkExtension	      0x10328954c sigabrt_signal_handler.cold.1 + 48
4   ...nterVPNApp.NetworkExtension	      0x103121300 sigabrt_signal_handler + 196
5   libsystem_platform.dylib      	      0x2b03dd178 _sigtramp + 55
6   libsystem_kernel.dylib        	      0x242d9599c abort_with_payload_wrapper_internal + 103
7   libsystem_kernel.dylib        	      0x242d95934 abort_with_reason + 31
8   libobjc.A.dylib               	      0x19252ac14 _objc_fatalv(unsigned long long, unsigned long long, char const*, char*) + 115
9   libobjc.A.dylib               	      0x19252aba0 _objc_fatal(char const*, ...) + 31
10  libobjc.A.dylib               	      0x192550de0 AutoreleasePoolPage::badPop(void*) + 259
11  CoreFoundation                	      0x1955a1ae8 _CFAutoreleasePoolPop + 31
12  Foundation                    	      0x192bf2518 -[NSAutoreleasePool release] + 139
13  ...nterVPNApp.NetworkExtension	      0x102efcda0 xamarin_release_managed_ref + 100
14  ...nterVPNApp.NetworkExtension	      0x102ea598c interp_to_native_trampoline + 172
15  ...nterVPNApp.NetworkExtension	      0x10312ca68 ves_pinvoke_method + 568
16  ...nterVPNApp.NetworkExtension	      0x1031269e8 mono_interp_exec_method + 8660
17  ...nterVPNApp.NetworkExtension	      0x10312283c interp_entry_from_trampoline + 640
18  ...nterVPNApp.NetworkExtension	      0x102ea5a60 native_to_interp_trampoline + 112
19  NetworkExtension              	      0x1cd6dd7dc __61-[NEPacketTunnelFlow readPacketObjectsWithCompletionHandler:]_block_invoke + 507
20  NetworkExtension              	      0x1cd55d9e4 NEVirtualInterfaceReadMultiplePackets + 1083
21  NetworkExtension              	      0x1cd55d588 __NEVirtualInterfaceCreateReadSource_block_invoke_2 + 99
22  libdispatch.dylib             	      0x1ce2de7fc _dispatch_client_callout + 15
23  libdispatch.dylib             	      0x1ce2c9664 _dispatch_continuation_pop + 595
24  libdispatch.dylib             	      0x1ce2dc538 _dispatch_source_latch_and_call + 395
25  libdispatch.dylib             	      0x1ce2db20c _dispatch_source_invoke + 843
26  libdispatch.dylib             	      0x1ce2cd2d0 _dispatch_lane_serial_drain + 331
27  libdispatch.dylib             	      0x1ce2cdf78 _dispatch_lane_invoke + 439
28  libdispatch.dylib             	      0x1ce2d83ec _dispatch_root_queue_drain_deferred_wlh + 291
29  libdispatch.dylib             	      0x1ce2d7ce4 _dispatch_workloop_worker_thread + 691
30  libsystem_pthread.dylib       	      0x1f210d3b8 _pthread_wqthread + 291
31  libsystem_pthread.dylib       	      0x1f210c8c0 start_wqthread + 7

Metadata

Metadata

Assignees

No one assigned

    Labels

    need-infoWaiting for more information before the bug can be investigated

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions