Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add HTTP/3 fuzzing harness #97

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 6 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
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ jobs:
- curl_fuzzer_gopher
- curl_fuzzer_http
- curl_fuzzer_https
- curl_fuzzer_http3
- curl_fuzzer_imap
- curl_fuzzer_ldap
- curl_fuzzer_mqtt
Expand Down
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ missing
/curl_fuzzer_http_seed_corpus.zip
/curl_fuzzer_https
/curl_fuzzer_https_seed_corpus.zip
/curl_fuzzer_http3
/curl_fuzzer_http3_seed_corpus.zip
/curl_fuzzer_imap
/curl_fuzzer_imap_seed_corpus.zip
/curl_fuzzer_ldap
Expand All @@ -62,3 +64,7 @@ missing
/curl_fuzzer_smtp_seed_corpus.zip
/curl_fuzzer_tftp
/curl_fuzzer_tftp_seed_corpus.zip
/curl_fuzzer_ws
/curl_fuzzer_ws_seed_corpus.zip
/fuzz_url
/fuzz_url.zip
4 changes: 4 additions & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ FUZZPROGS = curl_fuzzer \
curl_fuzzer_gopher \
curl_fuzzer_http \
curl_fuzzer_https \
curl_fuzzer_http3 \
curl_fuzzer_imap \
curl_fuzzer_ldap \
curl_fuzzer_mqtt \
Expand Down Expand Up @@ -89,6 +90,9 @@ curl_fuzzer_http_LDADD = $(COMMON_LDADD)
curl_fuzzer_https_SOURCES = $(COMMON_SOURCES)
curl_fuzzer_https_CXXFLAGS = $(COMMON_FLAGS) -DFUZZ_PROTOCOLS_HTTPS
curl_fuzzer_https_LDADD = $(COMMON_LDADD)
curl_fuzzer_http3_SOURCES = $(COMMON_SOURCES)
curl_fuzzer_http3_CXXFLAGS = $(COMMON_FLAGS) -DFUZZ_PROTOCOLS_HTTP3 -DFUZZ_PROTOCOLS_HTTPS
curl_fuzzer_http3_LDADD = $(COMMON_LDADD)
curl_fuzzer_imap_SOURCES = $(COMMON_SOURCES)
curl_fuzzer_imap_CXXFLAGS = $(COMMON_FLAGS) -DFUZZ_PROTOCOLS_IMAP
curl_fuzzer_imap_LDADD = $(COMMON_LDADD)
Expand Down
Binary file added corpora/curl_fuzzer_http3/test_simple_httppost
Binary file not shown.
12 changes: 12 additions & 0 deletions curl_fuzzer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -207,8 +207,10 @@ int fuzz_set_easy_options(FUZZ_DATA *fuzz)
/* Set the hsts header cache filepath so that it can be fuzzed. */
FTRY(curl_easy_setopt(fuzz->easy, CURLOPT_HSTS, FUZZ_HSTS_HEADER_CACHE_PATH));

#ifndef FUZZ_PROTOCOLS_HTTPS
/* Set the Certificate Revocation List file path so it can be fuzzed */
FTRY(curl_easy_setopt(fuzz->easy, CURLOPT_CRLFILE, FUZZ_CRL_FILE_PATH));
#endif

/* Set the .netrc file path so it can be fuzzed */
FTRY(curl_easy_setopt(fuzz->easy, CURLOPT_NETRC_FILE, FUZZ_NETRC_FILE_PATH));
Expand Down Expand Up @@ -571,6 +573,16 @@ int fuzz_set_allowed_protocols(FUZZ_DATA *fuzz)

FTRY(curl_easy_setopt(fuzz->easy, CURLOPT_PROTOCOLS_STR, allowed_protocols));

#ifdef FUZZ_PROTOCOLS_HTTP3
/* If we are fuzzing HTTP3, then we must be fuzzing the HTTPS protocol */
#ifndef FUZZ_PROTOCOLS_HTTPS
rc = 255;
goto EXIT_LABEL;
#endif

