Lines 1-279
Link Here
|
1 |
From 3624ce6cfd4d197db75f01ae4be37723d7d9b638 Mon Sep 17 00:00:00 2001 |
|
|
2 |
From: Arvid Norberg <arvid.norberg@gmail.com> |
3 |
Date: Sat, 4 Jun 2016 09:53:23 -0400 |
4 |
Subject: [PATCH] fixed crash on invalid input in http_parser (#782) |
5 |
|
6 |
fixed crash on invalid input to http_parser |
7 |
--- |
8 |
ChangeLog | 1 + |
9 |
include/libtorrent/add_torrent_params.hpp | 3 +- |
10 |
src/http_parser.cpp | 32 +++++++ |
11 |
test/test_http_parser.cpp | 139 +++++++++++++++++++++++++----- |
12 |
4 files changed, 151 insertions(+), 24 deletions(-) |
13 |
|
14 |
diff --git a/src/http_parser.cpp b/src/http_parser.cpp |
15 |
index a9497f8..52f6152 100644 |
16 |
--- src/http_parser.cpp |
17 |
+++ src/http_parser.cpp |
18 |
@@ -174,6 +174,7 @@ namespace libtorrent |
19 |
if (m_state == read_status) |
20 |
{ |
21 |
TORRENT_ASSERT(!m_finished); |
22 |
+ TORRENT_ASSERT(pos <= recv_buffer.end); |
23 |
char const* newline = std::find(pos, recv_buffer.end, '\n'); |
24 |
// if we don't have a full line yet, wait. |
25 |
if (newline == recv_buffer.end) |
26 |
@@ -194,6 +195,7 @@ namespace libtorrent |
27 |
|
28 |
char const* line = pos; |
29 |
++newline; |
30 |
+ TORRENT_ASSERT(newline >= pos); |
31 |
int incoming = int(newline - pos); |
32 |
m_recv_pos += incoming; |
33 |
boost::get<1>(ret) += newline - (m_recv_buffer.begin + start_pos); |
34 |
@@ -227,6 +229,7 @@ namespace libtorrent |
35 |
if (m_state == read_header) |
36 |
{ |
37 |
TORRENT_ASSERT(!m_finished); |
38 |
+ TORRENT_ASSERT(pos <= recv_buffer.end); |
39 |
char const* newline = std::find(pos, recv_buffer.end, '\n'); |
40 |
std::string line; |
41 |
|
42 |
@@ -277,6 +280,12 @@ namespace libtorrent |
43 |
if (name == "content-length") |
44 |
{ |
45 |
m_content_length = strtoll(value.c_str(), 0, 10); |
46 |
+ if (m_content_length < 0) |
47 |
+ { |
48 |
+ m_state = error_state; |
49 |
+ error = true; |
50 |
+ return ret; |
51 |
+ } |
52 |
} |
53 |
else if (name == "connection") |
54 |
{ |
55 |
@@ -294,12 +303,24 @@ namespace libtorrent |
56 |
if (string_begins_no_case("bytes ", ptr)) ptr += 6; |
57 |
char* end; |
58 |
m_range_start = strtoll(ptr, &end, 10); |
59 |
+ if (m_range_start < 0) |
60 |
+ { |
61 |
+ m_state = error_state; |
62 |
+ error = true; |
63 |
+ return ret; |
64 |
+ } |
65 |
if (end == ptr) success = false; |
66 |
else if (*end != '-') success = false; |
67 |
else |
68 |
{ |
69 |
ptr = end + 1; |
70 |
m_range_end = strtoll(ptr, &end, 10); |
71 |
+ if (m_range_end < 0) |
72 |
+ { |
73 |
+ m_state = error_state; |
74 |
+ error = true; |
75 |
+ return ret; |
76 |
+ } |
77 |
if (end == ptr) success = false; |
78 |
} |
79 |
|
80 |
@@ -318,6 +339,7 @@ namespace libtorrent |
81 |
} |
82 |
|
83 |
TORRENT_ASSERT(m_recv_pos <= recv_buffer.left()); |
84 |
+ TORRENT_ASSERT(pos <= recv_buffer.end); |
85 |
newline = std::find(pos, recv_buffer.end, '\n'); |
86 |
} |
87 |
boost::get<1>(ret) += newline - (m_recv_buffer.begin + start_pos); |
88 |
@@ -347,6 +369,12 @@ namespace libtorrent |
89 |
int header_size; |
90 |
if (parse_chunk_header(buf, &chunk_size, &header_size)) |
91 |
{ |
92 |
+ if (chunk_size < 0) |
93 |
+ { |
94 |
+ m_state = error_state; |
95 |
+ error = true; |
96 |
+ return ret; |
97 |
+ } |
98 |
if (chunk_size > 0) |
99 |
{ |
100 |
std::pair<boost::int64_t, boost::int64_t> chunk_range(m_cur_chunk_end + header_size |
101 |
@@ -419,6 +447,7 @@ namespace libtorrent |
102 |
bool http_parser::parse_chunk_header(buffer::const_interval buf |
103 |
, boost::int64_t* chunk_size, int* header_size) |
104 |
{ |
105 |
+ TORRENT_ASSERT(buf.begin <= buf.end); |
106 |
char const* pos = buf.begin; |
107 |
|
108 |
// ignore one optional new-line. This is since each chunk |
109 |
@@ -429,6 +458,7 @@ namespace libtorrent |
110 |
if (pos < buf.end && pos[0] == '\n') ++pos; |
111 |
if (pos == buf.end) return false; |
112 |
|
113 |
+ TORRENT_ASSERT(pos <= buf.end); |
114 |
char const* newline = std::find(pos, buf.end, '\n'); |
115 |
if (newline == buf.end) return false; |
116 |
++newline; |
117 |
@@ -441,6 +471,8 @@ namespace libtorrent |
118 |
|
119 |
// first, read the chunk length |
120 |
*chunk_size = strtoll(pos, 0, 16); |
121 |
+ if (*chunk_size < 0) return true; |
122 |
+ |
123 |
if (*chunk_size != 0) |
124 |
{ |
125 |
*header_size = newline - buf.begin; |
126 |
diff --git a/test/test_http_parser.cpp b/test/test_http_parser.cpp |
127 |
index c26d1c8..6835a12 100644 |
128 |
--- test/test_http_parser.cpp |
129 |
+++ test/test_http_parser.cpp |
130 |
@@ -361,29 +361,6 @@ TORRENT_TEST(http_parser) |
131 |
TEST_EQUAL(parser.headers().find("test2")->second, "bar"); |
132 |
} |
133 |
|
134 |
- // test chunked encoding |
135 |
- |
136 |
- parser.reset(); |
137 |
- |
138 |
- char const* chunked_input = |
139 |
- "HTTP/1.1 200 OK\r\n" |
140 |
- "Transfer-Encoding: chunked\r\n" |
141 |
- "Content-Type: text/plain\r\n" |
142 |
- "\r\n" |
143 |
- "4\r\ntest\r\n4\r\n1234\r\n10\r\n0123456789abcdef\r\n" |
144 |
- "0\r\n\r\n"; |
145 |
- received = feed_bytes(parser, chunked_input); |
146 |
- |
147 |
- TEST_EQUAL(strlen(chunked_input), 24 + 94) |
148 |
- TEST_CHECK(received == make_tuple(24, 94, false)); |
149 |
- TEST_CHECK(parser.finished()); |
150 |
- |
151 |
- char mutable_buffer[100]; |
152 |
- memcpy(mutable_buffer, parser.get_body().begin, parser.get_body().left()); |
153 |
- int len = parser.collapse_chunk_headers(mutable_buffer, parser.get_body().left()); |
154 |
- |
155 |
- TEST_CHECK(std::equal(mutable_buffer, mutable_buffer + len, "test12340123456789abcdef")); |
156 |
- |
157 |
// test url parsing |
158 |
|
159 |
error_code ec; |
160 |
@@ -476,3 +453,119 @@ TORRENT_TEST(http_parser) |
161 |
TEST_EQUAL(is_redirect(400), false); |
162 |
} |
163 |
|
164 |
+TORRENT_TEST(chunked_encoding) |
165 |
+{ |
166 |
+ char const* chunked_input = |
167 |
+ "HTTP/1.1 200 OK\r\n" |
168 |
+ "Transfer-Encoding: chunked\r\n" |
169 |
+ "Content-Type: text/plain\r\n" |
170 |
+ "\r\n" |
171 |
+ "4\r\ntest\r\n4\r\n1234\r\n10\r\n0123456789abcdef\r\n" |
172 |
+ "0\r\n\r\n"; |
173 |
+ |
174 |
+ http_parser parser; |
175 |
+ boost::tuple<int, int, bool> const received |
176 |
+ = feed_bytes(parser, chunked_input); |
177 |
+ |
178 |
+ TEST_EQUAL(strlen(chunked_input), 24 + 94) |
179 |
+ TEST_CHECK(received == make_tuple(24, 94, false)); |
180 |
+ TEST_CHECK(parser.finished()); |
181 |
+ |
182 |
+ char mutable_buffer[100]; |
183 |
+ memcpy(mutable_buffer, parser.get_body().begin, parser.get_body().left()); |
184 |
+ int len = parser.collapse_chunk_headers(mutable_buffer, parser.get_body().left()); |
185 |
+ |
186 |
+ TEST_CHECK(std::equal(mutable_buffer, mutable_buffer + len, "test12340123456789abcdef")); |
187 |
+} |
188 |
+ |
189 |
+TORRENT_TEST(invalid_content_length) |
190 |
+{ |
191 |
+ char const* chunked_input = |
192 |
+ "HTTP/1.1 200 OK\r\n" |
193 |
+ "Transfer-Encoding: chunked\r\n" |
194 |
+ "Content-Length: -45345\r\n" |
195 |
+ "\r\n"; |
196 |
+ |
197 |
+ http_parser parser; |
198 |
+ boost::tuple<int, int, bool> const received |
199 |
+ = feed_bytes(parser, chunked_input); |
200 |
+ |
201 |
+ TEST_CHECK(boost::get<2>(received) == true); |
202 |
+} |
203 |
+ |
204 |
+TORRENT_TEST(invalid_chunked) |
205 |
+{ |
206 |
+ char const* chunked_input = |
207 |
+ "HTTP/1.1 200 OK\r\n" |
208 |
+ "Transfer-Encoding: chunked\r\n" |
209 |
+ "\r\n" |
210 |
+ "-53465234545\r\n" |
211 |
+ "foobar"; |
212 |
+ |
213 |
+ http_parser parser; |
214 |
+ boost::tuple<int, int, bool> const received |
215 |
+ = feed_bytes(parser, chunked_input); |
216 |
+ |
217 |
+ TEST_CHECK(boost::get<2>(received) == true); |
218 |
+} |
219 |
+ |
220 |
+TORRENT_TEST(invalid_content_range_start) |
221 |
+{ |
222 |
+ char const* chunked_input = |
223 |
+ "HTTP/1.1 206 OK\n" |
224 |
+ "Content-Range: bYTes -3-4\n" |
225 |
+ "\n"; |
226 |
+ |
227 |
+ http_parser parser; |
228 |
+ boost::tuple<int, int, bool> const received |
229 |
+ = feed_bytes(parser, chunked_input); |
230 |
+ |
231 |
+ TEST_CHECK(boost::get<2>(received) == true); |
232 |
+} |
233 |
+ |
234 |
+TORRENT_TEST(invalid_content_range_end) |
235 |
+{ |
236 |
+ char const* chunked_input = |
237 |
+ "HTTP/1.1 206 OK\n" |
238 |
+ "Content-Range: bYTes 3--434\n" |
239 |
+ "\n"; |
240 |
+ |
241 |
+ http_parser parser; |
242 |
+ boost::tuple<int, int, bool> const received |
243 |
+ = feed_bytes(parser, chunked_input); |
244 |
+ |
245 |
+ TEST_CHECK(boost::get<2>(received) == true); |
246 |
+} |
247 |
+ |
248 |
+TORRENT_TEST(invalid_chunk_afl) |
249 |
+{ |
250 |
+ boost::uint8_t const invalid_chunked_input[] = { |
251 |
+ 0x48, 0x6f, 0x54, 0x50, 0x2f, 0x31, 0x2e, 0x31, // HoTP/1.1 200 OK |
252 |
+ 0x20, 0x32, 0x30, 0x30, 0x20, 0x4f, 0x4b, 0x0d, // Cont-Length: 20 |
253 |
+ 0x0a, 0x43, 0x6f, 0x6e, 0x74, 0x2d, 0x4c, 0x65, // Contente: tn |
254 |
+ 0x6e, 0x67, 0x74, 0x68, 0x3a, 0x20, 0x32, 0x30, // Transfer-Encoding: chunked |
255 |
+ 0x0d, 0x0a, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, // |
256 |
+ 0x74, 0x65, 0x3a, 0x20, 0x74, 0x6e, 0x0d, 0x0a, // |
257 |
+ 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, // |
258 |
+ 0x2d, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, // -89abc9abcdef |
259 |
+ 0x67, 0x3a, 0x20, 0x63, 0x68, 0x75, 0x6e, 0x6b, // � |
260 |
+ 0x65, 0x64, 0x0d, 0x0a, 0x0d, 0x0d, 0x0a, 0x0d, // T����������def |
261 |
+ 0x0a, 0x0a, 0x2d, 0x38, 0x39, 0x61, 0x62, 0x63, // � |
262 |
+ 0x39, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x0d, // T�����������est-headyr: foobar |
263 |
+ 0x0a, 0xd6, 0x0d, 0x0a, 0x54, 0xbd, 0xbd, 0xbd, |
264 |
+ 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0x64, |
265 |
+ 0x65, 0x66, 0x0d, 0x0a, 0xd6, 0x0d, 0x0a, 0x54, |
266 |
+ 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, |
267 |
+ 0xbd, 0xbd, 0xbd, 0x65, 0x73, 0x74, 0x2d, 0x68, |
268 |
+ 0x65, 0x61, 0x64, 0x79, 0x72, 0x3a, 0x20, 0x66, |
269 |
+ 0x6f, 0x6f, 0x62, 0x61, 0x72, 0x0d, 0x0a, 0x0d, |
270 |
+ 0x0a, 0x00 |
271 |
+ }; |
272 |
+ |
273 |
+ http_parser parser; |
274 |
+ boost::tuple<int, int, bool> const received |
275 |
+ = feed_bytes(parser, reinterpret_cast<char const*>(invalid_chunked_input)); |
276 |
+ |
277 |
+ TEST_CHECK(boost::get<2>(received) == true); |
278 |
+} |
279 |
+ |