From 72a8723e13b9574664f4c78e588069fa4aca6fc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Sat, 20 Jun 2026 20:36:19 +0800 Subject: [PATCH] Fix DNS query loopback deadlock from shared dedup lock --- dns/client.go | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/dns/client.go b/dns/client.go index 89b6170c..5bb94924 100644 --- a/dns/client.go +++ b/dns/client.go @@ -41,9 +41,9 @@ type Client struct { initRDRCFunc func() adapter.RDRCStore logger logger.ContextLogger cache freelru.Cache[dns.Question, *dns.Msg] - cacheLock compatible.Map[dns.Question, chan struct{}] + cacheLock compatible.Map[transportCacheKey, chan struct{}] transportCache freelru.Cache[transportCacheKey, *dns.Msg] - transportCacheLock compatible.Map[dns.Question, chan struct{}] + transportCacheLock compatible.Map[transportCacheKey, chan struct{}] } type ClientOptions struct { @@ -138,8 +138,9 @@ func (c *Client) Exchange(ctx context.Context, transport adapter.DNSTransport, m !options.ClientSubnet.IsValid() disableCache := !isSimpleRequest || c.disableCache || options.DisableCache if !disableCache { + cacheKey := transportCacheKey{Question: question, transportTag: transport.Tag()} if c.cache != nil { - cond, loaded := c.cacheLock.LoadOrStore(question, make(chan struct{})) + cond, loaded := c.cacheLock.LoadOrStore(cacheKey, make(chan struct{})) if loaded { select { case <-cond: @@ -148,12 +149,12 @@ func (c *Client) Exchange(ctx context.Context, transport adapter.DNSTransport, m } } else { defer func() { - c.cacheLock.Delete(question) + c.cacheLock.Delete(cacheKey) close(cond) }() } } else if c.transportCache != nil { - cond, loaded := c.transportCacheLock.LoadOrStore(question, make(chan struct{})) + cond, loaded := c.transportCacheLock.LoadOrStore(cacheKey, make(chan struct{})) if loaded { select { case <-cond: @@ -162,7 +163,7 @@ func (c *Client) Exchange(ctx context.Context, transport adapter.DNSTransport, m } } else { defer func() { - c.transportCacheLock.Delete(question) + c.transportCacheLock.Delete(cacheKey) close(cond) }() }