ithewei / libhv

🔥 比libevent/libuv/asio更易用的网络库。A c/c++ network library for developing TCP/UDP/SSL/HTTP/WebSocket/MQTT client/server.

Home Page:https://github.com/ithewei/libhv/wiki

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Buffer overflow when using reverse proxy via HttpService::Proxy

fnMrRice opened this issue · comments

  • Version: vcpkg-v1.3.2
  • Appearance: Sometimes reverse proxy may send invalid content to back server. With AddressSanitizer it would abort the program.

AddressSanitizer log:

==262128==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x50c000030777 at pc 0x7ffff796e003 bp 0x7fffe6e88c90 sp 0x7fffe6e88438
READ of size 524 at 0x50c000030777 thread T12
    #0 0x7ffff796e002 in __interceptor_memcpy /usr/src/debug/gcc/gcc/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:899
    #1 0x7ffff77eb786 in std::char_traits<char>::copy(char*, char const*, unsigned long) /usr/src/debug/gcc/gcc-build/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/char_traits.h:445
    #2 0x7ffff77eb786 in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_S_copy(char*, char const*, unsigned long) /usr/src/debug/gcc/gcc-build/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/basic_string.h:420
    #3 0x7ffff77eb786 in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_S_copy(char*, char const*, unsigned long) /usr/src/debug/gcc/gcc-build/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/basic_string.h:415
    #4 0x7ffff77eb786 in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_mutate(unsigned long, unsigned long, char const*, unsigned long) /usr/src/debug/gcc/gcc-build/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/basic_string.tcc:333
    #5 0x7ffff77ed100 in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_append(char const*, unsigned long) /usr/src/debug/gcc/gcc-build/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/basic_string.tcc:419
    #6 0x5555557436dc in HttpMessage::DumpBody(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&) /opt/vcpkg/buildtrees/libhv/src/v1.3.2-990dc4029c.clean/http/HttpMessage.cpp:565
    #7 0x555555744fde in HttpRequest::Dump[abi:cxx11](bool, bool) /opt/vcpkg/buildtrees/libhv/src/v1.3.2-990dc4029c.clean/http/HttpMessage.cpp:789
    #8 0x5555557881bc in HttpHandler::sendProxyRequest() /opt/vcpkg/buildtrees/libhv/src/v1.3.2-990dc4029c.clean/http/server/HttpHandler.cpp:1085
    #9 0x555555787b23 in HttpHandler::connectProxy(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) /opt/vcpkg/buildtrees/libhv/src/v1.3.2-990dc4029c.clean/http/server/HttpHandler.cpp:1020
    #10 0x5555557879bb in HttpHandler::handleReverseProxy() /opt/vcpkg/buildtrees/libhv/src/v1.3.2-990dc4029c.clean/http/server/HttpHandler.cpp:1007
    #11 0x555555787887 in HttpHandler::handleProxy() /opt/vcpkg/buildtrees/libhv/src/v1.3.2-990dc4029c.clean/http/server/HttpHandler.cpp:990
    #12 0x555555783300 in HttpHandler::onHeadersComplete() /opt/vcpkg/buildtrees/libhv/src/v1.3.2-990dc4029c.clean/http/server/HttpHandler.cpp:265
    #13 0x555555782341 in operator() /opt/vcpkg/buildtrees/libhv/src/v1.3.2-990dc4029c.clean/http/server/HttpHandler.cpp:95
    #14 0x555555789579 in __invoke_impl<void, HttpHandler::Init(int)::<lambda(HttpMessage*, http_parser_state, char const*, size_t)>&, HttpMessage*, http_parser_state, char const*, long unsigned int> /usr/include/c++/13.2.1/bits/invoke.h:61
    #15 0x555555789056 in __invoke_r<void, HttpHandler::Init(int)::<lambda(HttpMessage*, http_parser_state, char const*, size_t)>&, HttpMessage*, http_parser_state, char const*, long unsigned int> /usr/include/c++/13.2.1/bits/invoke.h:150
    #16 0x555555788b7b in _M_invoke /usr/include/c++/13.2.1/bits/std_function.h:290
    #17 0x5555557a22da in std::function<void (HttpMessage*, http_parser_state, char const*, unsigned long)>::operator()(HttpMessage*, http_parser_state, char const*, unsigned long) const /usr/include/c++/13.2.1/bits/std_function.h:591
    #18 0x5555557a1fcd in Http1Parser::invokeHttpCb(char const*, unsigned long) /opt/vcpkg/buildtrees/libhv/src/v1.3.2-990dc4029c.clean/http/Http1Parser.h:130
    #19 0x5555557a17cf in on_headers_complete /opt/vcpkg/buildtrees/libhv/src/v1.3.2-990dc4029c.clean/http/Http1Parser.cpp:128
    #20 0x5555557a6d5c in http_parser_execute /opt/vcpkg/buildtrees/libhv/src/v1.3.2-990dc4029c.clean/http/http_parser.c:1800
    #21 0x5555557a1d04 in Http1Parser::FeedRecvData(char const*, unsigned long) /opt/vcpkg/buildtrees/libhv/src/v1.3.2-990dc4029c.clean/http/Http1Parser.h:53
    #22 0x555555785b8e in HttpHandler::FeedRecvData(char const*, unsigned long) /opt/vcpkg/buildtrees/libhv/src/v1.3.2-990dc4029c.clean/http/server/HttpHandler.cpp:700
    #23 0x55555576ff74 in on_recv /opt/vcpkg/buildtrees/libhv/src/v1.3.2-990dc4029c.clean/http/server/HttpServer.cpp:30
    #24 0x555555736ed5 in hio_read_cb /opt/vcpkg/buildtrees/libhv/src/v1.3.2-990dc4029c.clean/event/hevent.c:404
    #25 0x555555736d78 in hio_handle_read /opt/vcpkg/buildtrees/libhv/src/v1.3.2-990dc4029c.clean/event/hevent.c:371
    #26 0x55555573ca53 in __read_cb /opt/vcpkg/buildtrees/libhv/src/v1.3.2-990dc4029c.clean/event/nio.c:48
    #27 0x55555573d688 in nio_read /opt/vcpkg/buildtrees/libhv/src/v1.3.2-990dc4029c.clean/event/nio.c:332
    #28 0x55555573d940 in hio_handle_events /opt/vcpkg/buildtrees/libhv/src/v1.3.2-990dc4029c.clean/event/nio.c:410
    #29 0x555555739c09 in hloop_process_pendings /opt/vcpkg/buildtrees/libhv/src/v1.3.2-990dc4029c.clean/event/hloop.c:122
    #30 0x555555739e9f in hloop_process_events /opt/vcpkg/buildtrees/libhv/src/v1.3.2-990dc4029c.clean/event/hloop.c:185
    #31 0x55555573a9c3 in hloop_run /opt/vcpkg/buildtrees/libhv/src/v1.3.2-990dc4029c.clean/event/hloop.c:464
    #32 0x55555568fa72 in hv::EventLoop::run() /opt/vcpkg/buildtrees/libhv/src/v1.3.2-990dc4029c.clean/evpp/EventLoop.h:54
    #33 0x555555770755 in loop_thread /opt/vcpkg/buildtrees/libhv/src/v1.3.2-990dc4029c.clean/http/server/HttpServer.cpp:158
    #34 0x7ffff6e15559  (/usr/lib/libc.so.6+0x8b559) (BuildId: c0caa0b7709d3369ee575fcd7d7d0b0fc48733af)
    #35 0x7ffff6e92a3b  (/usr/lib/libc.so.6+0x108a3b) (BuildId: c0caa0b7709d3369ee575fcd7d7d0b0fc48733af)

