Repository: go
Issue: net/http: tls handshake panic with custom dialer
Labels: NeedsInvestigation

<!--
Please answer these questions before submitting your issue. Thanks!
-->

### What version of Go are you using (`go version`)?

<pre>
go version go1.20 linux/amd64

</pre>

### Does this issue reproduce with the latest release?
yes, 1.20.0


### What operating system and processor architecture are you using (`go env`)?

<details><summary><code>go env</code> Output</summary><br><pre>
GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/aathan/.cache/go-build"
GOENV="/home/aathan/.config/go/env"
GOEXE=""
GOEXPERIMENT=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOINSECURE=""
GOMODCACHE="/home/aathan/go/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
GOPATH="/home/aathan/go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/usr/local/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64"
GOVCS=""
GOVERSION="go1.20"
GCCGO="gccgo"
GOAMD64="v1"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD="/dev/null"
GOWORK=""
CGO_CFLAGS="-O2 -g"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-O2 -g"
CGO_FFLAGS="-O2 -g"
CGO_LDFLAGS="-O2 -g"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build4181227200=/tmp/go-build -gno-record-gcc-switches"

</pre></details>

### What did you do?

```
var internalTransport *http.Transport = &http.Transport{
  DialContext: func(ctx context.Context, network, addr string) (ret net.Conn, err error) {
   return transportDialFunc(ctx, network, addr, myStuff)
  },
  TLSHandshakeTimeout: TLS_HANDSHAKE_TIMEOUT,
}
```


### What did you expect to see?

No panics from inside golang stdlib.


### What did you see instead?

```
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x18 pc=0x5d073c]
goroutine 4133611 [running]:
crypto/tls.(*Conn).handshakeContext.func2()
  /usr/local/go/src/crypto/tls/conn.go:1441 +0xbc
created by crypto/tls.(*Conn).handshakeContext
  /usr/local/go/src/crypto/tls/conn.go:1437 +0x205
Main process exited, code=exited, status=2/INVALIDARGUMENT
Failed with result 'exit-code'.
```

### Proposed fix:

In crypto/tls/conn.go around line 1441:

```
go func() {
select {
case <-handshakeCtx.Done():
// Close the connection, discarding the error
_ = c.conn.Close()
interruptRes <- handshakeCtx.Err()
case <-done:
interruptRes <- nil
}
}()
```

I believe the problem may be that this should read:

```
conn := c.conn
if conn!= nil {
  conn.Close()
}
```

I have not deeply understood the net.Conn or tls dialer internals, but, I believe the issue here stems from the possibility that the tls handshake timer may expire before the dialer is actually finished dialing. Whether this implies that there should also be a deeper/upstream fix is beyond me.

I've prepared all of my credentials so that I can submit a related PR to googlesource.com & gerrit etc. once I get some feedback.

I would appreciate any tips on how to work around this issue in the meantime. I'm not sure how to replicate or override a core golang package to patch in this fix??