1 |
From 82b0a629f60136e76112c6f2c6372cce77b683be Mon Sep 17 00:00:00 2001 |
2 |
From: Martin Blix Grydeland <martin@varnish-software.com> |
3 |
Date: Tue, 22 Jun 2021 11:47:55 +0200 |
4 |
Subject: [PATCH] Take content length into account on H/2 request bodies |
5 |
|
6 |
When receiving H/2 data frames, make sure to take the advertised content |
7 |
length into account, and fail appropriately if the combined sum of the |
8 |
data frames does not match the content length. |
9 |
--- |
10 |
bin/varnishd/http2/cache_http2.h | 2 ++ |
11 |
bin/varnishd/http2/cache_http2_proto.c | 46 ++++++++++++++++++++------ |
12 |
2 files changed, 38 insertions(+), 10 deletions(-) |
13 |
|
14 |
diff --git a/bin/varnishd/http2/cache_http2.h b/bin/varnishd/http2/cache_http2.h |
15 |
index 270306d15c..c1456a6803 100644 |
16 |
--- a/bin/varnishd/http2/cache_http2.h |
17 |
+++ b/bin/varnishd/http2/cache_http2.h |
18 |
@@ -134,6 +134,8 @@ struct h2_req { |
19 |
/* Where to wake this stream up */ |
20 |
struct worker *wrk; |
21 |
|
22 |
+ ssize_t reqbody_bytes; |
23 |
+ |
24 |
VTAILQ_ENTRY(h2_req) tx_list; |
25 |
h2_error error; |
26 |
}; |
27 |
diff --git a/bin/varnishd/http2/cache_http2_proto.c b/bin/varnishd/http2/cache_http2_proto.c |
28 |
index c079656b1b..59dd0336b5 100644 |
29 |
--- a/bin/varnishd/http2/cache_http2_proto.c |
30 |
+++ b/bin/varnishd/http2/cache_http2_proto.c |
31 |
@@ -554,6 +554,7 @@ h2_end_headers(struct worker *wrk, struct h2_sess *h2, |
32 |
struct req *req, struct h2_req *r2) |
33 |
{ |
34 |
h2_error h2e; |
35 |
+ ssize_t cl; |
36 |
|
37 |
ASSERT_RXTHR(h2); |
38 |
assert(r2->state == H2_S_OPEN); |
39 |
@@ -574,16 +575,24 @@ h2_end_headers(struct worker *wrk, struct h2_sess *h2, |
40 |
// XXX: Have I mentioned H/2 Is hodge-podge ? |
41 |
http_CollectHdrSep(req->http, H_Cookie, "; "); // rfc7540,l,3114,3120 |
42 |
|
43 |
+ cl = http_GetContentLength(req->http); |
44 |
+ assert(cl >= -2); |
45 |
+ if (cl == -2) { |
46 |
+ VSLb(h2->vsl, SLT_Debug, "Non-parseable Content-Length"); |
47 |
+ return (H2SE_PROTOCOL_ERROR); |
48 |
+ } |
49 |
+ |
50 |
if (req->req_body_status == NULL) { |
51 |
- if (!http_GetHdr(req->http, H_Content_Length, NULL)) |
52 |
+ if (cl == -1) |
53 |
req->req_body_status = BS_EOF; |
54 |
else |
55 |
req->req_body_status = BS_LENGTH; |
56 |
+ req->htc->content_length = cl; |
57 |
} else { |
58 |
/* A HEADER frame contained END_STREAM */ |
59 |
assert (req->req_body_status == BS_NONE); |
60 |
r2->state = H2_S_CLOS_REM; |
61 |
- if (http_GetContentLength(req->http) > 0) |
62 |
+ if (cl > 0) |
63 |
return (H2CE_PROTOCOL_ERROR); //rfc7540,l,1838,1840 |
64 |
} |
65 |
|
66 |
@@ -737,6 +746,7 @@ h2_rx_data(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2) |
67 |
int w1 = 0, w2 = 0; |
68 |
char buf[4]; |
69 |
unsigned wi; |
70 |
+ ssize_t cl; |
71 |
|
72 |
CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); |
73 |
ASSERT_RXTHR(h2); |
74 |
@@ -755,6 +765,23 @@ h2_rx_data(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2) |
75 |
Lck_Unlock(&h2->sess->mtx); |
76 |
return (h2->error ? h2->error : r2->error); |
77 |
} |
78 |
+ |
79 |
+ r2->reqbody_bytes += h2->rxf_len; |
80 |
+ if (h2->rxf_flags & H2FF_DATA_END_STREAM) |
81 |
+ r2->state = H2_S_CLOS_REM; |
82 |
+ cl = r2->req->htc->content_length; |
83 |
+ if (cl >= 0 && (r2->reqbody_bytes > cl || |
84 |
+ (r2->state >= H2_S_CLOS_REM && r2->reqbody_bytes != cl))) { |
85 |
+ VSLb(h2->vsl, SLT_Debug, |
86 |
+ "H2: stream %u: Received data and Content-Length" |
87 |
+ " mismatch", h2->rxf_stream); |
88 |
+ r2->error = H2SE_PROTOCOL_ERROR; // rfc7540,l,3150,3163 |
89 |
+ if (r2->cond) |
90 |
+ AZ(pthread_cond_signal(r2->cond)); |
91 |
+ Lck_Unlock(&h2->sess->mtx); |
92 |
+ return (H2SE_PROTOCOL_ERROR); |
93 |
+ } |
94 |
+ |
95 |
AZ(h2->mailcall); |
96 |
h2->mailcall = r2; |
97 |
h2->req0->r_window -= h2->rxf_len; |
98 |
@@ -773,6 +800,8 @@ h2_rx_data(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2) |
99 |
r2->r_window += wi; |
100 |
w2 = 1; |
101 |
} |
102 |
+ |
103 |
+ |
104 |
Lck_Unlock(&h2->sess->mtx); |
105 |
|
106 |
if (w1 || w2) { |
107 |
@@ -795,7 +824,7 @@ h2_vfp_body(struct vfp_ctx *vc, struct vfp_entry *vfe, void *ptr, ssize_t *lp) |
108 |
struct h2_req *r2; |
109 |
struct h2_sess *h2; |
110 |
unsigned l; |
111 |
- enum vfp_status retval = VFP_OK; |
112 |
+ enum vfp_status retval; |
113 |
|
114 |
CHECK_OBJ_NOTNULL(vc, VFP_CTX_MAGIC); |
115 |
CHECK_OBJ_NOTNULL(vfe, VFP_ENTRY_MAGIC); |
116 |
@@ -808,7 +837,6 @@ h2_vfp_body(struct vfp_ctx *vc, struct vfp_entry *vfe, void *ptr, ssize_t *lp) |
117 |
*lp = 0; |
118 |
|
119 |
Lck_Lock(&h2->sess->mtx); |
120 |
- assert (r2->state == H2_S_OPEN); |
121 |
r2->cond = &vc->wrk->cond; |
122 |
while (h2->mailcall != r2 && h2->error == 0 && r2->error == 0) |
123 |
AZ(Lck_CondWait(r2->cond, &h2->sess->mtx, 0)); |
124 |
@@ -831,12 +859,10 @@ h2_vfp_body(struct vfp_ctx *vc, struct vfp_entry *vfe, void *ptr, ssize_t *lp) |
125 |
Lck_Unlock(&h2->sess->mtx); |
126 |
return (VFP_OK); |
127 |
} |
128 |
- if (h2->rxf_len == 0) { |
129 |
- if (h2->rxf_flags & H2FF_DATA_END_STREAM) { |
130 |
- retval = VFP_END; |
131 |
- r2->state = H2_S_CLOS_REM; |
132 |
- } |
133 |
- } |
134 |
+ if (h2->rxf_len == 0 && r2->state >= H2_S_CLOS_REM) |
135 |
+ retval = VFP_END; |
136 |
+ else |
137 |
+ retval = VFP_OK; |
138 |
h2->mailcall = NULL; |
139 |
AZ(pthread_cond_signal(h2->cond)); |
140 |
} |