Skip to content

fix(file): protect Zone.Expired with mutex#7940

Merged
yongtang merged 1 commit into
coredns:masterfrom
thevilledev:fix/file-zone-race
Mar 16, 2026
Merged

fix(file): protect Zone.Expired with mutex#7940
yongtang merged 1 commit into
coredns:masterfrom
thevilledev:fix/file-zone-race

Conversation

@thevilledev

@thevilledev thevilledev commented Mar 16, 2026

Copy link
Copy Markdown
Collaborator

1. Why is this pull request needed and what does it do?

Copy() and CopyWithoutApex() read z.Expired without holding the mutex while TransferIn() writes it under Lock, causing a data race during concurrent zone transfers.

Add RLock in both copy functions and wrap the bare write in Update() with Lock/Unlock.

2. Which issues (if any) are related?

See this for a recent data race that was caused by this issue in CI. For posterity:

Details ================== WARNING: DATA RACE Write at 0x00c000d6e4e0 by goroutine 126027: github.com/coredns/coredns/plugin/file.(*Zone).TransferIn() /home/runner/work/coredns/coredns/plugin/file/secondary.go:59 +0x651 github.com/coredns/coredns/plugin/secondary.setup.func2.1.1() /home/runner/work/coredns/coredns/plugin/secondary/setup.go:51 +0x9c Previous read at 0x00c000d6e4e0 by goroutine 126032: github.com/coredns/coredns/plugin/file.(*Zone).CopyWithoutApex() /home/runner/work/coredns/coredns/plugin/file/zone.go:69 +0x1e4 github.com/coredns/coredns/plugin/file.(*Zone).TransferIn() /home/runner/work/coredns/coredns/plugin/file/secondary.go:20 +0xbe github.com/coredns/coredns/plugin/file.File.ServeDNS() /home/runner/work/coredns/coredns/plugin/file/file.go:73 +0x90d github.com/coredns/coredns/plugin/secondary.(*Secondary).ServeDNS() :1 +0x10a github.com/coredns/coredns/core/dnsserver.(*Server).ServeDNS() /home/runner/work/coredns/coredns/core/dnsserver/server.go:315 +0x1399 github.com/coredns/coredns/core/dnsserver.(*Server).ServePacket.func1() /home/runner/work/coredns/coredns/core/dnsserver/server.go:178 +0x9c github.com/miekg/dns.HandlerFunc.ServeDNS() /home/runner/go/pkg/mod/github.com/miekg/dns@v1.1.72/server.go:37 +0x47 github.com/miekg/dns.(*Server).serveDNS() /home/runner/go/pkg/mod/github.com/miekg/dns@v1.1.72/server.go:683 +0x826 github.com/miekg/dns.(*Server).serveUDPPacket() /home/runner/go/pkg/mod/github.com/miekg/dns@v1.1.72/server.go:624 +0x37d github.com/miekg/dns.(*Server).serveUDP.gowrap2() /home/runner/go/pkg/mod/github.com/miekg/dns@v1.1.72/server.go:554 +0xd8 Goroutine 126027 (running) created at: github.com/coredns/coredns/plugin/secondary.setup.func2.1() /home/runner/work/coredns/coredns/plugin/secondary/setup.go:47 +0x168 sync.(*Once).doSlow() /opt/hostedtoolcache/go/1.26.1/x64/src/sync/once.go:78 +0xd1 sync.(*Once).Do() /opt/hostedtoolcache/go/1.26.1/x64/src/sync/once.go:69 +0x44 github.com/coredns/coredns/plugin/secondary.setup.func2() /home/runner/work/coredns/coredns/plugin/secondary/setup.go:46 +0xe5 github.com/coredns/caddy.startWithListenerFds() /home/runner/go/pkg/mod/github.com/coredns/caddy@v1.1.4-0.20250930002214-15135a999495/caddy.go:538 +0x52f github.com/coredns/caddy.Start() /home/runner/go/pkg/mod/github.com/coredns/caddy@v1.1.4-0.20250930002214-15135a999495/caddy.go:474 +0x1b4 github.com/coredns/coredns/test.CoreDNSServer() /home/runner/work/coredns/coredns/test/server.go:21 +0x164 github.com/coredns/coredns/test.CoreDNSServerAndPorts() /home/runner/work/coredns/coredns/test/server.go:49 +0x34 github.com/coredns/coredns/test.TestSecondaryZoneNotify() /home/runner/work/coredns/coredns/test/secondary_test.go:277 +0x364 testing.tRunner() /opt/hostedtoolcache/go/1.26.1/x64/src/testing/testing.go:2036 +0x21c testing.(*T).Run.gowrap1() /opt/hostedtoolcache/go/1.26.1/x64/src/testing/testing.go:2101 +0x38 Goroutine 126032 (running) created at: github.com/miekg/dns.(*Server).serveUDP() /home/runner/go/pkg/mod/github.com/miekg/dns@v1.1.72/server.go:554 +0x7a9 github.com/miekg/dns.(*Server).ActivateAndServe() /home/runner/go/pkg/mod/github.com/miekg/dns@v1.1.72/server.go:390 +0x6d9 github.com/coredns/coredns/core/dnsserver.(*Server).ServePacket() /home/runner/work/coredns/coredns/core/dnsserver/server.go:182 +0x2a4 github.com/coredns/caddy.startServers.func1.2() /home/runner/go/pkg/mod/github.com/coredns/caddy@v1.1.4-0.20250930002214-15135a999495/caddy.go:816 +0xfe ================== --- FAIL: TestSecondaryZoneNotify (0.01s) testing.go:1712: race detected during execution of test

I think this is a latent bug that was surfaced by #7901 (March 2026), where two Go routines can call TransferIn function concurrently on the same zone. There's PRs #3024, #3056 and #3079 which tried to address the file locking issues earlier (in 2019). This latest change just introduced a new path that triggers the condition easily.

3. Which documentation changes (if any) need to be made?

None.

4. Does this introduce a backward incompatible change or deprecation?

No.

Copy() and CopyWithoutApex() read z.Expired without holding the
mutex while TransferIn() writes it under Lock, causing a data
race during concurrent zone transfers. Add RLock in both copy
functions and wrap the bare write in Update() with Lock/Unlock.

Signed-off-by: Ville Vesilehto <ville@vesilehto.fi>
@thevilledev thevilledev marked this pull request as ready for review March 16, 2026 17:27
@yongtang yongtang merged commit 5a63eb6 into coredns:master Mar 16, 2026
11 checks passed
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.

2 participants