FTRY(curl_easy_setopt(fuzz->easy, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_3ONLY));
#endif

EXIT_LABEL:

return rc;
Expand Down
9 changes: 8 additions & 1 deletion curl_fuzzer_callback.cc
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,14 @@ curl_socket_t fuzz_open_socket(void *ptr,
sman->index,
sman->index);

if(socketpair(AF_UNIX, SOCK_STREAM, 0, fds)) {

#ifdef FUZZ_PROTOCOLS_HTTP3
const int socket_type = SOCK_DGRAM;
#else
const int socket_type = SOCK_STREAM;
#endif

if(socketpair(AF_UNIX, socket_type, 0, fds)) {
/* Failed to create a pair of sockets. */
return CURL_SOCKET_BAD;
}
Expand Down
26 changes: 20 additions & 6 deletions mainline.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,26 @@ SCRIPTDIR=${BUILD_ROOT}/scripts

CURLDIR=/tmp/curl
OPENSSLDIR=/tmp/openssl
NGHTTPDIR=/tmp/nghttp2
NGHTTP2DIR=/tmp/nghttp2
NGHTTP3DIR=/tmp/nghttp3
NGTCP2DIR=/tmp/ngtcp2
OPENSLLQUICDIR=/tmp/openssl_quic
cmeister2 marked this conversation as resolved.
Show resolved Hide resolved
INSTALLDIR=/tmp/curl_install

# Parse the options.
OPTIND=1

while getopts "c:n:o:" opt
while getopts "c:n:o:t:" opt
do
case "$opt" in
c) CURLDIR=$OPTARG
;;
n) NGHTTPDIR=$OPTARG
n) NGHTTP2DIR=$OPTARG
;;
o) OPENSSLDIR=$OPTARG
;;
t) NGTCP2DIR=$OPTARG
;;
esac
done
shift $((OPTIND-1))
Expand All @@ -34,13 +39,22 @@ FUZZ_FLAG="-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION"
export CFLAGS="-fsanitize=address"
export CXXFLAGS="-fsanitize=address -stdlib=libstdc++ $FUZZ_FLAG"
export CPPFLAGS="$FUZZ_FLAG"
export OPENSSLFLAGS="-fno-sanitize=alignment"
export OPENSSLFLAGS="-fno-sanitize=alignment -lstdc++"

# Install openssl_quic (need openssl_quic, nghttp3, and ngtcp2 for HTTP3 support)
${SCRIPTDIR}/handle_x.sh openssl_quic ${OPENSLLQUICDIR} ${INSTALLDIR} || exit 1

# Install openssl
${SCRIPTDIR}/handle_x.sh openssl ${OPENSSLDIR} ${INSTALLDIR} || exit 1
# ${SCRIPTDIR}/handle_x.sh openssl ${OPENSSLDIR} ${INSTALLDIR} || exit 1
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is the solution, commenting this out is wrong. Remove the block altogether.


# Install nghttp2
${SCRIPTDIR}/handle_x.sh nghttp2 ${NGHTTPDIR} ${INSTALLDIR} || exit 1
${SCRIPTDIR}/handle_x.sh nghttp2 ${NGHTTP2DIR} ${INSTALLDIR} || exit 1

# Install nghttp3
${SCRIPTDIR}/handle_x.sh nghttp3 ${NGHTTP3DIR} ${INSTALLDIR} || exit 1

# Install ngtcp2
${SCRIPTDIR}/handle_x.sh ngtcp2 ${NGTCP2DIR} ${INSTALLDIR} || exit 1

# Install curl after all other dependencies
${SCRIPTDIR}/handle_x.sh curl ${CURLDIR} ${INSTALLDIR} || exit 1
Expand Down
17 changes: 13 additions & 4 deletions ossfuzz.sh
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ SCRIPTDIR=${BUILD_ROOT}/scripts

