CVE-2023-52267: Out-of-bounds Read (CWE-125)
value: ehttp 1.0.6 before 17405b9 has a simple_log.cpp _log out-of-bounds-read during error logging for long strings.
CWE description: The product reads data past the end, or before the beginning, of the intended buffer.
CVSS v3 base score: 7.5
Published: 2023-12-31T00:15Z
Repository: https://github.com/hongliuliao/ehttp
Language: C++
Files changed: Makefile, src/epoll_socket.cpp, src/sim_parser.cpp, test/issue38/poc.py
Commit date: 2023-10-08 06:54:01 +0000
Commit message: fix log overflow & epoll_context use after free bug

Diff: diff --git a/Makefile b/Makefile
index 9a312f5..79116e0 100644
--- a/Makefile
+++ b/Makefile
@@ -3,12 +3,12 @@
 CXX=g++
 CXXFLAGS += -g -Wall
 LDFLAGS += -pthread
 
 ifdef ACOV
-	CXXFLAGS += -fprofile-arcs -ftest-coverage
-	LDFLAGS += -lgcov --coverage
+	CXXFLAGS += -fprofile-arcs -ftest-coverage -fsanitize=address
+	LDFLAGS += -lgcov --coverage -fsanitize=address
 endif
 
 MULTIPART_PARSER_INC_PATH=-I deps/multipart-parser-c/
 MULTIPART_PARSER_LIB_PATH=deps/multipart-parser-c/multipart_parser.o
 
diff --git a/src/epoll_socket.cpp b/src/epoll_socket.cpp
index 635067c..0c75897 100644
--- a/src/epoll_socket.cpp
+++ b/src/epoll_socket.cpp
@@ -226,15 +226,10 @@ int EpollSocket::handle_accept_event(int &epollfd, epoll_event &event, EpollSock
 }
 
 void read_func(void *data) {
     TaskData *td = (TaskData *) data;
     td->es->handle_readable_event(td->event);
-
-    EpollContext *hc = (EpollContext *) td->event.data.ptr;
-    if (hc != NULL) {
-        hc->_ctx_status = CONTEXT_READ_OVER;
-    }
     delete td;
 }
 
 int EpollSocket::handle_readable_event(epoll_event &event) {
     EpollContext *epoll_context = (EpollContext *) event.data.ptr;
@@ -246,10 +241,13 @@ int EpollSocket::handle_readable_event(epoll_event &event) {
 
     int ret = _watcher->on_readable(_epollfd, event);
     if (ret == READ_CLOSE) {
         return close_and_release(event);
     }
+    if (epoll_context != NULL) {
+        epoll_context->_ctx_status = CONTEXT_READ_OVER;
+    }
 
     if (ret == READ_CONTINUE) {
         event.events = EPOLLIN | EPOLLONESHOT;
         ret = epoll_ctl(_epollfd, EPOLL_CTL_MOD, fd, &event);
     } else if (ret == READ_OVER) { // READ_OVER
diff --git a/src/sim_parser.cpp b/src/sim_parser.cpp
index 105db3a..faeba33 100644
--- a/src/sim_parser.cpp
+++ b/src/sim_parser.cpp
@@ -575,11 +575,11 @@ int Request::parse_request(const char *read_buffer, int read_size) {
     _total_req_size += read_size;
     if (_total_req_size > MAX_REQ_SIZE) {
         LOG_INFO("TOO BIG REQUEST WE WILL REFUSE IT! MAX_REQ_SIZE:%d", MAX_REQ_SIZE);
         return -1;
     }
-    LOG_DEBUG("read from client: size:%d, content:%s", read_size, read_buffer);
+    LOG_DEBUG("read from client: size:%d, content:%.4096s", read_size, read_buffer);
     ssize_t nparsed = http_parser_execute(&_parser, &_settings, read_buffer, read_size);
     if (nparsed != read_size) {
         std::string err_msg = "unkonw";
         if (_parser.http_errno) {
             err_msg = http_errno_description(HTTP_PARSER_ERRNO(&_parser));
diff --git a/test/issue38/poc.py b/test/issue38/poc.py
new file mode 100644
index 0000000..b299268
--- /dev/null
+++ b/test/issue38/poc.py
@@ -0,0 +1,9 @@
+#!/usr/bin/env python3
+
+import socket
+
+sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+sock.connect(("localhost", 8080))
+sock.send(b"GET"*5000+b" /hello"*5000+b" HTTP/1.1\r\nHost:localhost:8080\r\n\r\n")
+response = sock.recv(4096)
+sock.close()

Vulnerable code: 	CXXFLAGS += -fprofile-arcs -ftest-coverage
	LDFLAGS += -lgcov --coverage

    EpollContext *hc = (EpollContext *) td->event.data.ptr;
    if (hc != NULL) {
        hc->_ctx_status = CONTEXT_READ_OVER;
    }
    LOG_DEBUG("read from client: size:%d, content:%s", read_size, read_buffer);
Fixed code: 	CXXFLAGS += -fprofile-arcs -ftest-coverage -fsanitize=address
	LDFLAGS += -lgcov --coverage -fsanitize=address
    if (epoll_context != NULL) {
        epoll_context->_ctx_status = CONTEXT_READ_OVER;
    }
    LOG_DEBUG("read from client: size:%d, content:%.4096s", read_size, read_buffer);
#!/usr/bin/env python3

import socket

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(("localhost", 8080))
sock.send(b"GET"*5000+b" /hello"*5000+b" HTTP/1.1\r\nHost:localhost:8080\r\n\r\n")
response = sock.recv(4096)
sock.close()