Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions src/net/dnsclient_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,12 @@ type resolverConfig struct {
dnsConfig atomic.Pointer[dnsConfig] // parsed resolv.conf structure used in lookups
}

var resolvConf resolverConfig
// ch is created here rather than in init: a channel created inside a synctest
// bubble stays associated with it, and operating on such a channel from
// outside the bubble is a fatal error.
var resolvConf = resolverConfig{
ch: make(chan struct{}, 1),
}

func getSystemDNSConfig() *dnsConfig {
return getSystemDNSConfigNamed("/etc/resolv.conf")
Expand All @@ -380,10 +385,6 @@ func (conf *resolverConfig) init() {
// resolv.conf twice the first time.
conf.dnsConfig.Store(dnsReadConfig("/etc/resolv.conf"))
conf.lastChecked = time.Now()

// Prepare ch so that only one update of resolverConfig may
// run at once.
conf.ch = make(chan struct{}, 1)
}

// distantFuture is a sentinel time used for tests to signal that
Expand Down
27 changes: 27 additions & 0 deletions src/net/dnsclient_unix_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"context"
"errors"
"fmt"
"internal/testenv"
"maps"
"os"
"path"
Expand All @@ -21,6 +22,7 @@ import (
"sync"
"sync/atomic"
"testing"
"testing/synctest"
"time"

"golang.org/x/net/dns/dnsmessage"
Expand Down Expand Up @@ -2881,3 +2883,28 @@ func TestEmptyResolvConfReplacedWithConfHaingNameservers(t *testing.T) {
t.Fatal("resolv.conf was not re-loaded")
}
}

// Run in a subprocess: a bubble violation is fatal, not a recoverable panic.
func TestResolverConfigSemaphoresNotBubbled(t *testing.T) {
if os.Getenv("GO_NET_TEST_BUBBLE_HELPER") == "1" {
synctest.Test(t, func(t *testing.T) {
getSystemDNSConfig()
getSystemNSS()
})
// getSystemDNSConfig would skip the send on "options no-reload".
if resolvConf.tryAcquireSema() {
resolvConf.releaseSema()
}
if nssConfig.tryAcquireSema() {
nssConfig.releaseSema()
}
return
}
testenv.MustHaveExec(t)
cmd := testenv.Command(t, testenv.Executable(t), "-test.run=^"+t.Name()+"$", "-test.count=1")
cmd = testenv.CleanCmdEnv(cmd)
cmd.Env = append(cmd.Env, "GO_NET_TEST_BUBBLE_HELPER=1")
if out, err := cmd.CombinedOutput(); err != nil {
t.Errorf("helper failed: %v\n%s", err, out)
}
}
6 changes: 4 additions & 2 deletions src/net/nss.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ const (
nssConfigPath = "/etc/nsswitch.conf"
)

var nssConfig nsswitchConfig
// ch is created here rather than in init; see resolvConf.
var nssConfig = nsswitchConfig{
ch: make(chan struct{}, 1),
}

type nsswitchConfig struct {
initOnce sync.Once // guards init of nsswitchConfig
Expand All @@ -42,7 +45,6 @@ func getSystemNSS() *nssConf {
func (conf *nsswitchConfig) init() {
conf.nssConf = parseNSSConfFile("/etc/nsswitch.conf")
conf.lastChecked = time.Now()
conf.ch = make(chan struct{}, 1)
}

// tryUpdate tries to update conf.
Expand Down