diff options
-rw-r--r-- | meta/recipes-support/nghttp2/nghttp2/CVE-2023-35945.patch | 151 | ||||
-rw-r--r-- | meta/recipes-support/nghttp2/nghttp2_1.52.0.bb | 1 |
2 files changed, 152 insertions, 0 deletions
diff --git a/meta/recipes-support/nghttp2/nghttp2/CVE-2023-35945.patch b/meta/recipes-support/nghttp2/nghttp2/CVE-2023-35945.patch new file mode 100644 index 0000000000..04d2086e1c --- /dev/null +++ b/meta/recipes-support/nghttp2/nghttp2/CVE-2023-35945.patch | |||
@@ -0,0 +1,151 @@ | |||
1 | From ce385d3f55a4b76da976b3bdf71fe2deddf315ba Mon Sep 17 00:00:00 2001 | ||
2 | From: Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com> | ||
3 | Date: Mon, 4 Sep 2023 06:48:30 +0000 | ||
4 | Subject: [PATCH] Fix memory leak | ||
5 | |||
6 | This commit fixes memory leak that happens when PUSH_PROMISE or | ||
7 | HEADERS frame cannot be sent, and nghttp2_on_stream_close_callback | ||
8 | fails with a fatal error. For example, if GOAWAY frame has been | ||
9 | received, a HEADERS frame that opens new stream cannot be sent. | ||
10 | |||
11 | This issue has already been made public via CVE-2023-35945 [1] issued | ||
12 | by envoyproxy/envoy project. During embargo period, the patch to fix | ||
13 | this bug was accidentally submitted to nghttp2/nghttp2 repository [2]. | ||
14 | And they decided to disclose CVE early. I was notified just 1.5 hours | ||
15 | before disclosure. I had no time to respond. | ||
16 | |||
17 | PoC described in [1] is quite simple, but I think it is not enough to | ||
18 | trigger this bug. While it is true that receiving GOAWAY prevents a | ||
19 | client from opening new stream, and nghttp2 enters error handling | ||
20 | branch, in order to cause the memory leak, | ||
21 | nghttp2_session_close_stream function must return a fatal error. | ||
22 | nghttp2 defines 2 fatal error codes: | ||
23 | |||
24 | - NGHTTP2_ERR_NOMEM | ||
25 | - NGHTTP2_ERR_CALLBACK_FAILURE | ||
26 | |||
27 | NGHTTP2_ERR_NOMEM, as its name suggests, indicates out of memory. It | ||
28 | is unlikely that a process gets short of memory with this simple PoC | ||
29 | scenario unless application does something memory heavy processing. | ||
30 | |||
31 | NGHTTP2_ERR_CALLBACK_FAILURE is returned from application defined | ||
32 | callback function (nghttp2_on_stream_close_callback, in this case), | ||
33 | which indicates something fatal happened inside a callback, and a | ||
34 | connection must be closed immediately without any further action. As | ||
35 | nghttp2_on_stream_close_error_callback documentation says, any error | ||
36 | code other than 0 or NGHTTP2_ERR_CALLBACK_FAILURE is treated as fatal | ||
37 | error code. More specifically, it is treated as if | ||
38 | NGHTTP2_ERR_CALLBACK_FAILURE is returned. I guess that envoy returns | ||
39 | NGHTTP2_ERR_CALLBACK_FAILURE or other error code which is translated | ||
40 | into NGHTTP2_ERR_CALLBACK_FAILURE. | ||
41 | |||
42 | [1] https://github.com/envoyproxy/envoy/security/advisories/GHSA-jfxv-29pc-x22r | ||
43 | [2] https://github.com/nghttp2/nghttp2/pull/1929 | ||
44 | |||
45 | CVE: CVE-2023-35945 | ||
46 | |||
47 | Upstream-Status: Backport [https://github.com/nghttp2/nghttp2/commit/ce385d3f55a4b76da976b3bdf71fe2deddf315ba] | ||
48 | |||
49 | Signed-off-by: Yogita Urade <yogita.urade@windriver.com> | ||
50 | --- | ||
51 | lib/nghttp2_session.c | 10 +++++----- | ||
52 | tests/nghttp2_session_test.c | 34 ++++++++++++++++++++++++++++++++++ | ||
53 | 2 files changed, 39 insertions(+), 5 deletions(-) | ||
54 | |||
55 | diff --git a/lib/nghttp2_session.c b/lib/nghttp2_session.c | ||
56 | index 93f3f07..9bb32b2 100644 | ||
57 | --- a/lib/nghttp2_session.c | ||
58 | +++ b/lib/nghttp2_session.c | ||
59 | @@ -3300,6 +3300,7 @@ static ssize_t nghttp2_session_mem_send_internal(nghttp2_session *session, | ||
60 | if (rv < 0) { | ||
61 | int32_t opened_stream_id = 0; | ||
62 | uint32_t error_code = NGHTTP2_INTERNAL_ERROR; | ||
63 | + int rv2 = 0; | ||
64 | |||
65 | DEBUGF("send: frame preparation failed with %s\n", | ||
66 | nghttp2_strerror(rv)); | ||
67 | @@ -3342,19 +3343,18 @@ static ssize_t nghttp2_session_mem_send_internal(nghttp2_session *session, | ||
68 | } | ||
69 | if (opened_stream_id) { | ||
70 | /* careful not to override rv */ | ||
71 | - int rv2; | ||
72 | rv2 = nghttp2_session_close_stream(session, opened_stream_id, | ||
73 | error_code); | ||
74 | - | ||
75 | - if (nghttp2_is_fatal(rv2)) { | ||
76 | - return rv2; | ||
77 | - } | ||
78 | } | ||
79 | |||
80 | nghttp2_outbound_item_free(item, mem); | ||
81 | nghttp2_mem_free(mem, item); | ||
82 | active_outbound_item_reset(aob, mem); | ||
83 | |||
84 | + if (nghttp2_is_fatal(rv2)) { | ||
85 | + return rv2; | ||
86 | + } | ||
87 | + | ||
88 | if (rv == NGHTTP2_ERR_HEADER_COMP) { | ||
89 | /* If header compression error occurred, should terminiate | ||
90 | connection. */ | ||
91 | diff --git a/tests/nghttp2_session_test.c b/tests/nghttp2_session_test.c | ||
92 | index 08152d4..14ab132 100644 | ||
93 | --- a/tests/nghttp2_session_test.c | ||
94 | +++ b/tests/nghttp2_session_test.c | ||
95 | @@ -585,6 +585,15 @@ static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id, | ||
96 | return 0; | ||
97 | } | ||
98 | |||
99 | +static int fatal_error_on_stream_close_callback(nghttp2_session *session, | ||
100 | + int32_t stream_id, | ||
101 | + uint32_t error_code, | ||
102 | + void *user_data) { | ||
103 | + on_stream_close_callback(session, stream_id, error_code, user_data); | ||
104 | + | ||
105 | + return NGHTTP2_ERR_CALLBACK_FAILURE; | ||
106 | +} | ||
107 | + | ||
108 | static ssize_t pack_extension_callback(nghttp2_session *session, uint8_t *buf, | ||
109 | size_t len, const nghttp2_frame *frame, | ||
110 | void *user_data) { | ||
111 | @@ -4297,6 +4306,8 @@ void test_nghttp2_session_on_goaway_received(void) { | ||
112 | nghttp2_frame frame; | ||
113 | int i; | ||
114 | nghttp2_mem *mem; | ||
115 | + const uint8_t *data; | ||
116 | + ssize_t datalen; | ||
117 | |||
118 | mem = nghttp2_mem_default(); | ||
119 | user_data.frame_recv_cb_called = 0; | ||
120 | @@ -4338,6 +4349,29 @@ void test_nghttp2_session_on_goaway_received(void) { | ||
121 | |||
122 | nghttp2_frame_goaway_free(&frame.goaway, mem); | ||
123 | nghttp2_session_del(session); | ||
124 | + | ||
125 | + /* Make sure that no memory leak when stream_close callback fails | ||
126 | + with a fatal error */ | ||
127 | + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); | ||
128 | + callbacks.on_stream_close_callback = fatal_error_on_stream_close_callback; | ||
129 | + | ||
130 | + memset(&user_data, 0, sizeof(user_data)); | ||
131 | + | ||
132 | + nghttp2_session_client_new(&session, &callbacks, &user_data); | ||
133 | + | ||
134 | + nghttp2_frame_goaway_init(&frame.goaway, 0, NGHTTP2_NO_ERROR, NULL, 0); | ||
135 | + | ||
136 | + CU_ASSERT(0 == nghttp2_session_on_goaway_received(session, &frame)); | ||
137 | + | ||
138 | + nghttp2_submit_request(session, NULL, reqnv, ARRLEN(reqnv), NULL, NULL); | ||
139 | + | ||
140 | + datalen = nghttp2_session_mem_send(session, &data); | ||
141 | + | ||
142 | + CU_ASSERT(NGHTTP2_ERR_CALLBACK_FAILURE == datalen); | ||
143 | + CU_ASSERT(1 == user_data.stream_close_cb_called); | ||
144 | + | ||
145 | + nghttp2_frame_goaway_free(&frame.goaway, mem); | ||
146 | + nghttp2_session_del(session); | ||
147 | } | ||
148 | |||
149 | void test_nghttp2_session_on_window_update_received(void) { | ||
150 | -- | ||
151 | 2.35.5 | ||
diff --git a/meta/recipes-support/nghttp2/nghttp2_1.52.0.bb b/meta/recipes-support/nghttp2/nghttp2_1.52.0.bb index f57a15954d..0fba554919 100644 --- a/meta/recipes-support/nghttp2/nghttp2_1.52.0.bb +++ b/meta/recipes-support/nghttp2/nghttp2_1.52.0.bb | |||
@@ -7,6 +7,7 @@ LIC_FILES_CHKSUM = "file://COPYING;md5=764abdf30b2eadd37ce47dcbce0ea1ec" | |||
7 | SRC_URI = "\ | 7 | SRC_URI = "\ |
8 | ${GITHUB_BASE_URI}/download/v${PV}/nghttp2-${PV}.tar.xz \ | 8 | ${GITHUB_BASE_URI}/download/v${PV}/nghttp2-${PV}.tar.xz \ |
9 | file://0001-fetch-ocsp-response-use-python3.patch \ | 9 | file://0001-fetch-ocsp-response-use-python3.patch \ |
10 | file://CVE-2023-35945.patch \ | ||
10 | " | 11 | " |
11 | SRC_URI[sha256sum] = "3ea9f0439e60469ad4d39cb349938684ffb929dd7e8e06a7bffe9f9d21f8ba7d" | 12 | SRC_URI[sha256sum] = "3ea9f0439e60469ad4d39cb349938684ffb929dd7e8e06a7bffe9f9d21f8ba7d" |
12 | 13 | ||