Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/codeql-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@ jobs:

- name: Install libmicrohttpd dependency
run: |
curl https://s3.amazonaws.com/libhttpserver/libmicrohttpd_releases/libmicrohttpd-0.9.64.tar.gz -o libmicrohttpd-0.9.64.tar.gz ;
tar -xzf libmicrohttpd-0.9.64.tar.gz ;
cd libmicrohttpd-0.9.64 ;
curl https://s3.amazonaws.com/libhttpserver/libmicrohttpd_releases/libmicrohttpd-1.0.3.tar.gz -o libmicrohttpd-1.0.3.tar.gz ;
tar -xzf libmicrohttpd-1.0.3.tar.gz ;
cd libmicrohttpd-1.0.3 ;
./configure --disable-examples ;
make ;
sudo make install ;
Expand Down
42 changes: 21 additions & 21 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -78,21 +78,21 @@ jobs:
id: cache-libmicrohttpd
uses: actions/cache@v4
with:
path: libmicrohttpd-0.9.77
key: ubuntu-latest-gcc-libmicrohttpd-0.9.77-pre-built-v2
path: libmicrohttpd-1.0.3
key: ubuntu-latest-gcc-libmicrohttpd-1.0.3-pre-built-v2

- name: Build libmicrohttpd (if not cached)
if: steps.cache-libmicrohttpd.outputs.cache-hit != 'true'
run: |
curl https://s3.amazonaws.com/libhttpserver/libmicrohttpd_releases/libmicrohttpd-0.9.77.tar.gz -o libmicrohttpd-0.9.77.tar.gz
tar -xzf libmicrohttpd-0.9.77.tar.gz
cd libmicrohttpd-0.9.77
curl https://s3.amazonaws.com/libhttpserver/libmicrohttpd_releases/libmicrohttpd-1.0.3.tar.gz -o libmicrohttpd-1.0.3.tar.gz
tar -xzf libmicrohttpd-1.0.3.tar.gz
cd libmicrohttpd-1.0.3
./configure --disable-examples
make

- name: Install libmicrohttpd
run: |
cd libmicrohttpd-0.9.77
cd libmicrohttpd-1.0.3
sudo make install
sudo ldconfig

Expand Down Expand Up @@ -130,21 +130,21 @@ jobs:
id: cache-libmicrohttpd
uses: actions/cache@v4
with:
path: libmicrohttpd-0.9.77
key: ubuntu-latest-gcc-libmicrohttpd-0.9.77-pre-built-v2
path: libmicrohttpd-1.0.3
key: ubuntu-latest-gcc-libmicrohttpd-1.0.3-pre-built-v2

- name: Build libmicrohttpd (if not cached)
if: steps.cache-libmicrohttpd.outputs.cache-hit != 'true'
run: |
curl https://s3.amazonaws.com/libhttpserver/libmicrohttpd_releases/libmicrohttpd-0.9.77.tar.gz -o libmicrohttpd-0.9.77.tar.gz
tar -xzf libmicrohttpd-0.9.77.tar.gz
cd libmicrohttpd-0.9.77
curl https://s3.amazonaws.com/libhttpserver/libmicrohttpd_releases/libmicrohttpd-1.0.3.tar.gz -o libmicrohttpd-1.0.3.tar.gz
tar -xzf libmicrohttpd-1.0.3.tar.gz
cd libmicrohttpd-1.0.3
./configure --disable-examples
make

- name: Install libmicrohttpd
run: |
cd libmicrohttpd-0.9.77
cd libmicrohttpd-1.0.3
sudo make install
sudo ldconfig

Expand Down Expand Up @@ -181,21 +181,21 @@ jobs:
id: cache-libmicrohttpd
uses: actions/cache@v4
with:
path: libmicrohttpd-0.9.77
key: macos-latest-gcc-libmicrohttpd-0.9.77-pre-built-v2
path: libmicrohttpd-1.0.3
key: macos-latest-gcc-libmicrohttpd-1.0.3-pre-built-v2

