BUG/MEDIUM: http: prevent redirect from overwriting a buffer

See 4b788f7d349ddde3f70f063b7394529eac6ab678

If we use the action "http-request redirect" with a Lua sample-fetch or
converter, and the Lua function calls one of the Lua log function, the
header name is corrupted, it contains an extract of the last loggued data.

This is due to an overwrite of the trash buffer, because his scope is not
respected in the "add-header" function. The scope of the trash buffer must
be limited to the function using it. The build_logline() function can
execute a lot of other function which can use the trash buffer.

This patch fix the usage of the trash buffer. It limits the scope of this
global buffer to the local function, we build first the header value using
build_logline, and after we store the header name.

Thanks Jesse Schulman for the bug repport.

This patch must be backported in 1.7, 1.6 and 1.5 version, and it relies
on commit b686afd ("MINOR: chunks: implement a simple dynamic allocator for
trash buffers") for the trash allocator, which has to be backported as well.
diff --git a/src/proto_http.c b/src/proto_http.c
index 3490aa7..23a7dc4 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -4024,6 +4024,12 @@
 	struct http_msg *res = &txn->rsp;
 	const char *msg_fmt;
 	const char *location;
+	struct chunk *chunk;
+	int ret = 0;
+
+	chunk = alloc_trash_chunk();
+	if (!chunk)
+		goto leave;
 
 	/* build redirect message */
 	switch(rule->code) {
@@ -4045,10 +4051,10 @@
 		break;
 	}
 
-	if (unlikely(!chunk_strcpy(&trash, msg_fmt)))
-		return 0;
+	if (unlikely(!chunk_strcpy(chunk, msg_fmt)))
+		goto leave;
 
-	location = trash.str + trash.len;
+	location = chunk->str + chunk->len;
 
 	switch(rule->type) {
 	case REDIRECT_TYPE_SCHEME: {
@@ -4087,40 +4093,40 @@
 
 		if (rule->rdr_str) { /* this is an old "redirect" rule */
 			/* check if we can add scheme + "://" + host + path */
-			if (trash.len + rule->rdr_len + 3 + hostlen + pathlen > trash.size - 4)
-				return 0;
+			if (chunk->len + rule->rdr_len + 3 + hostlen + pathlen > chunk->size - 4)
+				goto leave;
 
 			/* add scheme */
-			memcpy(trash.str + trash.len, rule->rdr_str, rule->rdr_len);
-			trash.len += rule->rdr_len;
+			memcpy(chunk->str + chunk->len, rule->rdr_str, rule->rdr_len);
+			chunk->len += rule->rdr_len;
 		}
 		else {
 			/* add scheme with executing log format */
-			trash.len += build_logline(s, trash.str + trash.len, trash.size - trash.len, &rule->rdr_fmt);
+			chunk->len += build_logline(s, chunk->str + chunk->len, chunk->size - chunk->len, &rule->rdr_fmt);
 
 			/* check if we can add scheme + "://" + host + path */
-			if (trash.len + 3 + hostlen + pathlen > trash.size - 4)
-				return 0;
+			if (chunk->len + 3 + hostlen + pathlen > chunk->size - 4)
+				goto leave;
 		}
 		/* add "://" */
-		memcpy(trash.str + trash.len, "://", 3);
-		trash.len += 3;
+		memcpy(chunk->str + chunk->len, "://", 3);
+		chunk->len += 3;
 
 		/* add host */
-		memcpy(trash.str + trash.len, host, hostlen);
-		trash.len += hostlen;
+		memcpy(chunk->str + chunk->len, host, hostlen);
+		chunk->len += hostlen;
 
 		/* add path */
-		memcpy(trash.str + trash.len, path, pathlen);
-		trash.len += pathlen;
+		memcpy(chunk->str + chunk->len, path, pathlen);
+		chunk->len += pathlen;
 
 		/* append a slash at the end of the location if needed and missing */
-		if (trash.len && trash.str[trash.len - 1] != '/' &&
+		if (chunk->len && chunk->str[chunk->len - 1] != '/' &&
 		    (rule->flags & REDIRECT_FLAG_APPEND_SLASH)) {
-			if (trash.len > trash.size - 5)
-				return 0;
-			trash.str[trash.len] = '/';
-			trash.len++;
+			if (chunk->len > chunk->size - 5)
+				goto leave;
+			chunk->str[chunk->len] = '/';
+			chunk->len++;
 		}
 
 		break;
@@ -4149,38 +4155,38 @@
 		}
 
 		if (rule->rdr_str) { /* this is an old "redirect" rule */
-			if (trash.len + rule->rdr_len + pathlen > trash.size - 4)
-				return 0;
+			if (chunk->len + rule->rdr_len + pathlen > chunk->size - 4)
+				goto leave;
 
 			/* add prefix. Note that if prefix == "/", we don't want to
 			 * add anything, otherwise it makes it hard for the user to
 			 * configure a self-redirection.
 			 */
 			if (rule->rdr_len != 1 || *rule->rdr_str != '/') {
-				memcpy(trash.str + trash.len, rule->rdr_str, rule->rdr_len);
-				trash.len += rule->rdr_len;
+				memcpy(chunk->str + chunk->len, rule->rdr_str, rule->rdr_len);
+				chunk->len += rule->rdr_len;
 			}
 		}
 		else {
 			/* add prefix with executing log format */
-			trash.len += build_logline(s, trash.str + trash.len, trash.size - trash.len, &rule->rdr_fmt);
+			chunk->len += build_logline(s, chunk->str + chunk->len, chunk->size - chunk->len, &rule->rdr_fmt);
 
 			/* Check length */
-			if (trash.len + pathlen > trash.size - 4)
-				return 0;
+			if (chunk->len + pathlen > chunk->size - 4)
+				goto leave;
 		}
 
 		/* add path */
-		memcpy(trash.str + trash.len, path, pathlen);
-		trash.len += pathlen;
+		memcpy(chunk->str + chunk->len, path, pathlen);
+		chunk->len += pathlen;
 
 		/* append a slash at the end of the location if needed and missing */
-		if (trash.len && trash.str[trash.len - 1] != '/' &&
+		if (chunk->len && chunk->str[chunk->len - 1] != '/' &&
 		    (rule->flags & REDIRECT_FLAG_APPEND_SLASH)) {
-			if (trash.len > trash.size - 5)
-				return 0;
-			trash.str[trash.len] = '/';
-			trash.len++;
+			if (chunk->len > chunk->size - 5)
+				goto leave;
+			chunk->str[chunk->len] = '/';
+			chunk->len++;
 		}
 
 		break;
@@ -4188,29 +4194,29 @@
 	case REDIRECT_TYPE_LOCATION:
 	default:
 		if (rule->rdr_str) { /* this is an old "redirect" rule */
-			if (trash.len + rule->rdr_len > trash.size - 4)
-				return 0;
+			if (chunk->len + rule->rdr_len > chunk->size - 4)
+				goto leave;
 
 			/* add location */
-			memcpy(trash.str + trash.len, rule->rdr_str, rule->rdr_len);
-			trash.len += rule->rdr_len;
+			memcpy(chunk->str + chunk->len, rule->rdr_str, rule->rdr_len);
+			chunk->len += rule->rdr_len;
 		}
 		else {
 			/* add location with executing log format */
-			trash.len += build_logline(s, trash.str + trash.len, trash.size - trash.len, &rule->rdr_fmt);
+			chunk->len += build_logline(s, chunk->str + chunk->len, chunk->size - chunk->len, &rule->rdr_fmt);
 
 			/* Check left length */
-			if (trash.len > trash.size - 4)
-				return 0;
+			if (chunk->len > chunk->size - 4)
+				goto leave;
 		}
 		break;
 	}
 
 	if (rule->cookie_len) {
-		memcpy(trash.str + trash.len, "\r\nSet-Cookie: ", 14);
-		trash.len += 14;
-		memcpy(trash.str + trash.len, rule->cookie_str, rule->cookie_len);
-		trash.len += rule->cookie_len;
+		memcpy(chunk->str + chunk->len, "\r\nSet-Cookie: ", 14);
+		chunk->len += 14;
+		memcpy(chunk->str + chunk->len, rule->cookie_str, rule->cookie_len);
+		chunk->len += rule->cookie_len;
 	}
 
 	/* add end of headers and the keep-alive/close status.
@@ -4230,17 +4236,17 @@
 		/* keep-alive possible */
 		if (!(req->flags & HTTP_MSGF_VER_11)) {
 			if (unlikely(txn->flags & TX_USE_PX_CONN)) {
-				memcpy(trash.str + trash.len, "\r\nProxy-Connection: keep-alive", 30);
-				trash.len += 30;
+				memcpy(chunk->str + chunk->len, "\r\nProxy-Connection: keep-alive", 30);
+				chunk->len += 30;
 			} else {
-				memcpy(trash.str + trash.len, "\r\nConnection: keep-alive", 24);
-				trash.len += 24;
+				memcpy(chunk->str + chunk->len, "\r\nConnection: keep-alive", 24);
+				chunk->len += 24;
 			}
 		}
-		memcpy(trash.str + trash.len, "\r\n\r\n", 4);
-		trash.len += 4;
-		FLT_STRM_CB(s, flt_http_reply(s, txn->status, &trash));
-		bo_inject(res->chn, trash.str, trash.len);
+		memcpy(chunk->str + chunk->len, "\r\n\r\n", 4);
+		chunk->len += 4;
+		FLT_STRM_CB(s, flt_http_reply(s, txn->status, chunk));
+		bo_inject(res->chn, chunk->str, chunk->len);
 		/* "eat" the request */
 		bi_fast_delete(req->chn->buf, req->sov);
 		req->next -= req->sov;
@@ -4255,13 +4261,13 @@
 	} else {
 		/* keep-alive not possible */
 		if (unlikely(txn->flags & TX_USE_PX_CONN)) {
-			memcpy(trash.str + trash.len, "\r\nProxy-Connection: close\r\n\r\n", 29);
-			trash.len += 29;
+			memcpy(chunk->str + chunk->len, "\r\nProxy-Connection: close\r\n\r\n", 29);
+			chunk->len += 29;
 		} else {
-			memcpy(trash.str + trash.len, "\r\nConnection: close\r\n\r\n", 23);
-			trash.len += 23;
+			memcpy(chunk->str + chunk->len, "\r\nConnection: close\r\n\r\n", 23);
+			chunk->len += 23;
 		}
-		http_reply_and_close(s, txn->status, &trash);
+		http_reply_and_close(s, txn->status, chunk);
 		req->chn->analysers &= AN_REQ_FLT_END;
 	}
 
@@ -4270,7 +4276,10 @@
 	if (!(s->flags & SF_FINST_MASK))
 		s->flags |= SF_FINST_R;
 
-	return 1;
+	ret = 1;
+ leave:
+	free_trash_chunk(chunk);
+	return ret;
 }
 
 /* This stream analyser runs all HTTP request processing which is common to