-
Notifications
You must be signed in to change notification settings - Fork 286
Open
Description
使用过程中发现一个高并发的bug,用AI修复了一下。以下文字来自AI:
🐞 Bug Report
panic: sync: RUnlock of unlocked RWMutex —— Radius 认证高并发时进程崩溃
1. 环境信息
| 项目 | 版本 / 描述 |
|---|---|
| ToughRADIUS | v8 (9a9edd1 及之后 commit) |
| Go | 1.20.x |
| OS | CentOS 7 / Rocky 9(多台同配置,仅高并发节点复现) |
| 数据库 | PostgreSQL 15 |
2. 复现步骤
-
使用默认配置启动 ToughRADIUS(
radiusd.enabled=true,其余保持缺省)。 -
向 1812/UDP 持续发送 错误密码 的 Access-Request,触发 Reject 逻辑(脚本或真实 NAS 均可)。
-
数分钟后进程崩溃并被 systemd 重启,journalctl 输出首行:
fatal error: sync: RUnlock of unlocked RWMutex -
栈顶定位在
toughradius/radius_reject_delay.go:32,RejectItem.IsOver()。
3. 实际日志(截取)
fatal error: sync: RUnlock of unlocked RWMutex
goroutine 77 [running]:
sync.fatal(...)
sync.(*RWMutex).rUnlockSlow(...)
sync.(*RWMutex).RUnlock(...)
github.com/talkincode/toughradius/v8/toughradius.(*RejectItem).IsOver
toughradius/radius_reject_delay.go:32
github.com/talkincode/toughradius/v8/toughradius.(*RadiusService).CheckRadAuthError
toughradius/errors.go:28
...
4. 预期行为
高并发认证失败场景下,进程应稳定运行,Reject 限速逻辑正常生效,而不是触发 panic。
5. 初步根因分析
- 触发 commit:
9a9edd1 Refactor RejectCache to use a read-write mutex for concurrent access - 代码将
Mutex升级为RWMutex,但保留了旧的defer RUnlock(),又在逻辑分支里 手动RUnlock()后再次升级写锁,导致同一读锁被释放两次:
ri.Lock.RLock()
defer ri.Lock.RUnlock() // 第一次解锁
...
ri.Lock.RUnlock() // 第二次解锁 → panic
ri.Lock.Lock() // 升级写锁RejectCache.GetItem() 中同样存在「读锁 + defer + 手动 RUnlock」的重复解锁。
6. 修复建议(已验证)
func (ri *RejectItem) IsOver(max int64) bool {
- ri.Lock.RLock()
- defer ri.Lock.RUnlock()
+ ri.Lock.RLock()
if time.Since(ri.LastReject).Seconds() > 10 {
- ri.Lock.RUnlock()
+ ri.Lock.RUnlock() // 升级前显式释放
ri.Lock.Lock()
- defer ri.Lock.Unlock()
if time.Since(ri.LastReject).Seconds() > 10 {
atomic.StoreInt64(&ri.Rejects, 0)
}
+ ri.Lock.Unlock()
return false
}
- return atomic.LoadInt64(&ri.Rejects) > max
+ over := atomic.LoadInt64(&ri.Rejects) > max
+ ri.Lock.RUnlock()
+ return over
}RejectCache.GetItem() 同理,去掉 defer RUnlock(),按需手动释放并在升级写锁后再 Unlock()。
7. 影响范围
任何并发量较高、认证失败频繁的部署都会触发,导致进程循环崩溃并被 systemd/NSSM 重启。
8. 附件
- 完整 panic stack trace
- 最小复现脚本(可选提供)
完整的修改(仅供参考):falseen@def3c8d
感谢作者: 如需更多信息或验证补丁,请 @ 我 😊
Copilot
Metadata
Metadata
Assignees
Labels
No labels