Blame SOURCES/varnish-6.0.6-CVE-2021-36740.patch

77b66a
From 9be22198e258d0e7a5c41f4291792214a29405cf Mon Sep 17 00:00:00 2001
77b66a
From: Martin Blix Grydeland <martin@varnish-software.com>
77b66a
Date: Tue, 22 Jun 2021 11:47:55 +0200
77b66a
Subject: [PATCH] Take content length into account on H/2 request bodies
77b66a
77b66a
When receiving H/2 data frames, make sure to take the advertised content
77b66a
length into account, and fail appropriately if the combined sum of the
77b66a
data frames does not match the content length.
77b66a
---
77b66a
 bin/varnishd/http2/cache_http2.h       |  2 ++
77b66a
 bin/varnishd/http2/cache_http2_proto.c | 49 ++++++++++++++++++++------
77b66a
 2 files changed, 40 insertions(+), 11 deletions(-)
77b66a
77b66a
diff --git a/bin/varnishd/http2/cache_http2.h b/bin/varnishd/http2/cache_http2.h
77b66a
index c377d03aac..205b96ccb7 100644
77b66a
--- a/bin/varnishd/http2/cache_http2.h
77b66a
+++ b/bin/varnishd/http2/cache_http2.h
77b66a
@@ -131,6 +131,8 @@ struct h2_req {
77b66a
 	/* Where to wake this stream up */
77b66a
 	struct worker			*wrk;
77b66a
 
77b66a
+	ssize_t				reqbody_bytes;
77b66a
+
77b66a
 	VTAILQ_ENTRY(h2_req)		tx_list;
77b66a
 	h2_error			error;
77b66a
 
77b66a
diff --git a/bin/varnishd/http2/cache_http2_proto.c b/bin/varnishd/http2/cache_http2_proto.c
77b66a
index cb35bb4873..98f5dc4f37 100644
77b66a
--- a/bin/varnishd/http2/cache_http2_proto.c
77b66a
+++ b/bin/varnishd/http2/cache_http2_proto.c
77b66a
@@ -546,7 +546,7 @@ h2_end_headers(struct worker *wrk, struct h2_sess *h2,
77b66a
     struct req *req, struct h2_req *r2)
77b66a
 {
77b66a
 	h2_error h2e;
77b66a
-	const char *b;
77b66a
+	ssize_t cl;
77b66a
 
77b66a
 	ASSERT_RXTHR(h2);
77b66a
 	assert(r2->state == H2_S_OPEN);
77b66a
@@ -572,14 +572,24 @@ h2_end_headers(struct worker *wrk, struct h2_sess *h2,
77b66a
 	// XXX: Have I mentioned H/2 Is hodge-podge ?
77b66a
 	http_CollectHdrSep(req->http, H_Cookie, "; ");	// rfc7540,l,3114,3120
77b66a
 
77b66a
+	cl = http_GetContentLength(req->http);
77b66a
+	assert(cl >= -2);
77b66a
+	if (cl == -2) {
77b66a
+		VSLb(h2->vsl, SLT_Debug, "Non-parseable Content-Length");
77b66a
+		return (H2SE_PROTOCOL_ERROR);
77b66a
+	}
77b66a
+
77b66a
 	if (req->req_body_status == REQ_BODY_INIT) {
77b66a
-		if (!http_GetHdr(req->http, H_Content_Length, &b))
77b66a
+		if (cl == -1)
77b66a
 			req->req_body_status = REQ_BODY_WITHOUT_LEN;
77b66a
 		else
77b66a
 			req->req_body_status = REQ_BODY_WITH_LEN;
77b66a
+		req->htc->content_length = cl;
77b66a
 	} else {
77b66a
+		/* A HEADER frame contained END_STREAM */
77b66a
 		assert (req->req_body_status == REQ_BODY_NONE);
77b66a
-		if (http_GetContentLength(req->http) > 0)
77b66a
+		r2->state = H2_S_CLOS_REM;
77b66a
+		if (cl > 0)
77b66a
 			return (H2CE_PROTOCOL_ERROR); //rfc7540,l,1838,1840
77b66a
 	}
77b66a
 
77b66a
@@ -736,6 +746,7 @@ h2_rx_data(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2)
77b66a
 	int w1 = 0, w2 = 0;
77b66a
 	char buf[4];
77b66a
 	unsigned wi;
77b66a
+	ssize_t cl;
77b66a
 
77b66a
 	CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
77b66a
 	ASSERT_RXTHR(h2);
77b66a
@@ -754,6 +765,23 @@ h2_rx_data(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2)
77b66a
 		Lck_Unlock(&h2->sess->mtx);
77b66a
 		return (h2->error ? h2->error : r2->error);
77b66a
 	}
77b66a
+
77b66a
+	r2->reqbody_bytes += h2->rxf_len;
77b66a
+	if (h2->rxf_flags & H2FF_DATA_END_STREAM)
77b66a
+		r2->state = H2_S_CLOS_REM;
77b66a
+	cl = r2->req->htc->content_length;
77b66a
+	if (cl >= 0 && (r2->reqbody_bytes > cl ||
77b66a
+	      (r2->state >= H2_S_CLOS_REM && r2->reqbody_bytes != cl))) {
77b66a
+		VSLb(h2->vsl, SLT_Debug,
77b66a
+		    "H2: stream %u: Received data and Content-Length"
77b66a
+		    " mismatch", h2->rxf_stream);
77b66a
+		r2->error = H2SE_PROTOCOL_ERROR; // rfc7540,l,3150,3163
77b66a
+		if (r2->cond)
77b66a
+			AZ(pthread_cond_signal(r2->cond));
77b66a
+		Lck_Unlock(&h2->sess->mtx);
77b66a
+		return (H2SE_PROTOCOL_ERROR);
77b66a
+	}
77b66a
+
77b66a
 	AZ(h2->mailcall);
77b66a
 	h2->mailcall = r2;
77b66a
 	h2->req0->r_window -= h2->rxf_len;
77b66a
@@ -772,6 +800,8 @@ h2_rx_data(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2)
77b66a
 		r2->r_window += wi;
77b66a
 		w2 = 1;
77b66a
 	}
77b66a
+
77b66a
+
77b66a
 	Lck_Unlock(&h2->sess->mtx);
77b66a
 
77b66a
 	if (w1 || w2) {
77b66a
@@ -794,7 +824,7 @@ h2_vfp_body(struct vfp_ctx *vc, struct vfp_entry *vfe, void *ptr, ssize_t *lp)
77b66a
 	struct h2_req *r2;
77b66a
 	struct h2_sess *h2;
77b66a
 	unsigned l;
77b66a
-	enum vfp_status retval = VFP_OK;
77b66a
+	enum vfp_status retval;
77b66a
 
77b66a
 	CHECK_OBJ_NOTNULL(vc, VFP_CTX_MAGIC);
77b66a
 	CHECK_OBJ_NOTNULL(vfe, VFP_ENTRY_MAGIC);
77b66a
@@ -807,7 +837,6 @@ h2_vfp_body(struct vfp_ctx *vc, struct vfp_entry *vfe, void *ptr, ssize_t *lp)
77b66a
 	*lp = 0;
77b66a
 
77b66a
 	Lck_Lock(&h2->sess->mtx);
77b66a
-	assert (r2->state == H2_S_OPEN);
77b66a
 	r2->cond = &vc->wrk->cond;
77b66a
 	while (h2->mailcall != r2 && h2->error == 0 && r2->error == 0)
77b66a
 		AZ(Lck_CondWait(r2->cond, &h2->sess->mtx, 0));
77b66a
@@ -830,12 +859,10 @@ h2_vfp_body(struct vfp_ctx *vc, struct vfp_entry *vfe, void *ptr, ssize_t *lp)
77b66a
 			Lck_Unlock(&h2->sess->mtx);
77b66a
 			return (VFP_OK);
77b66a
 		}
77b66a
-		if (h2->rxf_len == 0) {
77b66a
-			if (h2->rxf_flags & H2FF_DATA_END_STREAM) {
77b66a
-				retval = VFP_END;
77b66a
-				r2->state = H2_S_CLOS_REM;
77b66a
-			}
77b66a
-		}
77b66a
+		if (h2->rxf_len == 0 && r2->state >= H2_S_CLOS_REM)
77b66a
+			retval = VFP_END;
77b66a
+		else
77b66a
+			retval = VFP_OK;
77b66a
 		h2->mailcall = NULL;
77b66a
 		AZ(pthread_cond_signal(h2->cond));
77b66a
 	}