golang/go

net/http/httptrace: attaching a ClientTrace twice to the same context causes stack overflow

Open

#32,925 opened on Jul 3, 2019

View on GitHub
 (5 comments) (1 reaction) (0 assignees)Go (19,008 forks)batch import
NeedsInvestigationhelp wanted

Repository metrics

Stars
 (133,883 stars)
PR merge metrics
 (No merged PRs in 30d)

Description

What version of Go are you using (go version)?

Does this issue reproduce with the latest release?

Yes

What did you do?


package main

import (
	"log"
	"net/http"
	"net/http/httptrace"
)

func main() {
	tracer := &httptrace.ClientTrace{
		ConnectStart: func(network, addr string) {
			log.Println("traced")
		},
	}
	req, err := http.NewRequest("GET", "http://localhost:9999/hello", nil)
	if err != nil {
		log.Printf("error creating request %v", err)
	}

	// Adding the same trace twice causes a stack overflow.
	req = req.WithContext(httptrace.WithClientTrace(req.Context(), tracer))
	req = req.WithContext(httptrace.WithClientTrace(req.Context(), tracer))

	client := &http.Client{}
	res, err := client.Do(req)
	if err != nil {
		log.Printf("request error: %v", err)
	}
	if res != nil && res.Body != nil {
		res.Body.Close()
	}
}

What did you expect to see?

"traced" gets printed twice, and then a request error because nothing is listening on localhost:9999.

What did you see instead?

The stack overflows.

What I'm doing is silly, but it happened in production for a service due to a lot of indirection on retries and attempting to use a single tracer object for all traces.

The bug seems to be here https://github.com/golang/go/blob/b412fde53a6b53475e25aaa9e49f3c6df3c48716/src/net/http/httptrace/trace.go#L202-L204

Since *t and *old are the same, the of.Call() ends up calling itself.

Contributor guide