- name: Build libmicrohttpd (if not cached)
if: steps.cache-libmicrohttpd.outputs.cache-hit != 'true'
run: |
curl https://s3.amazonaws.com/libhttpserver/libmicrohttpd_releases/libmicrohttpd-0.9.77.tar.gz -o libmicrohttpd-0.9.77.tar.gz
tar -xzf libmicrohttpd-0.9.77.tar.gz
cd libmicrohttpd-0.9.77
curl https://s3.amazonaws.com/libhttpserver/libmicrohttpd_releases/libmicrohttpd-1.0.3.tar.gz -o libmicrohttpd-1.0.3.tar.gz
tar -xzf libmicrohttpd-1.0.3.tar.gz
cd libmicrohttpd-1.0.3
./configure --disable-examples
make

- name: Install libmicrohttpd
run: |
cd libmicrohttpd-0.9.77
cd libmicrohttpd-1.0.3
sudo make install

- name: Fetch curl from cache
Expand Down Expand Up @@ -263,9 +263,9 @@ jobs:

- name: Build and install libmicrohttpd
run: |
curl https://s3.amazonaws.com/libhttpserver/libmicrohttpd_releases/libmicrohttpd-0.9.77.tar.gz -o libmicrohttpd-0.9.77.tar.gz
tar -xzf libmicrohttpd-0.9.77.tar.gz
cd libmicrohttpd-0.9.77
curl https://s3.amazonaws.com/libhttpserver/libmicrohttpd_releases/libmicrohttpd-1.0.3.tar.gz -o libmicrohttpd-1.0.3.tar.gz
tar -xzf libmicrohttpd-1.0.3.tar.gz
cd libmicrohttpd-1.0.3
./configure --disable-examples --enable-poll=no
make
make install
Expand Down
38 changes: 19 additions & 19 deletions .github/workflows/verify-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -511,30 +511,30 @@ jobs:
id: cache-libmicrohttpd
uses: actions/cache@v4
with:
path: libmicrohttpd-0.9.77
key: ${{ matrix.os }}-${{ matrix.c-compiler }}-libmicrohttpd-0.9.77-pre-built-v2
path: libmicrohttpd-1.0.3
key: ${{ matrix.os }}-${{ matrix.c-compiler }}-libmicrohttpd-1.0.3-pre-built-v2
if: ${{ matrix.os-type != 'windows' && matrix.build-type != 'no-dauth' && matrix.compiler-family != 'arm-cross' }}

- name: Build libmicrohttpd dependency (if not cached)
run: |
curl https://s3.amazonaws.com/libhttpserver/libmicrohttpd_releases/libmicrohttpd-0.9.77.tar.gz -o libmicrohttpd-0.9.77.tar.gz ;
tar -xzf libmicrohttpd-0.9.77.tar.gz ;
cd libmicrohttpd-0.9.77 ;
curl https://s3.amazonaws.com/libhttpserver/libmicrohttpd_releases/libmicrohttpd-1.0.3.tar.gz -o libmicrohttpd-1.0.3.tar.gz ;
tar -xzf libmicrohttpd-1.0.3.tar.gz ;
cd libmicrohttpd-1.0.3 ;
./configure --disable-examples ;
make ;
if: ${{ matrix.os-type != 'windows' && matrix.build-type != 'no-dauth' && matrix.compiler-family != 'arm-cross' && steps.cache-libmicrohttpd.outputs.cache-hit != 'true' }}

- name: Build libmicrohttpd without digest auth (no-dauth test)
run: |
curl https://s3.amazonaws.com/libhttpserver/libmicrohttpd_releases/libmicrohttpd-0.9.77.tar.gz -o libmicrohttpd-0.9.77.tar.gz ;
tar -xzf libmicrohttpd-0.9.77.tar.gz ;
cd libmicrohttpd-0.9.77 ;
curl https://s3.amazonaws.com/libhttpserver/libmicrohttpd_releases/libmicrohttpd-1.0.3.tar.gz -o libmicrohttpd-1.0.3.tar.gz ;
tar -xzf libmicrohttpd-1.0.3.tar.gz ;
cd libmicrohttpd-1.0.3 ;
./configure --disable-examples --disable-dauth ;
make ;
if: ${{ matrix.build-type == 'no-dauth' }}

- name: Install libmicrohttpd
run: cd libmicrohttpd-0.9.77 ; sudo make install ;
run: cd libmicrohttpd-1.0.3 ; sudo make install ;
if: ${{ matrix.os-type != 'windows' && matrix.compiler-family != 'arm-cross' }}

