Skip to content

Commit c9b7482

Browse files
committed
BUG/MAJOR: mux-h1: Prevent any UAF on H1 connection after draining a request
Since 2.9, it is possible to drain the request payload from the H1 multiplexer in case of early reply. When this happens, the upper stream is detached but the H1 stream is not destroyed. Once the whole request is drained, the end of the detach stage is finished. So the H1 stream is destroyed and the H1 connection is ready to be reused, if possible, otherwise it is released. And here is the issue. If some data of the next request are received with last bytes of the drained one, parsing of the next request is immediately started. The previous H1 stream is destroyed and a new one is created to handle the parsing. At this stage the H1 connection may be released, for instance because of a parsing error. This case was not properly handled. Instead of immediately exiting the mux, it was still possible to access the released H1 connection to refresh its timeouts, leading to a UAF issue. Many thanks to Annika for her invaluable help on this issue. The patch should fix the issue #2602. It must be backported as far as 2.9. (cherry picked from commit 0e09cce) Signed-off-by: Christopher Faulet <[email protected]>
1 parent 34a0a7c commit c9b7482

File tree

1 file changed

+20
-10
lines changed

1 file changed

+20
-10
lines changed

src/mux_h1.c

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1018,9 +1018,10 @@ static int h1s_must_shut_conn(struct h1s *h1s)
10181018

10191019
/* Really detach the H1S. Most of time of it called from h1_detach() when the
10201020
* stream is detached from the connection. But if the request message must be
1021-
* drained first, the detach is deferred.
1021+
* drained first, the detach is deferred. Returns 0 if the h1s is detached but
1022+
* h1c is still usable. -1 is returned if h1s was released.
10221023
*/
1023-
static void h1s_finish_detach(struct h1s *h1s)
1024+
static int h1s_finish_detach(struct h1s *h1s)
10241025
{
10251026
struct h1c *h1c;
10261027
struct session *sess;
@@ -1063,7 +1064,7 @@ static void h1s_finish_detach(struct h1s *h1s)
10631064
if (!session_add_conn(sess, h1c->conn, h1c->conn->target)) {
10641065
h1c->conn->owner = NULL;
10651066
h1c->conn->mux->destroy(h1c);
1066-
goto end;
1067+
goto released;
10671068
}
10681069
/* Always idle at this step */
10691070

@@ -1074,7 +1075,7 @@ static void h1s_finish_detach(struct h1s *h1s)
10741075
if (session_check_idle_conn(sess, h1c->conn)) {
10751076
/* The connection got destroyed, let's leave */
10761077
TRACE_DEVEL("outgoing connection killed", H1_EV_STRM_END|H1_EV_H1C_END);
1077-
goto end;
1078+
goto released;
10781079
}
10791080
}
10801081
else {
@@ -1092,13 +1093,13 @@ static void h1s_finish_detach(struct h1s *h1s)
10921093
/* The server doesn't want it, let's kill the connection right away */
10931094
h1c->conn->mux->destroy(h1c);
10941095
TRACE_DEVEL("outgoing connection killed", H1_EV_STRM_END|H1_EV_H1C_END);
1095-
goto end;
1096+
goto released;
10961097
}
10971098
/* At this point, the connection has been added to the
10981099
* server idle list, so another thread may already have
10991100
* hijacked it, so we can't do anything with it.
11001101
*/
1101-
return;
1102+
goto end;
11021103
}
11031104
}
11041105

@@ -1110,15 +1111,18 @@ static void h1s_finish_detach(struct h1s *h1s)
11101111
!h1c->conn->owner) {
11111112
TRACE_DEVEL("killing dead connection", H1_EV_STRM_END, h1c->conn);
11121113
h1_release(h1c);
1114+
goto released;
11131115
}
11141116
else {
11151117
if (h1c->state == H1_CS_IDLE) {
11161118
/* If we have a new request, process it immediately or
11171119
* subscribe for reads waiting for new data
11181120
*/
11191121
if (unlikely(b_data(&h1c->ibuf))) {
1120-
if (h1_process(h1c) == -1)
1121-
goto end;
1122+
if (h1_process(h1c) == -1) {
1123+
/* h1c was released, don't reuse it anymore */
1124+
goto released;
1125+
}
11221126
}
11231127
else
11241128
h1c->conn->xprt->subscribe(h1c->conn, h1c->conn->xprt_ctx, SUB_RETRY_RECV, &h1c->wait_event);
@@ -1128,6 +1132,11 @@ static void h1s_finish_detach(struct h1s *h1s)
11281132
}
11291133
end:
11301134
TRACE_LEAVE(H1_EV_STRM_END);
1135+
return 0;
1136+
1137+
released:
1138+
TRACE_DEVEL("leaving after releasing the connection", H1_EV_STRM_END);
1139+
return -1;
11311140
}
11321141

11331142

@@ -4017,8 +4026,8 @@ static int h1_process(struct h1c * h1c)
40174026
h1_shutw_conn(conn);
40184027
goto release;
40194028
}
4020-
h1s_finish_detach(h1c->h1s);
4021-
goto end;
4029+
if (h1s_finish_detach(h1c->h1s) == -1)
4030+
goto released;
40224031
}
40234032
}
40244033

@@ -4088,6 +4097,7 @@ static int h1_process(struct h1c * h1c)
40884097
h1_release(h1c);
40894098
TRACE_DEVEL("leaving after releasing the connection", H1_EV_H1C_WAKE);
40904099
}
4100+
released:
40914101
return -1;
40924102
}
40934103

0 commit comments

Comments
 (0)