What version of Go are you using (go version)?
$ go version
go version go1.15.3 windows/amd64
Does this issue reproduce with the latest release?
yes
What operating system and processor architecture are you using (go env)?
go env Output$ go env
windows 10 1909, amd64
What did you do?
check tls.Conn is destroyed(object unreachable)
What did you expect to see?
destroyed(object unreachable)
What did you see instead?
not destroyed(object reachable)
test code
// destroyed is used to check object is destroyed.
func destroyed(object interface{}) bool {
destroyed := make(chan struct{})
runtime.SetFinalizer(object, func(interface{}) {
close(destroyed)
})
// total 3 seconds
timer := time.NewTimer(10 * time.Millisecond)
defer timer.Stop()
for i := 0; i < 300; i++ {
timer.Reset(10 * time.Millisecond)
runtime.GC()
select {
case <-destroyed:
return true
case <-timer.C:
}
}
return false
}
func TestTLSConn(t *testing.T) {
gm := testsuite.MarkGoroutines(t)
defer gm.Compare()
serverCfg, clientCfg := testsuite.TLSConfigPair(t, "127.0.0.1")
const network = "tcp"
listener, err := tls.Listen(network, "127.0.0.1:0", serverCfg)
require.NoError(t, err)
address := listener.Addr().String()
go func() {
conn, err := listener.Accept()
require.NoError(t, err)
_, err = conn.Write([]byte("data"))
require.NoError(t, err)
time.Sleep(time.Second)
err = conn.Close()
require.NoError(t, err)
}()
conn, err := tls.Dial(network, address, clientCfg)
require.NoError(t, err)
buf := make([]byte, 4)
_, err = io.ReadFull(conn, buf)
require.NoError(t, err)
err = conn.Close()
require.NoError(t, err)
require.True(t, destroyed(conn)) // here
}
tls.Conn is not destroyed
// crypto/tls/conn.go:
// A Conn represents a secured connection.
// It implements the net.Conn interface.
type Conn struct {
// constant
conn net.Conn
isClient bool
handshakeFn func() error // (*Conn).clientHandshake or serverHandshake
// handshakeFn will reference self
// ...
}
// crypto/tls/tls.go:
// Server returns a new TLS server side connection
// using conn as the underlying transport.
// The configuration config must be non-nil and must include
// at least one certificate or else set GetCertificate.
func Server(conn net.Conn, config *Config) *Conn {
c := &Conn{
conn: conn,
config: config,
}
c.handshakeFn = c.serverHandshake // reference self
return c
}
// Client returns a new TLS client side connection
// using conn as the underlying transport.
// The config cannot be nil: users must set either ServerName or
// InsecureSkipVerify in the config.
func Client(conn net.Conn, config *Config) *Conn {
c := &Conn{
conn: conn,
config: config,
isClient: true,
}
c.handshakeFn = c.clientHandshake // reference self
return c
}
fix it that don't use handshakeFn.
// crypto/tls/conn.go:
func (c *Conn) Handshake() error {
c.handshakeMutex.Lock()
defer c.handshakeMutex.Unlock()
if err := c.handshakeErr; err != nil {
return err
}
if c.handshakeComplete() {
return nil
}
c.in.Lock()
defer c.in.Unlock()
// use it
if c.isClient {
c.handshakeErr = c.clientHandshake()
} else {
c.handshakeErr = c.serverHandshake()
}
// c.handshakeErr = c.handshakeFn()
if c.handshakeErr == nil {
c.handshakes++
} else {
// If an error occurred during the handshake try to flush the
// alert that might be left in the buffer.
c.flush()
}
if c.handshakeErr == nil && !c.handshakeComplete() {
c.handshakeErr = errors.New("tls: internal error: handshake should have had a result")
}
return c.handshakeErr
}
fix it and TestTLSConn is passed.
What version of Go are you using (
go version)?Does this issue reproduce with the latest release?
yes
What operating system and processor architecture are you using (
go env)?go envOutputWhat did you do?
check tls.Conn is destroyed(object unreachable)
What did you expect to see?
destroyed(object unreachable)
What did you see instead?
not destroyed(object reachable)
test code
tls.Conn is not destroyed
fix it that don't use handshakeFn.
fix it and TestTLSConn is passed.