- name: Verify digest auth is disabled (no-dauth test)
Expand All @@ -550,9 +550,9 @@ jobs:
- name: Build and install libmicrohttpd (Windows)
if: ${{ matrix.os-type == 'windows' }}
run: |
curl https://s3.amazonaws.com/libhttpserver/libmicrohttpd_releases/libmicrohttpd-0.9.77.tar.gz -o libmicrohttpd-0.9.77.tar.gz
tar -xzf libmicrohttpd-0.9.77.tar.gz
cd libmicrohttpd-0.9.77
curl https://s3.amazonaws.com/libhttpserver/libmicrohttpd_releases/libmicrohttpd-1.0.3.tar.gz -o libmicrohttpd-1.0.3.tar.gz
tar -xzf libmicrohttpd-1.0.3.tar.gz
cd libmicrohttpd-1.0.3
./configure --disable-examples --enable-poll=no
make
make install
Expand All @@ -561,16 +561,16 @@ jobs:
id: cache-libmicrohttpd-arm
uses: actions/cache@v4
with:
path: libmicrohttpd-0.9.77-${{ matrix.build-type }}
key: ${{ matrix.os }}-${{ matrix.build-type }}-libmicrohttpd-0.9.77-cross-compiled
path: libmicrohttpd-1.0.3-${{ matrix.build-type }}
key: ${{ matrix.os }}-${{ matrix.build-type }}-libmicrohttpd-1.0.3-cross-compiled
if: ${{ matrix.compiler-family == 'arm-cross' }}

- name: Cross-compile libmicrohttpd for ARM
run: |
curl https://s3.amazonaws.com/libhttpserver/libmicrohttpd_releases/libmicrohttpd-0.9.77.tar.gz -o libmicrohttpd-0.9.77.tar.gz
tar -xzf libmicrohttpd-0.9.77.tar.gz
mv libmicrohttpd-0.9.77 libmicrohttpd-0.9.77-${{ matrix.build-type }}
cd libmicrohttpd-0.9.77-${{ matrix.build-type }}
curl https://s3.amazonaws.com/libhttpserver/libmicrohttpd_releases/libmicrohttpd-1.0.3.tar.gz -o libmicrohttpd-1.0.3.tar.gz
tar -xzf libmicrohttpd-1.0.3.tar.gz
mv libmicrohttpd-1.0.3 libmicrohttpd-1.0.3-${{ matrix.build-type }}
cd libmicrohttpd-1.0.3-${{ matrix.build-type }}
mkdir -p ${{ github.workspace }}/arm-sysroot
if [ "${{ matrix.build-type }}" = "arm32" ]; then
./configure --host=arm-linux-gnueabihf --prefix=${{ github.workspace }}/arm-sysroot --disable-examples --disable-doc
Expand All @@ -583,7 +583,7 @@ jobs:

- name: Install cross-compiled libmicrohttpd from cache
run: |
cd libmicrohttpd-0.9.77-${{ matrix.build-type }}
cd libmicrohttpd-1.0.3-${{ matrix.build-type }}
mkdir -p ${{ github.workspace }}/arm-sysroot
make install
if: ${{ matrix.compiler-family == 'arm-cross' && steps.cache-libmicrohttpd-arm.outputs.cache-hit == 'true' }}
Expand Down
3 changes: 3 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ Version 0.20.0
Fixed auth skip path bypass via path traversal (e.g. /public/../protected).
Fixed use of free() instead of MHD_free() for digest auth username.
Fixed unchecked write error during file upload.
Fixed std::terminate when MHD invokes the URI log callback with a
null uri pointer (e.g. port scans, half-open connections, or
non-HTTP traffic). Resolves issue #371.

Version 0.19.0 - 2023-06-15

Expand Down
7 changes: 6 additions & 1 deletion src/webserver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -626,7 +626,12 @@ void* uri_log(void* cls, const char* uri, struct MHD_Connection *con) {
std::ignore = con;

auto mr = std::make_unique<details::modded_request>();
mr->complete_uri = uri;
// MHD may invoke this callback with a null uri before the request line
// has been parsed (e.g. port scans, half-open connections, or non-HTTP
// traffic on the listening port). Treat that as an empty URI so the
// std::string assignment does not throw std::logic_error and abort the
// process via std::terminate. See issue #371.
mr->complete_uri = (uri != nullptr) ? uri : "";
return reinterpret_cast<void*>(mr.release());
}

