From 5efbdd9d10908206d4e0351cb4724c5fefdfa2be Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Fri, 22 Jan 2016 13:31:57 -0800 Subject: [PATCH] net: fix race in (*resolverConfig).tryUpdate Fixes #14072. Change-Id: Ie31caa06690ac621906fc5acd34da2efa4e2049f Reviewed-on: https://go-review.googlesource.com/18860 Reviewed-by: Mikio Hara Run-TryBot: Mikio Hara --- src/net/dnsclient_unix.go | 22 ++++++---------------- src/net/dnsconfig_unix.go | 28 +++++++++++++++++++--------- src/net/dnsconfig_unix_test.go | 2 ++ 3 files changed, 27 insertions(+), 25 deletions(-) diff --git a/src/net/dnsclient_unix.go b/src/net/dnsclient_unix.go index 17188f0024..736e57322c 100644 --- a/src/net/dnsclient_unix.go +++ b/src/net/dnsclient_unix.go @@ -229,7 +229,6 @@ type resolverConfig struct { // time to recheck resolv.conf. ch chan struct{} // guards lastChecked and modTime lastChecked time.Time // last time resolv.conf was checked - modTime time.Time // time of resolv.conf modification mu sync.RWMutex // protects dnsConfig dnsConfig *dnsConfig // parsed resolv.conf structure used in lookups @@ -239,16 +238,12 @@ var resolvConf resolverConfig // init initializes conf and is only called via conf.initOnce. func (conf *resolverConfig) init() { - // Set dnsConfig, modTime, and lastChecked so we don't parse + // Set dnsConfig and lastChecked so we don't parse // resolv.conf twice the first time. conf.dnsConfig = systemConf().resolv if conf.dnsConfig == nil { conf.dnsConfig = dnsReadConfig("/etc/resolv.conf") } - - if fi, err := os.Stat("/etc/resolv.conf"); err == nil { - conf.modTime = fi.ModTime() - } conf.lastChecked = time.Now() // Prepare ch so that only one update of resolverConfig may @@ -274,17 +269,12 @@ func (conf *resolverConfig) tryUpdate(name string) { } conf.lastChecked = now + var mtime time.Time if fi, err := os.Stat(name); err == nil { - if fi.ModTime().Equal(conf.modTime) { - return - } - conf.modTime = fi.ModTime() - } else { - // If modTime wasn't set prior, assume nothing has changed. - if conf.modTime.IsZero() { - return - } - conf.modTime = time.Time{} + mtime = fi.ModTime() + } + if mtime.Equal(conf.dnsConfig.mtime) { + return } dnsConf := dnsReadConfig(name) diff --git a/src/net/dnsconfig_unix.go b/src/net/dnsconfig_unix.go index 6073fdb6d8..0515ca90de 100644 --- a/src/net/dnsconfig_unix.go +++ b/src/net/dnsconfig_unix.go @@ -8,18 +8,21 @@ package net +import "time" + var defaultNS = []string{"127.0.0.1", "::1"} type dnsConfig struct { - servers []string // servers to use - search []string // suffixes to append to local name - ndots int // number of dots in name to trigger absolute lookup - timeout int // seconds before giving up on packet - attempts int // lost packets before giving up on server - rotate bool // round robin among servers - unknownOpt bool // anything unknown was encountered - lookup []string // OpenBSD top-level database "lookup" order - err error // any error that occurs during open of resolv.conf + servers []string // servers to use + search []string // suffixes to append to local name + ndots int // number of dots in name to trigger absolute lookup + timeout int // seconds before giving up on packet + attempts int // lost packets before giving up on server + rotate bool // round robin among servers + unknownOpt bool // anything unknown was encountered + lookup []string // OpenBSD top-level database "lookup" order + err error // any error that occurs during open of resolv.conf + mtime time.Time // time of resolv.conf modification } // See resolv.conf(5) on a Linux machine. @@ -38,6 +41,13 @@ func dnsReadConfig(filename string) *dnsConfig { return conf } defer file.close() + if fi, err := file.file.Stat(); err == nil { + conf.mtime = fi.ModTime() + } else { + conf.servers = defaultNS + conf.err = err + return conf + } for line, ok := file.readLine(); ok; line, ok = file.readLine() { if len(line) > 0 && (line[0] == ';' || line[0] == '#') { // comment. diff --git a/src/net/dnsconfig_unix_test.go b/src/net/dnsconfig_unix_test.go index c8eed61890..849c0da93b 100644 --- a/src/net/dnsconfig_unix_test.go +++ b/src/net/dnsconfig_unix_test.go @@ -10,6 +10,7 @@ import ( "os" "reflect" "testing" + "time" ) var dnsReadConfigTests = []struct { @@ -76,6 +77,7 @@ func TestDNSReadConfig(t *testing.T) { if conf.err != nil { t.Fatal(conf.err) } + conf.mtime = time.Time{} if !reflect.DeepEqual(conf, tt.want) { t.Errorf("%s:\ngot: %+v\nwant: %+v", tt.name, conf, tt.want) }