ZLIBDIR=/src/zlib
OPENSSLDIR=/src/openssl
NGHTTPDIR=/src/nghttp2
NGHTTP2DIR=/src/nghttp2
NGHTTP3DIR=/src/nghttp3
NGTCP2DIR=/src/ngtcp2
GDBDIR=/src/gdb

# Check for GDB-specific behaviour by checking for the GDBMODE flag.
Expand Down Expand Up @@ -74,13 +76,20 @@ ${SCRIPTDIR}/handle_x.sh zlib ${ZLIBDIR} ${INSTALLDIR} || exit 1
# affect (see 16697, 17624)
if [[ ${SANITIZER} != "memory" ]]
then
# Install openssl
# Install openssl_quic (need openssl_quic, nghttp3, and ngtcp2 for HTTP3 support)
export OPENSSLFLAGS="-fno-sanitize=alignment"
${SCRIPTDIR}/handle_x.sh openssl ${OPENSSLDIR} ${INSTALLDIR} || exit 1
${SCRIPTDIR}/handle_x.sh openssl_quic ${OPENSSLDIR} ${INSTALLDIR} || exit 1

# HTTP3 requires SSL, so we also install it here
# Install nghttp3
${SCRIPTDIR}/handle_x.sh nghttp3 ${NGHTTP3DIR} ${INSTALLDIR} || exit 1

# Install ngtcp2
${SCRIPTDIR}/handle_x.sh ngtcp2 ${NGTCP2DIR} ${INSTALLDIR} || exit 1
fi

# Install nghttp2
${SCRIPTDIR}/handle_x.sh nghttp2 ${NGHTTPDIR} ${INSTALLDIR} || exit 1
${SCRIPTDIR}/handle_x.sh nghttp2 ${NGHTTP2DIR} ${INSTALLDIR} || exit 1

# Compile curl
${SCRIPTDIR}/install_curl.sh /src/curl ${INSTALLDIR}
Expand Down
6 changes: 6 additions & 0 deletions scripts/download_nghttp3.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/bin/bash

# If any commands fail, fail the script immediately.
set -ex

git clone --depth 1 --branch v1.1.0 https://github.com/ngtcp2/nghttp3 $1
cmeister2 marked this conversation as resolved.
Show resolved Hide resolved
88 changes: 88 additions & 0 deletions scripts/download_ngtcp2.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
#!/bin/bash

# If any commands fail, fail the script immediately.
set -ex

# Clone the repository to the specified directory.
git clone --depth 1 --branch v1.1.0 https://github.com/ngtcp2/ngtcp2 $1
cmeister2 marked this conversation as resolved.
Show resolved Hide resolved

# Teach ngtcp2 about sockets
pushd $1
patch -p1 <<'EOF'
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inlining the patch here is nasty. I'd rather this were in a patch file.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, doing this via a patch mechanism severely limits updating this dependency in the future. What's the long-term plan here - add support to ngtcp2 upstream?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, having the patch here is not a permanent solution. The long-term plan would be to work with ngtcp2 to make it more fuzzing friendly -- e.g. by allowing it to work with AF_UNIX sockets like this patch does.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bagder do you have an opinion here? In my experience this kind of mechanism is fairly brittle; I don't know if it's something we want to support moving forwards.

From 52690d08112ccd9a5c01b5991735bf3f8e5121b0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Emilio=20L=C3=B3pez?= <[email protected]>
Date: Sat, 16 Dec 2023 21:33:17 +0000
Subject: [PATCH] Teach ngtcp2 about sockets

---
crypto/shared.c | 4 ++++
lib/includes/ngtcp2/ngtcp2.h | 5 +++++
lib/ngtcp2_addr.c | 8 ++++++++
3 files changed, 17 insertions(+)