Expand Down
7 changes: 6 additions & 1 deletion test/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ LDADD += -lcurl

AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/src/httpserver/
METASOURCES = AUTO
check_PROGRAMS = basic file_upload http_utils threaded nodelay string_utilities http_endpoint ban_system ws_start_stop authentication deferred http_resource http_response create_webserver
check_PROGRAMS = basic file_upload http_utils threaded nodelay string_utilities http_endpoint ban_system ws_start_stop authentication deferred http_resource http_response create_webserver uri_log

MOSTLYCLEANFILES = *.gcda *.gcno *.gcov

Expand All @@ -44,6 +44,11 @@ nodelay_SOURCES = integ/nodelay.cpp
http_resource_SOURCES = unit/http_resource_test.cpp
http_response_SOURCES = unit/http_response_test.cpp
create_webserver_SOURCES = unit/create_webserver_test.cpp
uri_log_SOURCES = unit/uri_log_test.cpp
# uri_log_test directly references libmicrohttpd via ~modded_request(), so
# it needs an explicit -lmicrohttpd in its link line on top of the default
# LDADD (modern ld enforces --no-copy-dt-needed-entries).
uri_log_LDADD = $(LDADD) -lmicrohttpd

noinst_HEADERS = littletest.hpp
AM_CXXFLAGS += -Wall -fPIC -Wno-overloaded-virtual
Expand Down
86 changes: 86 additions & 0 deletions test/unit/uri_log_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
This file is part of libhttpserver
Copyright (C) 2011-2019 Sebastiano Merlino

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
USA
*/

#include <string>

#include "./httpserver.hpp"
#include "httpserver/details/modded_request.hpp"

#include "./littletest.hpp"

// uri_log is the MHD URI-log callback defined in src/webserver.cpp. It is
// exported from the library but has no public header, so we re-declare its
// signature here. MHD_Connection is opaque to this test - we only ever pass
// nullptr, mirroring how MHD itself may invoke the callback before the
// connection is fully initialised.
namespace httpserver {
void* uri_log(void* cls, const char* uri, struct MHD_Connection* con);
} // namespace httpserver

LT_BEGIN_SUITE(uri_log_suite)
void set_up() {
}

void tear_down() {
}
LT_END_SUITE(uri_log_suite)

// Regression test for issue #371: under load (port scans, half-open
// connections, non-HTTP traffic on the listening port) MHD may invoke the
// URI-log callback with a null uri pointer before the request line has
// been parsed. The previous implementation assigned the raw pointer into
// std::string, which throws std::logic_error and aborts the process via
// std::terminate because the throw escapes a C callback.
LT_BEGIN_AUTO_TEST(uri_log_suite, null_uri_does_not_throw)
void* raw = nullptr;
LT_CHECK_NOTHROW(raw = httpserver::uri_log(nullptr, nullptr, nullptr));
LT_CHECK(raw != nullptr);

auto* mr = static_cast<httpserver::details::modded_request*>(raw);
LT_CHECK_EQ(mr->complete_uri, std::string(""));
delete mr;
LT_END_AUTO_TEST(null_uri_does_not_throw)

// Sanity check that the happy path still records the URI as before.
LT_BEGIN_AUTO_TEST(uri_log_suite, valid_uri_is_stored)
const char* uri = "/some/path?with=query";
void* raw = httpserver::uri_log(nullptr, uri, nullptr);
LT_CHECK(raw != nullptr);

auto* mr = static_cast<httpserver::details::modded_request*>(raw);
LT_CHECK_EQ(mr->complete_uri, std::string(uri));
delete mr;
LT_END_AUTO_TEST(valid_uri_is_stored)

// Empty (but non-null) URI should be stored verbatim - this is the same
// observable state the null-uri path now produces, so route matching falls
// through to a 404 in both cases.
LT_BEGIN_AUTO_TEST(uri_log_suite, empty_uri_is_stored)
void* raw = httpserver::uri_log(nullptr, "", nullptr);
LT_CHECK(raw != nullptr);

auto* mr = static_cast<httpserver::details::modded_request*>(raw);
LT_CHECK_EQ(mr->complete_uri, std::string(""));
delete mr;
LT_END_AUTO_TEST(empty_uri_is_stored)

LT_BEGIN_AUTO_TEST_ENV()
AUTORUN_TESTS()
LT_END_AUTO_TEST_ENV()
Loading