From: Willy Tarreau Date: Thu, 24 Apr 2014 15:02:57 +0000 (+0200) Subject: BUG/MEDIUM: channel: bi_putblk() must not wrap before the end of buffer X-Git-Tag: v1.5-dev24~29 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=285ff0f25a105f6714ae5dbc334c86e308d4bd46;p=thirdparty%2Fhaproxy.git BUG/MEDIUM: channel: bi_putblk() must not wrap before the end of buffer The errors reported by Igor Chan on the stats interface in chunked mode were caused by data wrapping at the wrong place in the buffer. It could be reliably reproduced by picking random buffer sizes until the issue appeared (for a given conf, 5300 with 1024 maxrewrite was OK). The issue is that the stats interface uses bi_putchk() to emit data, which relies on bi_putblk(). This code checks the largest part that can be emitted while preserving the rewrite reserve, but uses that result to compute the wrapping offset, which is wrong. If some data remain present in the buffer, the wrapping may be allowed and will happen before the end of the buffer, leaving some old data in the buffer. The reason it did not happen before keep-alive is simply that the buffer was much less likely to contain older data. It also used to happen only for certain configs after a certain amount of time because the size of the counters used to inflate the output till the point wrapping started to happen. The fix is trivial, buffer_contig_space_with_res() simply needs to be replaced by buffer_contig_space(). Note that peers were using the same function so it is possible that they were affected as well. This issue was introduced in 1.5-dev8. No backport to stable is needed. --- diff --git a/src/channel.c b/src/channel.c index 1250370767..2f983960db 100644 --- a/src/channel.c +++ b/src/channel.c @@ -174,7 +174,7 @@ int bi_putblk(struct channel *chn, const char *blk, int len) return 0; /* OK so the data fits in the buffer in one or two blocks */ - max = buffer_contig_space_with_res(chn->buf, chn->buf->size - max); + max = buffer_contig_space(chn->buf); memcpy(bi_end(chn->buf), blk, MIN(len, max)); if (len > max) memcpy(chn->buf->data, blk + max, len - max);