I think the problem is that req was not parsed before calling Dump() in HttpHandler::sendProxyRequest()

possible fix:

diff --git a/http/server/HttpHandler.cpp b/http/server/HttpHandler.cpp
index 1a693b4..f02d360 100644
--- a/http/server/HttpHandler.cpp
+++ b/http/server/HttpHandler.cpp
@@ -1082,7 +1082,7 @@ EOF
     req->headers["Connection"] = keepalive ? "keep-alive" : "close";
     req->headers["X-Real-IP"] = ip;
     // NOTE: send head + received body
-    std::string msg = req->Dump(true, true);
+    std::string msg = req->Dump(true, false) + req->body;
     // printf("%s\n", msg.c_str());
     req->Reset();
 

Try this:

diff --git a/http/HttpMessage.h b/http/HttpMessage.h
index cc779c3..17ee731 100644
--- a/http/HttpMessage.h
+++ b/http/HttpMessage.h
@@ -297,6 +297,7 @@ public:
     void* Content() {
         if (content == NULL && body.size() != 0) {
             content = (void*)body.data();
+            content_length = body.size();
         }
         return content;
     }

Try this:

diff --git a/http/HttpMessage.h b/http/HttpMessage.h
index cc779c3..17ee731 100644
--- a/http/HttpMessage.h
+++ b/http/HttpMessage.h
@@ -297,6 +297,7 @@ public:
     void* Content() {
         if (content == NULL && body.size() != 0) {
             content = (void*)body.data();
+            content_length = body.size();
         }
         return content;
     }

It solved the buffer overflow, but the body was incorrect

for multipart/form-data,
here is the data sent by browser:

-----------------------------5125518413293710885640709111
Content-Disposition: form-data; name="index"

1
-----------------------------5125518413293710885640709111
Content-Disposition: form-data; name="name"

root
-----------------------------5125518413293710885640709111--

here is the data forwarded by libhv:

-----------------------------5125518413293710885640709111--
-----------------------------5125518413293710885640709111
Content-Disposition: form-data; name="index"

1
-----------------------------5125518413293710885640709111
Content-Disposition: form-data; name="name"

root

for application/json, body would be appended by a string "null" like this:
null{"index":1, .....}

I know why. When the http header is received, a proxy connection will be made. If it is connected immediately, req.body will be empty at this time, but DumpBody will automatically generate the body based on the Content-Type header, resulting in this kind of bad case, your fix + my fix should be a relatively complete solution. Can you submit a PR to fix it?