BUG/MEDIUM: channel: don't schedule data in transit for leaving until connected

Option http-send-name-header is still hurting. If a POST request has to be
redispatched when this option is used, and the next server's name is larger
than the initial one, and the POST body fills the buffer, it becomes
impossible to rewrite the server's name in the buffer when redispatching.
In 1.4, this is worse, the process may crash because of a negative size
computation for the memmove().

The only solution to fix this is to refrain from eating the reserve before
we're certain that we won't modify the buffer anymore. And the condition for
that is that the connection is established.

This patch introduces "channel_may_send()" which helps to detect whether it's
safe to eat the reserve or not. This condition is used by channel_in_transit()
introduced by recent patches.

This patch series must be backported into 1.5, and a simpler version must be
backported into 1.4 where fixing the bug is much easier since there were no
channels by then. Note that in 1.4 the severity is major.
This commit is contained in:
Willy Tarreau 2015-01-14 16:08:45 +01:00
parent 27bb0e14a8
commit 9c06ee4ccf

View File

@ -117,15 +117,29 @@ static inline int channel_reserved(const struct channel *chn)
return rem >= 0;
}
/* Tells whether data are likely to leave the buffer. This is used to know when
* we can safely ignore the reserve since we know we cannot retry a connection.
* It returns zero if data are blocked, non-zero otherwise.
*/
static inline int channel_may_send(const struct channel *chn)
{
return chn->cons->state == SI_ST_EST;
}
/* Returns the amount of bytes from the channel that are already scheduled for
* leaving (buf->o) or that are still part of the input and expected to be sent
* soon as covered by to_forward. This is useful to know by how much we can
* shrink the rewrite reserve during forwards.
* shrink the rewrite reserve during forwards. Buffer data are not considered
* in transit until the channel is connected, so that the reserve remains
* protected.
*/
static inline int channel_in_transit(const struct channel *chn)
{
int ret;
if (!channel_may_send(chn))
return 0;
/* below, this is min(i, to_forward) optimized for the fast case */
if (chn->to_forward >= chn->buf->i ||
(CHN_INFINITE_FORWARD < MAX_RANGE(typeof(chn->buf->i)) &&