yhirose/cpp-httplib

double free or corruption

Kracken256 opened this issue · 1 comments

Double free or corruption in the SSL Subsystem.

  • This bug is occurring in a multi-threaded application with 100+ connections per second.
  • This build was running in a standard Alpine Linux Docker container.
  • My app only runs on Linux. It is unknown if this happens on windows.
  • This appears to be a double-free bug. It is possible (but not confirmed) that this could result in an arbitrary write primitive yielding arbitrary code execution by remote adversaries.
  • This bug is common and recurrent. It appears to occur within 10 seconds when routing many (100+) connections.
  • I have triggered this bug 10 times in a row yielding the same stack trace.
  • I am using C++20
  • Operating OS: Linux e85bf220472e 5.15.0-107-generic #117-Ubuntu SMP Fri Apr 26 12:26:49 UTC 2024 x86_64 Linux
get_meta (p=p@entry=0x7f1e93358ba0 "localhost") at src/malloc/mallocng/meta.h:141
warning: 141    src/malloc/mallocng/meta.h: No such file or directory
(gdb) bt
#0  get_meta (p=p@entry=0x7f1e93358ba0 "localhost") at src/malloc/mallocng/meta.h:141
#1  0x00007f1e93f13bbe in __libc_free (p=0x7f1e93358ba0) at src/malloc/mallocng/free.c:105
#2  0x00007f1e93a7110a in SSL_free () from /lib/libssl.so.3
#3  0x000055759c1bc027 in httplib::detail::ssl_delete (ctx_mutex=..., ssl=0x7f1e9332e080, shutdown_gracefully=false) at /app/dsfcontrol-agent/src/api/httplib.hh:9029
#4  0x000055759c1bca89 in httplib::SSLServer::process_and_close_socket (this=0x7f1e935cb090, sock=24) at /app/dsfcontrol-agent/src/api/httplib.hh:9306
#5  0x000055759c1b7c5a in httplib::Server::listen_internal()::{lambda()#2}::operator()() const (__closure=0x7f1e9357d950) at /app/dsfcontrol-agent/src/api/httplib.hh:6944
#6  0x000055759c1e54b0 in std::__invoke_impl<void, httplib::Server::listen_internal()::{lambda()#2}&>(std::__invoke_other, httplib::Server::listen_internal()::{lambda()#2}&) (__f=...)
    at /usr/include/c++/13.2.1/bits/invoke.h:61
#7  0x000055759c1dd98b in std::__invoke_r<void, httplib::Server::listen_internal()::{lambda()#2}&>(httplib::Server::listen_internal()::{lambda()#2}&) (__fn=...) at /usr/include/c++/13.2.1/bits/invoke.h:111
#8  0x000055759c1d3c48 in std::_Function_handler<void (), httplib::Server::listen_internal()::{lambda()#2}>::_M_invoke(std::_Any_data const&) (__functor=...) at /usr/include/c++/13.2.1/bits/std_function.h:290
#9  0x000055759c1bf780 in std::function<void ()>::operator()() const (this=0x7f1e9357d950) at /usr/include/c++/13.2.1/bits/std_function.h:591
#10 0x000055759c1a8851 in httplib::ThreadPool::worker::operator() (this=0x7f1e935d0128) at /app/dsfcontrol-agent/src/api/httplib.hh:733
#11 0x000055759c2156a0 in std::__invoke_impl<void, httplib::ThreadPool::worker> (__f=...) at /usr/include/c++/13.2.1/bits/invoke.h:61
--Type <RET> for more, q to quit, c to continue without paging--
#12 0x000055759c215663 in std::__invoke<httplib::ThreadPool::worker> (__fn=...) at /usr/include/c++/13.2.1/bits/invoke.h:96
#13 0x000055759c21559e in std::thread::_Invoker<std::tuple<httplib::ThreadPool::worker> >::_M_invoke<0ul> (this=0x7f1e935d0128) at /usr/include/c++/13.2.1/bits/std_thread.h:292
#14 0x000055759c21552e in std::thread::_Invoker<std::tuple<httplib::ThreadPool::worker> >::operator() (this=0x7f1e935d0128) at /usr/include/c++/13.2.1/bits/std_thread.h:299
#15 0x000055759c2154de in std::thread::_State_impl<std::thread::_Invoker<std::tuple<httplib::ThreadPool::worker> > >::_M_run (this=0x7f1e935d0120) at /usr/include/c++/13.2.1/bits/std_thread.h:244
#16 0x00007f1e937bc1bf in ?? () from /usr/lib/libstdc++.so.6
#17 0x00007f1e93f4b22e in start (p=0x7f1e9357da80) at src/thread/pthread_create.c:207
#18 0x00007f1e93f4d82f in __clone () at src/thread/x86_64/clone.s:22
Backtrace stopped: frame did not save the PC
(gdb) 

False alarm. cpp-httplib does not appear to have an issue here.

I had a bug in my code of the form:

list = (char **)malloc(sizeof(char *) * size + 1);

Where it should have been:

list = (char **)malloc(sizeof(char *) * (size + 1));

This heap-memory overwrite ended up causing the double-free/corruption in the web-server component.