diff --git a/crypto/shared.c b/crypto/shared.c
index 162094a3..08c9ed44 100644
--- a/crypto/shared.c
+++ b/crypto/shared.c
@@ -1096,6 +1096,10 @@ static size_t crypto_generate_regular_token_aad(uint8_t *dest,
(const uint8_t *)&((const ngtcp2_sockaddr_in6 *)(void *)sa)->sin6_addr;
addrlen = sizeof(((const ngtcp2_sockaddr_in6 *)(void *)sa)->sin6_addr);
break;
+ case NGTCP2_AF_UNIX:
+ addr = NULL;
+ addrlen = 0;
+ break;
default:
assert(0);
abort();
diff --git a/lib/includes/ngtcp2/ngtcp2.h b/lib/includes/ngtcp2/ngtcp2.h
index a8d4b4af..63958e4c 100644
--- a/lib/includes/ngtcp2/ngtcp2.h
+++ b/lib/includes/ngtcp2/ngtcp2.h
@@ -1235,6 +1235,10 @@ typedef struct ngtcp2_pkt_stateless_reset {
# error NGTCP2_AF_INET6 must be defined
# endif /* !NGTCP2_AF_INET6 */

+# ifndef NGTCP2_AF_UNIX
+# error NGTCP2_AF_UNIX must be defined
+# endif /* !NGTCP2_AF_UNIX */
+
typedef unsigned short int ngtcp2_sa_family;
typedef uint16_t ngtcp2_in_port;

@@ -1270,6 +1274,7 @@ typedef uint32_t ngtcp2_socklen;
#else /* !NGTCP2_USE_GENERIC_SOCKADDR */
# define NGTCP2_AF_INET AF_INET
# define NGTCP2_AF_INET6 AF_INET6
+# define NGTCP2_AF_UNIX AF_UNIX

/**
* @typedef
diff --git a/lib/ngtcp2_addr.c b/lib/ngtcp2_addr.c
index f389abe7..26b57f3d 100644
--- a/lib/ngtcp2_addr.c
+++ b/lib/ngtcp2_addr.c
@@ -67,6 +67,10 @@ static int sockaddr_eq(const ngtcp2_sockaddr *a, const ngtcp2_sockaddr *b) {
return ai->sin6_port == bi->sin6_port &&
memcmp(&ai->sin6_addr, &bi->sin6_addr, sizeof(ai->sin6_addr)) == 0;
}
+ case NGTCP2_AF_UNIX: {
+ // TODO: see what makes sense here
+ return 1;
+ }
default:
ngtcp2_unreachable();
}
@@ -109,6 +113,10 @@ uint32_t ngtcp2_addr_compare(const ngtcp2_addr *aa, const ngtcp2_addr *bb) {
}
return flags;
}
+ case NGTCP2_AF_UNIX: {
+ // TODO: see what makes sense here?
+ return 0;
+ }
default:
ngtcp2_unreachable();
}
EOF
popd
7 changes: 7 additions & 0 deletions scripts/download_openssl_quic.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/bin/bash

# If any commands fail, fail the script immediately.
set -ex

# Clone the curl repository to the specified directory.
git clone --depth 1 -b openssl-3.1.4+quic https://github.com/quictls/openssl $1
cmeister2 marked this conversation as resolved.
Show resolved Hide resolved
2 changes: 1 addition & 1 deletion scripts/fuzz_targets
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
#!/bin/bash

export FUZZ_TARGETS="curl_fuzzer_dict curl_fuzzer_file curl_fuzzer_ftp curl_fuzzer_gopher curl_fuzzer_http curl_fuzzer_https curl_fuzzer_imap curl_fuzzer_ldap curl_fuzzer_mqtt curl_fuzzer_pop3 curl_fuzzer_rtmp curl_fuzzer_rtsp curl_fuzzer_scp curl_fuzzer_sftp curl_fuzzer_smb curl_fuzzer_smtp curl_fuzzer_tftp curl_fuzzer_ws curl_fuzzer fuzz_url"
export FUZZ_TARGETS="curl_fuzzer_dict curl_fuzzer_file curl_fuzzer_ftp curl_fuzzer_gopher curl_fuzzer_http curl_fuzzer_https curl_fuzzer_http3 curl_fuzzer_imap curl_fuzzer_ldap curl_fuzzer_mqtt curl_fuzzer_pop3 curl_fuzzer_rtmp curl_fuzzer_rtsp curl_fuzzer_scp curl_fuzzer_sftp curl_fuzzer_smb curl_fuzzer_smtp curl_fuzzer_tftp curl_fuzzer_ws curl_fuzzer fuzz_url"
25 changes: 24 additions & 1 deletion scripts/install_curl.sh
Original file line number Diff line number Diff line change
Expand Up @@ -45,16 +45,39 @@ fi

pushd ${SRCDIR}

# TODO: Ignore HTTP3 socket connection failures
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto: inline patches.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this a TODO? Should it be?

pushd $1
patch -p1 <<'EOF'
diff --git a/lib/cf-socket.c b/lib/cf-socket.c
index e42b4a87b..f99fcd80c 100644
--- a/lib/cf-socket.c
+++ b/lib/cf-socket.c
@@ -1650,7 +1650,7 @@ static CURLcode cf_udp_setup_quic(struct Curl_cfilter *cf,

rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen);
if(-1 == rc) {
- return socket_connect_result(data, ctx->r_ip, SOCKERRNO);
+ /* return socket_connect_result(data, ctx->r_ip, SOCKERRNO); */
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels wrong - why are we disabling this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is quite hacky as you noticed, so I've specially marked it with a TODO. The harness uses AF_UNIX sockets and it makes the connect() call fail, which blocks fuzzing. Ideally we would have something nicer than this patch on cURL itself, to allow for fuzzing to proceed.

}
set_local_ip(cf, data);
CURL_TRC_CF(data, cf, "%s socket %" CURL_FORMAT_SOCKET_T
EOF
popd

# Build the library.
./buildconf
./configure --prefix=${INSTALLDIR} \
./configure PKG_CONFIG_PATH=${INSTALLDIR}/lib/pkgconfig \
--prefix=${INSTALLDIR} \
--disable-shared \
--enable-debug \
--enable-maintainer-mode \
--disable-symbol-hiding \
--enable-ipv6 \
--enable-websockets \
--with-random=/dev/null \
--with-openssl \
--with-nghttp3 \
--with-ngtcp2 \
${SSLOPTION} \
${NGHTTPOPTION} \
${CODE_COVERAGE_OPTION}
Expand Down
26 changes: 26 additions & 0 deletions scripts/install_nghttp3.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#!/bin/bash

# If any commands fail, fail the script immediately.
set -ex

SRCDIR=$1
INSTALLDIR=$2

if [[ ! -d ${INSTALLDIR} ]]
then
# Make an install target directory.
mkdir ${INSTALLDIR}
fi

pushd ${SRCDIR}

# Build the library.
autoreconf -fi
./configure --prefix=${INSTALLDIR} \
--disable-shared \
--enable-static \
--disable-threads \
--enable-lib-only

make
make install
29 changes: 29 additions & 0 deletions scripts/install_ngtcp2.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/bin/bash

# If any commands fail, fail the script immediately.
set -ex

SRCDIR=$1
INSTALLDIR=$2

if [[ ! -d ${INSTALLDIR} ]]
then
# Make an install target directory.
mkdir ${INSTALLDIR}
fi

pushd ${SRCDIR}

autoreconf -fi
./configure PKG_CONFIG_PATH=${INSTALLDIR}/lib/pkgconfig \
LDFLAGS="-Wl,-rpath,${INSTALLDIR}" \
--prefix=${INSTALLDIR} \
--disable-shared \
--enable-static \
--disable-threads \
--enable-lib-only \
--with-openssl \


make
make install
10 changes: 10 additions & 0 deletions scripts/install_openssl_quic.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@

#!/bin/bash

# If any commands fail, fail the script immediately.
set -ex

SRCDIR=$1
INSTALLDIR=$2

$(dirname "$0")/install_openssl.sh $SRCDIR $INSTALLDIR