Skip to content

修复多处资源泄漏、错误处理缺陷与并发数据竞争#1693

Open
diaoyunxi wants to merge 1 commit into
cloudflare:masterfrom
diaoyunxi:fix/resource-leaks-error-handling-race
Open

修复多处资源泄漏、错误处理缺陷与并发数据竞争#1693
diaoyunxi wants to merge 1 commit into
cloudflare:masterfrom
diaoyunxi:fix/resource-leaks-error-handling-race

Conversation

@diaoyunxi

Copy link
Copy Markdown

背景

对 cloudflared 代码库进行系统性代码审查后, 发现多处资源泄漏、错误处理缺陷与并发数据竞争问题。本 PR 修复其中 12 处确信的真实缺陷, 均为最小化外科手术式修改, 未改变任何公共 API。

修改内容

资源泄漏

文件 问题 修复
token/encrypt.go writeKey/fetchKey 未关闭文件句柄; fetchKey 还忽略了 io.Copy 错误 显式关闭并返回关闭错误; 检查 io.Copy 错误
config/configuration.go strict 模式二次打开配置文件未关闭, 每次(重)加载泄漏一个 fd 增加 defer file.Close()
cmd/cloudflared/updater/update.go AutoUpdater.Runtime.NewTickerStop 增加 defer ticker.Stop()
features/selector.go refreshLoop 的 ticker 未 Stop 增加 defer ticker.Stop()
hello/hello.go sseHandler 的 ticker 未 Stop 增加 defer ticker.Stop()
cmd/cloudflared/updater/check.go StartWarningCheck 使用无缓冲 channel, getWarningselect/default 非阻塞读取, 若在网络请求完成前被读取, 发送方 goroutine 永久阻塞泄漏 改为 make(chan string, 1)
cmd/cloudflared/tail/cmd.go websocket.Dial 返回错误时未关闭 resp.Body (nhooyr.io/websocket 要求调用方关闭) 在错误路径 defer resp.Body.Close()

错误处理

文件 问题 修复
tunnelrpc/pogs/session_manager.go RegisterUdpSessionSetTraceContext 错误被 _ = 吞掉, 与同函数其他字段处理不一致 检查并返回错误
cmd/cloudflared/updater/workers_update.go downloadwriteBatchFile 忽略 Close 错误, 可能用不完整的二进制/批处理文件执行更新 显式关闭并返回关闭错误
cmd/cloudflared/linux_service.go copyFile 忽略 Close 错误, 刷盘失败时仍保留不完整的 service 配置 (ok=true 先于 Close 设置) Close 失败时删除目标文件并返回错误

并发安全

文件 问题 修复
quic/v3/session.go session.log (*zerolog.Logger) 在 Migrate 中无锁写入, 而 readLoop/writeLoop/Write 并发读取, 构成数据竞争 (go test -race 会报 DATA RACE)。s.eyeball 已用 atomic.Pointer 保护, 唯独 s.log 遗漏 改为 atomic.Pointer[zerolog.Logger], 用 Store/Load 访问

逻辑修复

文件 问题 修复
datagramsession/session.go dstToTransportsendFunc 失败时返回 closeSession=false, 导致会话不关闭; 当 origin 持续上报数据时会陷入"读取-发送失败-日志-重复"的快速循环, 耗费 CPU 并刷爆日志 改为 return true, sendErr 关闭会话, 与 quic/v3 session.readLoop 中发送失败即关闭的行为一致

验证

  • 所有改动为最小化外科手术式修改, 未改变公共 API。
  • 由于本地环境未安装 Go 工具链, 未能运行 make test lint; 改动均已人工逐行核对语法与并发语义, 恳请 CI 校验。

备注

审查中还发现 ingress/icmp_linux.go 存在一处 ICMP flow 的 goroutine+socket 泄漏 (listenResponse 永久阻塞在 ReadFrom, ctx 取消时不关闭 funnel)。因修复需在共享的 packet.FunnelTracker 上新增方法并处理双重关闭语义, 为控制本 PR 风险与范围暂未包含, 建议作为后续独立 PR 处理。

本次提交修复代码审查中发现的多处缺陷:

资源泄漏:
- token/encrypt.go: writeKey/fetchKey 未关闭文件句柄, 且 fetchKey 忽略 io.Copy 错误
- config/configuration.go: strict 模式二次打开配置文件未关闭, 导致 fd 泄漏
- cmd/cloudflared/updater/update.go: AutoUpdater.Run 的 ticker 未 Stop
- features/selector.go: refreshLoop 的 ticker 未 Stop
- hello/hello.go: sseHandler 的 ticker 未 Stop
- cmd/cloudflared/updater/check.go: StartWarningCheck 使用无缓冲 channel, 网络请求完成前被非阻塞读取会导致 goroutine 永久阻塞泄漏
- cmd/cloudflared/tail/cmd.go: websocket.Dial 返回错误时未关闭 resp.Body

错误处理:
- tunnelrpc/pogs/session_manager.go: RegisterUdpSession 忽略 SetTraceContext 错误, 与同函数其他字段处理不一致
- cmd/cloudflared/updater/workers_update.go: 下载二进制与生成批处理文件时忽略 Close 错误, 可能使用不完整文件执行更新
- cmd/cloudflared/linux_service.go: copyFile 忽略 Close 错误, 可能在刷盘失败时保留不完整的 service 配置

并发安全:
- quic/v3/session.go: session.log 在 Migrate 中无锁写入, 与 readLoop/writeLoop/Write 并发读取产生数据竞争, 改用 atomic.Pointer 保护

逻辑修复:
- datagramsession/session.go: sendFunc 失败时未关闭会话, 在 origin 持续上报数据时陷入只读只失败的快速循环, 改为关闭会话 (与 quic/v3 行为一致)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant