Skip to content

net/http: h2_bundle.go (fatal error: concurrent map iteration and map write) #74458

Open
@java-ml

Description

@java-ml

Go version

go version go1.23.5 darwin/arm64

Output of go env in your module/workspace:

GO111MODULE='on'
GOARCH='arm64'
GOBIN=''
GOCACHE='/Users/yzy/Library/Caches/go-build'
GOENV='/Users/yzy/Library/Application Support/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFLAGS=''
GOHOSTARCH='arm64'
GOHOSTOS='darwin'
GOINSECURE=''
GOMODCACHE='/Users/yzy/go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='darwin'
GOPATH='/Users/yzy/go'
GOPRIVATE=''
GOPROXY='https://goproxy.cn,direct'
GOROOT='/usr/local/go'
GOSUMDB='sum.golang.org'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/usr/local/go/pkg/tool/darwin_arm64'
GOVCS=''
GOVERSION='go1.23.5'
GODEBUG=''
GOTELEMETRY='local'
GOTELEMETRYDIR='/Users/yzy/Library/Application Support/go/telemetry'
GCCGO='gccgo'
GOARM64='v8.0'
AR='ar'
CC='clang'
CXX='clang++'
CGO_ENABLED='1'
GOMOD='/Users/yzy/GolandProjects/ai_services/go.mod'
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 -arch arm64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -ffile-prefix-map=/var/folders/2f/v169q89x0h9cg8lknlk8s3xr0000gn/T/go-build1486772356=/tmp/go-build -gno-record-gcc-switches -fno-common'

What did you do?

The service uses the GIN framework, and when concurrent GET requests for static resources are made from the same IP address, the following occurs:

fatal error: concurrent map iteration and map write

goroutine 102500 [running]:
net/http.http2cloneHeader(...)
/usr/local/go/src/net/http/h2_bundle.go:6817
net/http.(*http2responseWriterState).writeHeader(0xc0000f2e00, 0xc000013e11?)
/usr/local/go/src/net/http/h2_bundle.go:6811 +0xd8
net/http.(*http2responseWriter).WriteHeader(0x8ea88278fa88a?, 0x10425e0?)
/usr/local/go/src/net/http/h2_bundle.go:6775 +0x1b
github.com/gin-gonic/gin.(*responseWriter).WriteHeaderNow(...)
/Users/yzy/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/response_writer.go:77
github.com/gin-gonic/gin.(*responseWriter).Write(0xc0001c0100, {0xc00023e612, 0xa, 0xa})
/Users/yzy/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/response_writer.go:82 +0x4d
compress/gzip.(*Writer).Write(0xc00023e5a0, {0xfe18f0, 0x12, 0x12})
/usr/local/go/src/compress/gzip/gzip.go:168 +0x13b
github.com/gin-contrib/gzip.(*gzipWriter).Write(0xc0005c05e8, {0xfe18f0, 0x12, 0x12})
/Users/yzy/go/pkg/mod/github.com/gin-contrib/gzip@v1.2.3/gzip.go:37 +0x85
github.com/gin-gonic/gin.serveError(0xc00009f200, 0x194, {0xfe18f0, 0x12, 0x12})
/Users/yzy/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/gin.go:683 +0x124
github.com/gin-gonic/gin.(*Engine).handleHTTPRequest(0xc000025520, 0xc00009f200)
/Users/yzy/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/gin.go:670 +0x4c5
github.com/gin-gonic/gin.(*Engine).ServeHTTP(0xc000025520, {0xbedd08, 0xc0004e55e8}, 0xc000299040)
/Users/yzy/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/gin.go:589 +0x1b2
net/http.serverHandler.ServeHTTP({0x2c?}, {0xbedd08?, 0xc0004e55e8?}, 0xc0002f9000?)
/usr/local/go/src/net/http/server.go:3210 +0x8e
net/http.initALPNRequest.ServeHTTP({{0xbeeb70?, 0xc00048a060?}, 0xc000004a88?, {0xc000156000?}}, {0xbedd08, 0xc0004e55e8}, 0xc000299040)
/usr/local/go/src/net/http/server.go:3819 +0x231
net/http.(*http2serverConn).runHandler(0x44323b?, 0x0?, 0x0?, 0x0?)
/usr/local/go/src/net/http/h2_bundle.go:6249 +0xf5
created by net/http.(*http2serverConn).scheduleHandler in goroutine 102371
/usr/local/go/src/net/http/h2_bundle.go:6183 +0x21d

goroutine 1 [IO wait, 1 minutes]:
internal/poll.runtime_pollWait(0x7f0bb66da680, 0x72)
/usr/local/go/src/runtime/netpoll.go:351 +0x85
internal/poll.(*pollDesc).wait(0xc0000f2680?, 0x900000036?, 0x0)
/usr/local/go/src/internal/poll/fd_poll_runtime.go:84 +0x27
internal/poll.(*pollDesc).waitRead(...)
/usr/local/go/src/internal/poll/fd_poll_runtime.go:89
internal/poll.(*FD).Accept(0xc0000f2680)
/usr/local/go/src/internal/poll/fd_unix.go:620 +0x295
net.(*netFD).accept(0xc0000f2680)
/usr/local/go/src/net/fd_unix.go:172 +0x29
net.(*TCPListener).accept(0xc000093900)
/usr/local/go/src/net/tcpsock_posix.go:159 +0x1e
net.(*TCPListener).Accept(0xc000093900)
/usr/local/go/src/net/tcpsock.go:372 +0x30

What did you see happen?

It seems that the http2cloneHeader method is iterating over parameters without acquiring a read lock, leading to conflicts in a multi-concurrent environment.

if len(rws.handlerHeader) > 0 {

		rws.snapHeader = http2cloneHeader(rws.handlerHeader)
}


func http2cloneHeader(h Header) Header {
	h2 := make(Header, len(h))
	for k, vv := range h {
		vv2 := make([]string, len(vv))
		copy(vv2, vv)
		h2[k] = vv2
	}
	return h2
}

What did you expect to see?

Can we improve the occurrence of this small probability problem through read-write locks?

Metadata

Metadata

Assignees

No one assigned

    Labels

    BugReportIssues describing a possible bug in the Go implementation.WaitingForInfoIssue is not actionable because of missing required information, which needs to be provided.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions