yiguodev:Xray iOS TUN 入站修复经本地验证可行,借用 fd 须先复制再包装 os.File

在 Xray-core PR #6883(hossinasaadi 提出为 iOS TUN 入站复制 NetworkExtension 持有的文件描述符,open、未合并)评审中,yiguodev 复核 commit 9929c7c 后表示该修复方向正确、未发现阻塞问题:仅跳过显式 close 并不够,因为借用来的描述符被包进 os.File 后,其 finalizer 仍可能在 GC 后关闭原描述符;在包装前先复制描述符,Xray 才有自己的句柄可关,同时保留 NetworkExtension 持有的句柄。他认为 SetNonblock 失败路径正确释放了副本、外部托管接口仍跳过系统路由配置,保留 F_DUPFD_CLOEXEC 也合理;ownsFd 命名问题不阻塞(复制后两条路径都由 Xray 拥有描述符,该标记现在控制的是系统路由管理,不建议在 supplied-fd 路径上直接置为 true)。他在 macOS 上用 socketpair 做了本地验证(不启动 VPN):复现旧包装方式下 GC 关闭原描述符;对 PR 实际的 supplied-fd NewTun 路径跑了 100 次 create/start/close/GC 循环,原描述符每轮之后仍可正常 I/O;扫描 0–1023 范围,循环前后打开 fd 数都是 6。他同时指出真实 NetworkExtension 休眠/唤醒与 reload 场景未覆盖,建议补一个借用 fd 的 close/GC/复用回归测试。

作者原文

Reviewed commit 9929c7c. The fix looks correct to me; I found no blocking issue.

Skipping an explicit close is insufficient when a borrowed descriptor is wrapped in os.File: its finalizer can still close the original descriptor after GC. Duplicating the descriptor before wrapping it gives Xray its own handle to close, while preserving the NetworkExtension-owned handle. The SetNonblock failure path correctly releases the duplicate, and externally managed interfaces still skip system route configuration. Keeping F_DUPFD_CLOEXEC is reasonable.

I validated this locally on macOS using a socketpair, without starting a VPN:

  • Reproduced GC closing the original descriptor with the old wrapping approach.
  • Exercised the PR's actual supplied-fd NewTun path through 100 create/start/close/GC cycles. The original descriptor remained usable for I/O after every cycle.
  • The observed open-fd count remained 6 before and after those cycles (scanned range: 0–1023).

The ownsFd naming concern is non-blocking. After duplication, Xray owns its descriptor in both paths, while the flag now controls system route management. Renaming it would improve clarity, but leaving it unchanged does not break the current behavior. It should not simply be set to true in the supplied-fd path.

Real NetworkExtension sleep/wake and reload scenarios were not exercised. Adding a regression test for borrowed-fd close/GC/reuse would be useful.