Skip to content

h2: executeHandler reads a stream's OutboundBuffer without its lock while the event loop resets it on a WINDOW_UPDATE (data race) #822

Description

@FumingPower3925

Processor.executeHandler (protocol/h2/stream/processor.go, about line 787 on main a73afb6) reads a stream's OutboundBuffer without the stream's lock after the handler has returned:

if state == StateOpen || state == StateHalfClosedRemote {
	if stream.OutboundBuffer != nil && stream.OutboundBuffer.Len() > 0 {
		return
	}

It runs on a shared worker pool goroutine (an async route). A response larger than the client's flow-control window leaves the rest of its DATA in OutboundBuffer, and the connection's event loop, handling the client's WINDOW_UPDATE, sends it and resets the buffer under s.mu (flushStreamOutbound, and the SETTINGS path in ProcessFrame). Every other access of the buffer takes s.mu, including the deferred check a few lines below this one. The race detector caught it in #805's TestLargeResponseIsDeliveredH2/adaptive/async-route/16777216 (16 MiB to net/http's h2c client; linux/arm64 Docker, 4 CPUs, 8 MiB memlock, -race): write by flushStreamOutbound → bytes.(*Buffer).Reset on the epoll loop, previous read by executeHandler → bytes.(*Buffer).Len on the pool goroutine. Log: evidence/lanes-20260927/WRITE/761/suites/m8-engine_epoll_engine_iouring_internal_conn-basea73afb6-head98ba46d.log (section head, "WARNING: DATA RACE"). It is timing-dependent: the loop has to reset the buffer between the handler's return and the pool goroutine's deferred, locked, check.

Fix: read it under stream.mu.RLock, as the deferred check does. Fixed in #805, whose test finds it.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/protocolProtocol parsing / detectionbugSomething isn't workingprotocol/h2HTTP/2 protocol

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions