[GH-ISSUE #3146] Windows: tun2socks crashes with 0xc0000005 while using XRay/VLESS+Reality+XHTTP, causing Error 804 #1460

Open
opened 2026-10-13 20:42:53 +00:00 by bsakaguchi · 4 comments
Owner

Originally created by @DjentAgent on GitHub (Sep 8, 2026).
Original GitHub issue: https://github.com/amnezia-vpn/amnezia-client/issues/3146

Environment

  • AmneziaVPN: 5.0.1.5 (7d4f3e0f5)
  • OS: Windows 11 25H2 x86_64
  • Protocol: XRay / VLESS + Reality + XHTTP
  • Server works correctly on Android with the same configuration
  • AmneziaWG on Windows is stable

Problem

AmneziaVPN randomly disconnects with Error 804 while connected through XRay.

The issue is not tied to high load, sleep/hibernate, screen off, or reconnecting.
It may happen after a short time or after a much longer session.

The actual failure is a crash of tun2socks.exe.

Crash #1

tun2socks reports:

fatal error: unexpected signal during runtime execution
[signal 0xc0000005 code=0x0 addr=0x1c00000020 ...]

Relevant stack:

sync/atomic.(*Pointer[...]).Load
sync.(*poolChain).popTail
sync.(*Pool).getSlow
sync.(*Pool).Get
go.uber.org/zap/internal/pool.(*Pool[...]).Get
go.uber.org/zap/zapcore.getCheckedEntry
...
github.com/xjasonlyu/tun2socks/v2/log.Infof
github.com/xjasonlyu/tun2socks/v2/tunnel.(*Tunnel).handleUDPConn

After that AmneziaVPN logs:

Tun2socks process was closed with 2 exit code
XrayProtocol::stop()
ErrorCode::804

Crash #2

A second crash happened on another session, again with:

signal 0xc0000005
addr=0x1c00000020
sync.(*poolChain).popTail
sync.(*Pool).Get

But this time the stack was different:

io.discard.ReadFrom
io.Copy
net.(*TCPConn).WriteTo
github.com/xjasonlyu/tun2socks/v2/proxy.(*Socks5).DialUDP.func2

Again, after the crash:

Tun2socks process was closed with 2 exit code
XrayProtocol::stop()
Error 804

This suggests the zap logger itself is probably not the root cause, because the second crash happened in a different code path, while the same sync.Pool / access violation pattern remained.

Additional notes

  • Same VPS/XRay configuration is stable on Android.
  • Disabling IPv6 on the TUN adapter did not help.
  • Disabling power saving for the physical network adapter did not help.
  • Rebooting Windows does not prevent the issue from eventually recurring.
  • The crashes look random and are not correlated with traffic spikes or system load.

Expected behavior

The XRay connection should remain stable and tun2socks should not crash.

Originally created by @DjentAgent on GitHub (Sep 8, 2026). Original GitHub issue: https://github.com/amnezia-vpn/amnezia-client/issues/3146 Environment - AmneziaVPN: 5.0.1.5 (7d4f3e0f5) - OS: Windows 11 25H2 x86_64 - Protocol: XRay / VLESS + Reality + XHTTP - Server works correctly on Android with the same configuration - AmneziaWG on Windows is stable Problem AmneziaVPN randomly disconnects with Error 804 while connected through XRay. The issue is not tied to high load, sleep/hibernate, screen off, or reconnecting. It may happen after a short time or after a much longer session. The actual failure is a crash of tun2socks.exe. Crash #1 tun2socks reports: fatal error: unexpected signal during runtime execution [signal 0xc0000005 code=0x0 addr=0x1c00000020 ...] Relevant stack: sync/atomic.(*Pointer[...]).Load sync.(*poolChain).popTail sync.(*Pool).getSlow sync.(*Pool).Get go.uber.org/zap/internal/pool.(*Pool[...]).Get go.uber.org/zap/zapcore.getCheckedEntry ... github.com/xjasonlyu/tun2socks/v2/log.Infof github.com/xjasonlyu/tun2socks/v2/tunnel.(*Tunnel).handleUDPConn After that AmneziaVPN logs: Tun2socks process was closed with 2 exit code XrayProtocol::stop() ErrorCode::804 Crash #2 A second crash happened on another session, again with: signal 0xc0000005 addr=0x1c00000020 sync.(*poolChain).popTail sync.(*Pool).Get But this time the stack was different: io.discard.ReadFrom io.Copy net.(*TCPConn).WriteTo github.com/xjasonlyu/tun2socks/v2/proxy.(*Socks5).DialUDP.func2 Again, after the crash: Tun2socks process was closed with 2 exit code XrayProtocol::stop() Error 804 This suggests the zap logger itself is probably not the root cause, because the second crash happened in a different code path, while the same sync.Pool / access violation pattern remained. Additional notes - Same VPS/XRay configuration is stable on Android. - Disabling IPv6 on the TUN adapter did not help. - Disabling power saving for the physical network adapter did not help. - Rebooting Windows does not prevent the issue from eventually recurring. - The crashes look random and are not correlated with traffic spikes or system load. Expected behavior The XRay connection should remain stable and tun2socks should not crash.
Author
Owner

@DjentAgent commented on GitHub (Sep 8, 2026):

AmneziaVPN(1).log

AmneziaVPN.log

<!-- gh-comment-id:5582380437 --> @DjentAgent commented on GitHub (Sep 8, 2026): [AmneziaVPN(1).log](https://github.com/user-attachments/files/31948688/AmneziaVPN.1.log) [AmneziaVPN.log](https://github.com/user-attachments/files/31948714/AmneziaVPN.log)
Author
Owner

@HeisLuka commented on GitHub (Sep 8, 2026):

There is one packaging/build variable here that may be worth isolating before treating Error 804 as an XRay issue.

I checked the exact 5.0.1.5 source from the report. Amnezia builds:

tun2socks v2.6.0
with Go 1.26.0

However upstream tun2socks v2.6.0 itself declares:

go 1.23.1
gVisor 20250523...
zap 1.27.0

Upstream later explicitly migrated to Go 1.26 together with newer gVisor/dependencies, and current upstream v2.7.0 uses:

Go 1.26.3
gVisor 20260701...
zap 1.28.0

Current Amnezia dev still packages tun2socks/2.6.0.

I am not saying this proves a Go/toolchain incompatibility. But because the reported failures are Windows 0xc0000005 access violations inside Go runtime / sync.Pool, this gives a useful controlled build matrix.

I would reproduce the same UDP-heavy XRay workload with:

A. upstream v2.6.0 build / its original toolchain-compatible build

B. current Amnezia:
tun2socks v2.6.0 + Go 1.26.0

C. tun2socks v2.7.0 + current upstream Go/dependency set

Interpretation:

only B crashes
-> investigate Amnezia packaging/toolchain combination

A + B crash, C stable
-> likely v2.6/runtime dependency issue;
v2.7 becomes a strong upgrade candidate

A + B + C crash
-> reproduce/report upstream tun2socks/Go/gVisor Windows issue

only Amnezia-integrated runs crash
-> investigate process/TUN/IPC integration rather than tun2socks itself

This seems especially useful because the two crashes in the report do not share the same application stack frame:

crash 1: logging / handleUDPConn
crash 2: io.Copy / Socks5.DialUDP

while the low-level failure pattern is similar. That makes blaming zap from the first stack alone unsafe.

There is also a separate recovery question in Amnezia.

XrayProtocol::startTun2Socks() currently retries the known startup failure:

"resource busy"

but a runtime CrashExit is terminal:

tun2socks crash
-> stop()
-> ErrorCode::Tun2SockExecutableCrashed (804)

I would keep that separate from the root-cause experiment. Automatically restarting a process after a possible memory-corruption crash could hide the failure frequency.

If graceful recovery is later added, I would make it bounded and observable, e.g.:

first unexpected runtime crash
-> record crash signature + uptime
-> rebuild TUN/routing state cleanly
-> one controlled restart

repeated crash inside a short window
-> fail closed with 804

rather than an unlimited restart loop.

A useful regression/stress test would therefore record:

tun2socks build/version
Go version
runtime before crash
TCP/UDP flow counts
crash exit/status
automatic-recovery count

and run the same traffic profile for A/B/C.

That should tell us whether #3146 belongs primarily to Amnezia integration, the specific v2.6 build combination, or upstream tun2socks.

<!-- gh-comment-id:5586023722 --> @HeisLuka commented on GitHub (Sep 8, 2026): There is one packaging/build variable here that may be worth isolating before treating Error 804 as an XRay issue. I checked the exact 5.0.1.5 source from the report. Amnezia builds: tun2socks v2.6.0 with Go 1.26.0 However upstream tun2socks v2.6.0 itself declares: go 1.23.1 gVisor 20250523... zap 1.27.0 Upstream later explicitly migrated to Go 1.26 together with newer gVisor/dependencies, and current upstream v2.7.0 uses: Go 1.26.3 gVisor 20260701... zap 1.28.0 Current Amnezia dev still packages tun2socks/2.6.0. I am not saying this proves a Go/toolchain incompatibility. But because the reported failures are Windows 0xc0000005 access violations inside Go runtime / sync.Pool, this gives a useful controlled build matrix. I would reproduce the same UDP-heavy XRay workload with: A. upstream v2.6.0 build / its original toolchain-compatible build B. current Amnezia: tun2socks v2.6.0 + Go 1.26.0 C. tun2socks v2.7.0 + current upstream Go/dependency set Interpretation: only B crashes -> investigate Amnezia packaging/toolchain combination A + B crash, C stable -> likely v2.6/runtime dependency issue; v2.7 becomes a strong upgrade candidate A + B + C crash -> reproduce/report upstream tun2socks/Go/gVisor Windows issue only Amnezia-integrated runs crash -> investigate process/TUN/IPC integration rather than tun2socks itself This seems especially useful because the two crashes in the report do not share the same application stack frame: crash 1: logging / handleUDPConn crash 2: io.Copy / Socks5.DialUDP while the low-level failure pattern is similar. That makes blaming zap from the first stack alone unsafe. There is also a separate recovery question in Amnezia. XrayProtocol::startTun2Socks() currently retries the known startup failure: "resource busy" but a runtime CrashExit is terminal: tun2socks crash -> stop() -> ErrorCode::Tun2SockExecutableCrashed (804) I would keep that separate from the root-cause experiment. Automatically restarting a process after a possible memory-corruption crash could hide the failure frequency. If graceful recovery is later added, I would make it bounded and observable, e.g.: first unexpected runtime crash -> record crash signature + uptime -> rebuild TUN/routing state cleanly -> one controlled restart repeated crash inside a short window -> fail closed with 804 rather than an unlimited restart loop. A useful regression/stress test would therefore record: tun2socks build/version Go version runtime before crash TCP/UDP flow counts crash exit/status automatic-recovery count and run the same traffic profile for A/B/C. That should tell us whether #3146 belongs primarily to Amnezia integration, the specific v2.6 build combination, or upstream tun2socks.
Author
Owner

@C1nderC0ated commented on GitHub (Sep 22, 2026):

crashes for me too

<!-- gh-comment-id:5772266032 --> @C1nderC0ated commented on GitHub (Sep 22, 2026): crashes for me too
Author
Owner

@C1nderC0ated commented on GitHub (Sep 23, 2026):

Data point: matrix arm C (tun2socks v2.7.0 + current toolchain/deps) — ~7 h clean vs ~55 min MTBF on the bundled build

I ran @HeisLuka's arm C on one Windows machine. Single machine, so not conclusive, but here are the numbers.

Setup

  • AmneziaVPN 5.0.3.0, Windows 11 IoT Enterprise LTSC (build 26100)
  • VLESS + Reality + Vision (xtls-rprx-vision) — note this is a different transport from the original report, which is XHTTP
  • Same visible failure: Error 804, preceded by Tun2socks process was closed with 2 exit code

go version -m on the bundled binary matches arm B as described above. The main module shows as (devel); the dependency versions are exactly those in v2.6.0's go.mod:

go1.26.0
mod  github.com/xjasonlyu/tun2socks/v2  (devel)
dep  go.uber.org/zap                    v1.27.0
dep  gvisor.dev/gvisor                  v0.0.0-20250523182742-eede7a881b20

Baseline (arm B)

I had no tun2socks stderr captured, so I reconstructed process lifetimes from Microsoft-Windows-NetworkProfile/Operational events 10000/10001 (tun2 adapter up/down):

5 min | 2 h 30 min | 1 h 14 min | 33 min | 16 min     ->  MTBF ~55 min

Caveat worth stating up front: those are lifetimes, not crash signatures. I cannot confirm my 804s carried the same 0xc0000005 / sync.Pool stack as the report.

Arm C

Built current upstream v2.7.0, unmodified except for the two integration patches below:

# go1.26.8, run in the v2.7.0 module root after applying the two patches
GOOS=windows GOARCH=amd64 GOAMD64=v1 CGO_ENABLED=0 go build -buildvcs=false -trimpath \
  -ldflags "-w -s -X github.com/xjasonlyu/tun2socks/v2/internal/version.Version=v2.7.0 -X github.com/xjasonlyu/tun2socks/v2/internal/version.GitCommit=amnezia-compat.1" \
  -o tun2socks.exe .
# reproducible: sha256 0c023a29edf617cf6364543a16be8f37bdcc87d8ac312e2725bb24bd6a6cae3e

Dropped in as tun2socks.exe (stock kept as a .bak).

Result: 6 h 56 min continuous, 5,517,292 packets, 0 interface errors/discards, 0 process exits — with Discord voice (UDP, the path in crash #1) active for most of that window. Still no 804 the following day.

The caveat that matters: the toolchain and the dependency set moved together here, so this does not isolate go1.26.0 from gVisor/zap. It is arm C vs arm B, n=1 machine.

Arm A not obtained

I also tried the official upstream v2.6.0 release binary. Amnezia killed it ~10 s after start, 15/15 attempts, with no crash of its own. Flags and [STACK] output were identical to the build that works, so I never found the reason. Arm A is effectively blocked for me.

Two patches were needed before Amnezia would accept an upstream build

Relevant for anyone repeating this, and arguably worth fixing on one side or the other:

  1. Flag style. Amnezia invokes tun2socks with single-dash long flags (-device, -proxy, -loglevel, …). v2.6.0 uses the stdlib flag package, which accepts those; v2.7.0 switched to spf13/pflag, which reads -device as a cluster of shorthands and exits. I normalise argv (-name -> --name for the known long names) before pflag sees it.

  2. [STACK] readiness line. Amnezia waits for a specific shape of the [STACK] line on stderr. Upstream emits log.Infof("[STACK] %s <-> %s", k.Device, k.Proxy); Amnezia expects [STACK] <type>://<name> <-> <scheme>://<host>. Separately, and independent of this issue: that line writes the full proxy URL including credentials into the log. Redacting it upstream looks worthwhile either way.

Edit: corrected the build command (the main package is at the module root, not ./cmd/tun2socks) and the go version -m excerpt.

<!-- gh-comment-id:5786921608 --> @C1nderC0ated commented on GitHub (Sep 23, 2026): **Data point: matrix arm C (tun2socks v2.7.0 + current toolchain/deps) — ~7 h clean vs ~55 min MTBF on the bundled build** I ran @HeisLuka's arm C on one Windows machine. Single machine, so not conclusive, but here are the numbers. ### Setup - AmneziaVPN 5.0.3.0, Windows 11 IoT Enterprise LTSC (build 26100) - VLESS + Reality + **Vision** (`xtls-rprx-vision`) — note this is a different transport from the original report, which is XHTTP - Same visible failure: Error 804, preceded by `Tun2socks process was closed with 2 exit code` `go version -m` on the bundled binary matches arm B as described above. The main module shows as `(devel)`; the dependency versions are exactly those in v2.6.0's `go.mod`: ``` go1.26.0 mod github.com/xjasonlyu/tun2socks/v2 (devel) dep go.uber.org/zap v1.27.0 dep gvisor.dev/gvisor v0.0.0-20250523182742-eede7a881b20 ``` ### Baseline (arm B) I had no tun2socks stderr captured, so I reconstructed process lifetimes from `Microsoft-Windows-NetworkProfile/Operational` events 10000/10001 (tun2 adapter up/down): ``` 5 min | 2 h 30 min | 1 h 14 min | 33 min | 16 min -> MTBF ~55 min ``` Caveat worth stating up front: those are lifetimes, not crash signatures. I cannot confirm my 804s carried the same `0xc0000005` / `sync.Pool` stack as the report. ### Arm C Built current upstream v2.7.0, unmodified except for the two integration patches below: ``` # go1.26.8, run in the v2.7.0 module root after applying the two patches GOOS=windows GOARCH=amd64 GOAMD64=v1 CGO_ENABLED=0 go build -buildvcs=false -trimpath \ -ldflags "-w -s -X github.com/xjasonlyu/tun2socks/v2/internal/version.Version=v2.7.0 -X github.com/xjasonlyu/tun2socks/v2/internal/version.GitCommit=amnezia-compat.1" \ -o tun2socks.exe . # reproducible: sha256 0c023a29edf617cf6364543a16be8f37bdcc87d8ac312e2725bb24bd6a6cae3e ``` Dropped in as `tun2socks.exe` (stock kept as a `.bak`). Result: **6 h 56 min continuous, 5,517,292 packets, 0 interface errors/discards, 0 process exits** — with Discord voice (UDP, the path in crash #1) active for most of that window. Still no 804 the following day. The caveat that matters: the toolchain and the dependency set moved together here, so this does **not** isolate go1.26.0 from gVisor/zap. It is arm C vs arm B, n=1 machine. ### Arm A not obtained I also tried the official upstream **v2.6.0** release binary. Amnezia killed it ~10 s after start, 15/15 attempts, with no crash of its own. Flags and `[STACK]` output were identical to the build that works, so I never found the reason. Arm A is effectively blocked for me. ### Two patches were needed before Amnezia would accept an upstream build Relevant for anyone repeating this, and arguably worth fixing on one side or the other: 1. **Flag style.** Amnezia invokes tun2socks with single-dash long flags (`-device`, `-proxy`, `-loglevel`, …). v2.6.0 uses the stdlib `flag` package, which accepts those; v2.7.0 switched to `spf13/pflag`, which reads `-device` as a cluster of shorthands and exits. I normalise argv (`-name` -> `--name` for the known long names) before pflag sees it. 2. **`[STACK]` readiness line.** Amnezia waits for a specific shape of the `[STACK]` line on stderr. Upstream emits `log.Infof("[STACK] %s <-> %s", k.Device, k.Proxy)`; Amnezia expects `[STACK] <type>://<name> <-> <scheme>://<host>`. Separately, and independent of this issue: that line writes the **full proxy URL including credentials** into the log. Redacting it upstream looks worthwhile either way. _Edit: corrected the build command (the main package is at the module root, not `./cmd/tun2socks`) and the `go version -m` excerpt._
Sign in to join this conversation.
No labels
pull-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
amnezia-vpn/amnezia-client#1460
No description provided.