On Wed, Oct 28, 2026 at 11:19:59AM +0100, Adam Szymanski wrote:
> SPOP_CF_DEM_BLOCK_ANY is documented as "aggregate of the demux flags above
> except DALLOC/DFULL", like H2_CF_DEM_BLOCK_ANY and FCGI_CF_DEM_BLOCK_ANY, but
> its value 0x7E0 also covers SPOP_CF_DEM_DALLOC (0x20) and SPOP_CF_DEM_DFULL
> (0x40).
> 
> When a single recv() fills the demux buffer, spop_recv() sets
> SPOP_CF_DEM_DFULL. spop_process() then skips spop_process_demux() because
> the flag is part of SPOP_CF_DEM_BLOCK_ANY, and the only place clearing
> SPOP_CF_DEM_DFULL is inside that skipped block. spop_recv_allowed() also
> refuses to read. The connection stays stuck with a full dbuf forever: the
> peer sees a zero TCP window, ACK frames are never parsed, every stream
> attached to the connection times out, and new streams keep being attached
> to it because it is still considered available.
> 
> This happens with pipelining when enough ACK frames are queued at once to
> fill the 16kB demux buffer in one read (e.g. ACKs carrying ~1-3kB
> variables), so "no option pipelining" in the spoe-agent section hides it.
> 
> The value must be 0x780 (MROOM|SALLOC|SFULL|TOOMANY).
> 
> Reproduced on HAProxy 3.4.7 with nbthread 4, pipelining enabled,
> 50 concurrent HTTP keep-alive clients, and a Go SPOA agent returning
> ACK frames carrying approximately 3kB of variables. In three 10-second
> test runs, the unpatched version left 3-5 connections stuck with
> DEM_DFULL and produced 400-520 SPOE timeouts per run. With the fix,
> no stuck connections or SPOE timeouts were observed.
> 
> This must be backported as far as 3.1.
> 
> Co-authored-by: ITGG <[email protected]>

Indeed, good catch. Now applied, thank you!
Willy


Reply via email to