SagerNet / SagerNet/sing-tun

system stack panic: As4 called on IPv6 address under dual-stack TCP churn on iOS

Open
#86 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Go
Stars
237
Forks
228
PR merge metrics
No merged PRs in 30d

Description

Environment

  • sing-box for Apple: 1.14.0
  • sing-box core: 1.14.0-rc.1
  • sing-tun: v0.9.0-beta.2
  • Go: go1.26.6, ios/arm64
  • Device: iPhone 16 (iPhone17,3)
  • OS: iOS 27.0 beta (24A5424a)
  • TUN stack: system
  • TUN addresses: dual stack (one IPv4 prefix and one IPv6 prefix)

This was reproduced twice with the official, unmodified core/framework. No locally patched sing-tun code was installed.

What happened

The Network Extension hit critical memory pressure during a burst of dual-stack TCP connection retries and then panicked in rewriteIPv4TCP because an IPv6 address reached netip.Addr.As4().

Both reproductions followed the same pattern:

  1. Start the Network Extension with a dual-stack system TUN.
  2. Generate high connection churn, including repeated TCP attempts to currently unreachable IPv6 destinations.
  3. The in-process OOM monitor reports critical memory pressure.
  4. About 1–2 minutes after launch, the Network Extension aborts with the same panic.

The two observed timelines were:

  • Start 07:31:51Z; memory-pressure report 07:32:21Z; panic 07:34:54Z.
  • Start 08:46:30Z; memory-pressure report 08:47:12Z; panic 08:48:42Z.

Panic

panic: As4 called on IPv6 address

net/netip.Addr.As4(...)
github.com/sagernet/sing-tun.rewriteIPv4TCP(...)
    github.com/sagernet/sing-tun@v0.9.0-beta.2/stack_system.go:553
github.com/sagernet/sing-tun.(*System).processIPv4TCP(...)
    github.com/sagernet/sing-tun@v0.9.0-beta.2/stack_system.go:483
github.com/sagernet/sing-tun.(*System).processIPv4(...)
github.com/sagernet/sing-tun.(*System).processPacket(...)
github.com/sagernet/sing-tun.(*System).tunLoop(...)

The native exception is SIGABRT, with exceptionReason set to As4 called on IPv6 address; it was not a jetsam termination.

Memory-pressure evidence from the second reproduction

At 08:47:12Z, 42 seconds after launch:

memoryUsage:    40 MB
availableMemory: 9.5 MB
heapAlloc:      19 MB
heapInuse:      25 MB
heapSys:        33 MB
sys:            42 MB
totalAlloc:     1.7 GB
numGC:          305
numGoroutine:   228

Heap inuse_space top entries:

7.24 MB  github.com/sagernet/sing-tun.(*TCPNat).Lookup
3.27 MB  github.com/sagernet/sing-tun.(*ForwardDispatcher).insertEntry
4.27 MB  github.com/sagernet/sing-tun.(*ForwardDispatcher).installSimple (cumulative)

Heap inuse_objects included:

43,734  github.com/sagernet/sing-tun.(*TCPNat).Lookup
32,769  github.com/sagernet/sing-tun.(*ForwardDispatcher).installSimple

Source observation

In the system stack, IPv4 and IPv6 TCP processing appear to share the same tcpNat. Both families call Lookup/LookupBack, while processIPv4TCP passes the returned session addresses to rewriteIPv4TCP, which calls As4() without checking the address family.

I cannot yet prove whether the wrong-family session is caused by NAT-port reuse, a stale entry, or a race, but the panic proves that an IPv6 address was returned to the IPv4 rewrite path. It may be worth either separating the TCP NAT state by address family or validating the session family before rewriting.

Expected behavior

High dual-stack connection churn, including unreachable IPv6 destinations, should not allow an IPv6 session to reach the IPv4 rewrite path and should not panic the Network Extension.

Full crash/OOM reports and pprof data can be provided privately if useful. The active configuration is not attached because it contains proxy credentials and subscription data.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in stack_system.go at processIPv4TCP around line 483 and rewriteIPv4TCP around line 553, then trace the shared TCPNat Lookup and LookupBack paths. Reproduce dual-stack TCP churn with unreachable IPv6 destinations while observing the reported memory-pressure and panic sequence. Done means an IPv6 session cannot reach the IPv4 rewrite path and the Network Extension does not panic.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
networking
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.