summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/ot_http.c6
-rw-r--r--tests/integration/test_keepalive.py16
2 files changed, 19 insertions, 3 deletions
diff --git a/src/ot_http.c b/src/ot_http.c
index fc49333..1b5281c 100644
--- a/src/ot_http.c
+++ b/src/ot_http.c
@@ -762,8 +762,8 @@ ssize_t http_handle_request(const int64_t sock, struct ot_workstruct *ws) {
762 } 762 }
763 } 763 }
764 764
765 /* This one implicitely tests strlen < 5, too -- remember, it is \n terminated */ 765 /* The request need not be NUL terminated and can be shorter than the literal */
766 if (memcmp(read_ptr, "GET /", 5)) 766 if (ws->request_size < 5 || memcmp(read_ptr, "GET /", 5))
767 HTTPERROR_400; 767 HTTPERROR_400;
768 768
769 /* Skip leading '/' */ 769 /* Skip leading '/' */
@@ -786,7 +786,7 @@ ssize_t http_handle_request(const int64_t sock, struct ot_workstruct *ws) {
786 if ((*write_ptr == 'a') || (*write_ptr == '?')) 786 if ((*write_ptr == 'a') || (*write_ptr == '?'))
787 http_handle_announce(sock, ws, read_ptr); 787 http_handle_announce(sock, ws, read_ptr);
788#ifdef WANT_FULLSCRAPE 788#ifdef WANT_FULLSCRAPE
789 else if (!memcmp(write_ptr, "scrape HTTP/", 12)) 789 else if (ws->request + ws->request_size - write_ptr >= 12 && !memcmp(write_ptr, "scrape HTTP/", 12))
790 http_handle_fullscrape(sock, ws); 790 http_handle_fullscrape(sock, ws);
791#endif 791#endif
792 /* This is the hardcore match for scrape */ 792 /* This is the hardcore match for scrape */
diff --git a/tests/integration/test_keepalive.py b/tests/integration/test_keepalive.py
index 2c724fc..f9aa468 100644
--- a/tests/integration/test_keepalive.py
+++ b/tests/integration/test_keepalive.py
@@ -107,6 +107,22 @@ class KeepAlive(TrackerTestCase):
107 finally: 107 finally:
108 c.close() 108 c.close()
109 109
110 def test_a_tiny_split_request_does_not_overread(self):
111 """A request that arrives in pieces and completes as a buffer shorter
112 than the dispatch literals (\"GET /\" etc.) used to read past the
113 exactly sized buffer. It must be rejected without a sanitizer hit."""
114 c = self.tracker.connect()
115 try:
116 c.sock.sendall(b"\r\n")
117 time.sleep(0.05)
118 c.sock.sendall(b"\r\n") # completes as a 4-byte buffer
119 time.sleep(0.1)
120 finally:
121 c.close()
122 # the tracker must still answer; tearDown's stop() surfaces any sanitizer abort
123 answer = self.tracker.announce(self.hash_for("after-tiny"), port=6881, left=0)
124 self.assertNotIn(b"failure reason", answer, "the tracker stopped serving after a tiny request")
125
110 def test_a_split_request_answered_by_a_worker_does_not_spin(self): 126 def test_a_split_request_answered_by_a_worker_does_not_spin(self):
111 """A reply that a worker produces does not go through the send path, 127 """A reply that a worker produces does not go through the send path,
112 so the request stays in the buffer the read loop is reading. The loop 128 so the request stays in the buffer the read loop is reading. The loop