2
0
mirror of https://github.com/boostorg/url.git synced 2026-01-19 04:42:15 +00:00

test: extend fuzz testing

fix #763
This commit is contained in:
alandefreitas
2023-07-24 21:01:56 -03:00
committed by Alan de Freitas
parent 33cfb939d0
commit 516e0093c5
13 changed files with 278 additions and 537 deletions

View File

@@ -1,109 +0,0 @@
name: Run and build
description: Build the project
inputs:
toolset:
description: B2 toolset module to use
required: true
version:
description: B2 toolset version to use
required: false
default: ''
comment:
description: Human-readable job description
required: false
default: ''
path:
description: Path to project root
required: false
default: ''
buildtype:
description: Build type
required: false
default: ''
cxxstd:
description: C++ standard versions to use (separated by commas)
required: false
default: ''
defines:
description: Macro definitions to use (separated by commas)
required: false
default: ''
variant:
description: B2 build variant to use
required: false
default: release
testflags:
description: B2 test properties to set
required: false
default: ''
targets:
description: B2 targets to build (separated by spaces)
required: false
default: ''
b2_flags:
description: Extra B2 flags to use
required: false
default: 'warnings=extra warnings-as-errors=on'
valgrind_options:
description: Valgrind options to use
required: false
default: ''
asan:
description: Use '1' to enable address sanitizer
required: false
default: ''
ubsan:
description: Use '1' to enable UB sanitizer
required: false
default: ''
runs:
using: composite
steps:
- name: Setup environment variables
shell: bash
run: |
B2_TOOLSET=${{ inputs.toolset }}
case $B2_TOOLSET in
gcc) CXX=g++ ;;
clang) CXX=clang++ ;;
*) CXX=$B2_TOOLSET ;;
esac
if [ -n "${{ inputs.version }}" ]; then
B2_TOOLSET=$B2_TOOLSET-${{ inputs.version }}
CXX=$CXX-${{ inputs.version }}
fi
echo "CXX=$CXX" >> $GITHUB_ENV
echo "COMMENT=${{ inputs.comment }}" >> $GITHUB_ENV
echo "LCOV_BRANCH_COVERAGE=$LCOV_BRANCH_COVERAGE" >> $GITHUB_ENV
echo "B2_CI_VERSION=1" >> $GITHUB_ENV
echo "B2_CXXSTD=${{ inputs.cxxstd }}" >> $GITHUB_ENV
echo "B2_TOOLSET=$B2_TOOLSET" >> $GITHUB_ENV
echo "B2_DEFINES=${{ inputs.defines }}" >> $GITHUB_ENV
echo "B2_VARIANT=${{ inputs.variant }}" >> $GITHUB_ENV
echo "B2_TESTFLAGS=${{ inputs.testflags }}" >> $GITHUB_ENV
echo "VALGRIND_OPTS=${{ inputs.valgrind_opts }}" >> $GITHUB_ENV
echo "B2_ASAN=${{ inputs.asan }}" >> $GITHUB_ENV
echo "B2_UBSAN=${{ inputs.ubsan }}" >> $GITHUB_ENV
echo "B2_TARGETS=${{ inputs.targets }}" >> $GITHUB_ENV
echo "B2_FLAGS=${{ inputs.b2_flags }}" >> $GITHUB_ENV
echo "DRONE_BUILD_EVENT=${{ github.event_name }}" >> $GITHUB_ENV
echo "DRONE_JOB_BUILDTYPE=${{ inputs.buildtype }}" >> $GITHUB_ENV
echo "DRONE_COMMIT=${{ github.sha }}" >> $GITHUB_ENV
echo "DRONE_BRANCH=${{ github.base_ref }}" >> $GITHUB_ENV
echo "DRONE_REPO=${{ github.repository }}" >> $GITHUB_ENV
- name: linux
if: runner.os == 'Linux'
shell: bash
run: . .drone/drone.sh
working-directory: ${{ inputs.path }}
- name: windows
if: runner.os == 'Windows'
shell: cmd
run: call .drone/drone.bat
working-directory: ${{ inputs.path }}

View File

@@ -69,7 +69,7 @@ jobs:
factors: |
gcc Asan Shared No-Threads
msvc Shared x86
clang Time-Trace
clang Time-Trace Fuzz
mingw Shared
trace-commands: true
@@ -120,19 +120,42 @@ jobs:
shell: bash
run: |
set -xe
# Identify boost module being tested
module=${GITHUB_REPOSITORY#*/}
echo "module=$module" >> $GITHUB_OUTPUT
# Identify GitHub workspace root
workspace_root=$(echo "$GITHUB_WORKSPACE" | sed 's/\\/\//g')
echo -E "workspace_root=$workspace_root" >> $GITHUB_OUTPUT
# Remove module from boost-source
rm -r "libs/$module" || true
cd ..
# Copy cached boost-source to an isolated boost-root
mkdir boost-root || true
cp -r "boost-source"/* "boost-root"
# Set boost-root output
cd boost-root
boost_root="$(pwd)"
boost_root=$(echo "$boost_root" | sed 's/\\/\//g')
echo -E "boost_root=$boost_root" >> $GITHUB_OUTPUT
# Patch boost-root with workspace module
mkdir "libs/$module"
cp -r "$workspace_root"/* "libs/$module"
cxxstd=${{ matrix.cxxstd }}
latest_std=$(echo $cxxstd | awk -F ',' '{print $NF}')
echo "latest_std=$latest_std" >> $GITHUB_OUTPUT
- name: Fuzz corpus
if: ${{ matrix.fuzz }}
uses: actions/cache@v3.3.1
id: cache-corpus
with:
path: ${{ steps.patch.outputs.workspace_root }}/corpus.tar
key: corpus-${{ github.run_id }}
enableCrossOsArchive: true
restore-keys: |
corpus-
- name: Boost CMake Workflow
uses: alandefreitas/cpp-actions/cmake-workflow@v1.5.0
@@ -141,7 +164,7 @@ jobs:
build-dir: __build_cmake_test__
generator: ${{ matrix.generator }}
build-type: ${{ matrix.build-type }}
build-target: tests boost_url_tests boost_url_limits boost_url_extra
build-target: tests boost_url_tests boost_url_limits boost_url_extra ${{ (matrix.fuzz && 'fuzz') || ''}}
run-tests: true
install-prefix: $GITHUB_WORKSPACE/.local
cxxstd: ${{ matrix.latest-cxxstd }}
@@ -151,7 +174,7 @@ jobs:
cxxflags: ${{ matrix.cxxflags }}
shared: ${{ matrix.shared }}
cmake-version: '>=3.15'
extra-args: ${{ format('-D Boost_VERBOSE=ON -D BOOST_INCLUDE_LIBRARIES={0} -D BOOST_URL_DISABLE_THREADS={1}', steps.patch.outputs.module, ( matrix.no-threads && 'ON' ) || 'OFF') }}
extra-args: -D Boost_VERBOSE=ON -D BOOST_INCLUDE_LIBRARIES=${{ steps.patch.outputs.module }} -D BOOST_URL_DISABLE_THREADS=${{ ( matrix.no-threads && 'ON' ) || 'OFF' }} -D BOOST_URL_BUILD_FUZZERS=${{ ( matrix.fuzz && format('ON -D BOOST_URL_FUZZER_CORPUS_PATH={0}/corpus.tar', steps.patch.outputs.workspace_root) ) || 'OFF' }}
export-compile-commands: ${{ matrix.time-trace }}
package: false
package-artifact: false

View File

@@ -1,54 +0,0 @@
name: fuzz
on:
push:
pull_request:
schedule:
- cron: "25 */12 * * *"
workflow_dispatch:
jobs:
fuzz:
runs-on: ubuntu-latest
steps:
- name: Fetch head
uses: actions/checkout@v3
with:
path: 'head'
- name: Restore corpus
uses: actions/cache@v3
id: cache-corpus
with:
path: head/test/fuzz/corpus.tar
key: corpus-${{ github.run_id }}
restore-keys: corpus-
- name: Unzip corpus
if: steps.cache-corpus.outputs.cache-hit == 'true'
working-directory: head/test/fuzz/
run: |
tar -vxf corpus.tar
- name: Build boost and run fuzzer
uses: ./head/.github/actions/build
with:
buildtype: 'boost'
path: 'head'
toolset: clang
targets: libs/url/test/fuzz//run
- name: Pack the corpus
working-directory: boost-root/libs/url/test/fuzz/
run: |
tar cf - cmin > corpus.tar.tmp
mv corpus.tar.tmp "${GITHUB_WORKSPACE}"/head/test/fuzz/corpus.tar
- name: Archive any crashes as an artifact
uses: actions/upload-artifact@v3
if: always()
with:
name: crashes
path: |
boost-root/crash-*
boost-root/leak-*
boost-root/timeout-*
if-no-files-found: ignore

View File

@@ -1,30 +1,130 @@
#
# Copyright (c) 2023 alandefreitas (alandefreitas@gmail.com)
# Copyright (c) 2023 Alan de Freitas (alandefreitas@gmail.com)
#
# Distributed under the Boost Software License, Version 1.0.
# https://www.boost.org/LICENSE_1_0.txt
#
# Files
source_group("" FILES fuzz_parse.cpp)
# Get number of cores
include(ProcessorCount)
ProcessorCount(PROCESSOR_COUNT)
function(add_boost_url_fuzzer NAME SOURCE_FILES)
# Fuzzer library
add_library(fuzzerlib_${NAME} ${SOURCE_FILES})
target_link_libraries(fuzzerlib_${NAME} PRIVATE Boost::url)
set_property(TARGET fuzzerlib_${NAME} PROPERTY FOLDER "fuzzing")
# Determine total fuzz time per file
file(GLOB BOOST_URL_FUZZER_SOURCE_FILES *.cpp)
list(LENGTH BOOST_URL_FUZZER_SOURCE_FILES BOOST_URL_FUZZER_SOURCE_FILES_COUNT)
set(BOOST_URL_FUZZER_TOTAL_TIME_DEFAULT 30)
math(EXPR BOOST_URL_FUZZER_TOTAL_TIME_DEFAULT "${BOOST_URL_FUZZER_TOTAL_TIME_DEFAULT} / (${PROCESSOR_COUNT} * ${BOOST_URL_FUZZER_SOURCE_FILES_COUNT}) + 1")
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
# Fuzzer executable
add_executable(fuzzer_${NAME} ${SOURCE_FILES})
target_link_libraries(fuzzer_${NAME} PRIVATE Boost::url)
target_compile_options(fuzzer_${NAME} PRIVATE -g -O2 -fsanitize=fuzzer,address,undefined -fno-sanitize-recover=undefined)
target_link_libraries(fuzzer_${NAME} PRIVATE -fsanitize=fuzzer -fuse-ld=lld)
# Fuzzing options
set(BOOST_URL_FUZZER_TOTAL_TIME ${BOOST_URL_FUZZER_TOTAL_TIME_DEFAULT} CACHE STRING "Total time for fuzzing")
set(BOOST_URL_FUZZER_RSS_LIMIT 8192 CACHE STRING "RSS limit for fuzzing")
set(BOOST_URL_FUZZER_TIMEOUT 30 CACHE STRING "Timeout for fuzzing")
set(BOOST_URL_FUZZER_MAX_LEN 4000 CACHE STRING "Maximum size of the input")
set(BOOST_URL_FUZZER_JOBS ${PROCESSOR_COUNT} CACHE STRING "Number of jobs for fuzzing")
option(BOOST_URL_FUZZER_ADD_TO_CTEST "Add fuzzing targets to ctest" OFF)
set(BOOST_URL_FUZZER_CORPUS_PATH ${CMAKE_CURRENT_BINARY_DIR}/corpus.tar CACHE STRING "Path to corpus.tar")
# Custom target to run fuzzer executable
add_custom_target(fuzz_${NAME} fuzzer_${NAME} -rss_limit_mb=8192 -max_total_time=30 -timeout=30 DEPENDS fuzz_${NAME})
set_property(TARGET fuzzer_${NAME} PROPERTY FOLDER "fuzzing")
endif ()
# Corpus
set(BOOST_URL_FUZZER_SEEDS_PATH ${CMAKE_CURRENT_SOURCE_DIR}/seeds.tar)
set(BOOST_URL_FUZZER_SEEDS_DIR ${CMAKE_CURRENT_BINARY_DIR}/seeds)
get_filename_component(BOOST_URL_FUZZER_SEEDS_PARENT_DIR ${BOOST_URL_FUZZER_SEEDS_DIR} DIRECTORY)
add_custom_target(
untar_seeds
COMMAND ${CMAKE_COMMAND} -E echo "Untar fuzz seeds from ${BOOST_URL_FUZZER_SEEDS_PATH} to ${BOOST_URL_FUZZER_SEEDS_PARENT_DIR}/seeds"
COMMAND ${CMAKE_COMMAND} -E tar xf ${BOOST_URL_FUZZER_SEEDS_PATH}
WORKING_DIRECTORY ${BOOST_URL_FUZZER_SEEDS_PARENT_DIR}
COMMENT "Unzipping fuzz seeds"
VERBATIM)
set(BOOST_URL_FUZZER_CORPUS_DIR ${CMAKE_CURRENT_BINARY_DIR}/corpus)
set(BOOST_URL_FUZZER_MERGED_CORPUS_DIR ${CMAKE_CURRENT_BINARY_DIR}/merged-corpus)
if(EXISTS ${BOOST_URL_FUZZER_CORPUS_PATH})
add_custom_target(
untar_corpus
COMMAND ${CMAKE_COMMAND} -E echo "Untar fuzz corpus archive from \"${BOOST_URL_FUZZER_CORPUS_PATH}\" to \"${CMAKE_CURRENT_BINARY_DIR}/corpus\""
COMMAND ${CMAKE_COMMAND} -E tar xf ${BOOST_URL_FUZZER_CORPUS_PATH}
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMENT "Unzipping fuzz corpus"
VERBATIM)
else()
add_custom_target(untar_corpus
COMMAND ${CMAKE_COMMAND} -E echo "No fuzz corpus archive in ${BOOST_URL_FUZZER_CORPUS_PATH}. Create empty fuzz corpus dir \"${BOOST_URL_FUZZER_CORPUS_DIR}.\""
COMMAND ${CMAKE_COMMAND} -E make_directory ${BOOST_URL_FUZZER_CORPUS_DIR}
COMMENT "Creating fuzz corpus directory"
VERBATIM)
endif()
add_dependencies(untar_corpus untar_seeds)
# Target that runs all fuzz targets
get_filename_component(BOOST_URL_FUZZER_CORPUS_PARENT_DIR ${BOOST_URL_FUZZER_CORPUS_PATH} DIRECTORY)
add_custom_target(
fuzz
COMMAND ${CMAKE_COMMAND} -E echo "Archive corpus from \"${BOOST_URL_FUZZER_CORPUS_DIR}\" to \"${BOOST_URL_FUZZER_CORPUS_PATH}\""
COMMAND ${CMAKE_COMMAND} -E tar cf ${BOOST_URL_FUZZER_CORPUS_PATH} ${BOOST_URL_FUZZER_CORPUS_DIR}
WORKING_DIRECTORY ${BOOST_URL_FUZZER_CORPUS_PARENT_DIR}
VERBATIM)
# Boost.URL with fuzz options
add_library(boost_url_fuzz ${BOOST_URL_HEADERS} ${BOOST_URL_SOURCES})
boost_url_setup_properties(boost_url_fuzz)
target_compile_options(boost_url_fuzz PRIVATE -g -O2 -fsanitize=fuzzer,address,undefined -fno-sanitize-recover=undefined)
target_link_libraries(boost_url_fuzz PRIVATE -fsanitize=fuzzer,address,undefined)
# Register a single fuzzer and add as dependency to fuzz target
function(add_boost_url_fuzzer NAME)
# Fuzzer executable
set(SOURCE_FILES ${ARGN})
add_executable(fuzzer_${NAME} ${SOURCE_FILES})
target_link_libraries(fuzzer_${NAME} PRIVATE boost_url_fuzz)
target_compile_options(fuzzer_${NAME} PRIVATE -g -O2 -fsanitize=fuzzer,address,undefined -fno-sanitize-recover=undefined)
target_link_libraries(fuzzer_${NAME} PRIVATE -fsanitize=fuzzer,address,undefined)
set_property(TARGET fuzzer_${NAME} PROPERTY FOLDER "fuzzing")
# Custom target to run fuzzer executable
add_custom_target(
fuzz_${NAME}
ALL
COMMAND ${CMAKE_COMMAND} -E make_directory ${BOOST_URL_FUZZER_CORPUS_DIR}/${NAME}
COMMAND ${CMAKE_COMMAND} -E echo "Running fuzzer ${NAME} with corpus from ${BOOST_URL_FUZZER_CORPUS_DIR}/${NAME} and seeds from ${BOOST_URL_FUZZER_SEEDS_DIR}/${NAME}"
COMMAND
fuzzer_${NAME}
-rss_limit_mb=${BOOST_URL_FUZZER_RSS_LIMIT}
-max_total_time=${BOOST_URL_FUZZER_TOTAL_TIME}
-timeout=${BOOST_URL_FUZZER_TIMEOUT}
-max_len=${BOOST_URL_FUZZER_MAX_LEN}
-jobs=${BOOST_URL_FUZZER_JOBS}
${BOOST_URL_FUZZER_CORPUS_DIR}/${NAME}
${BOOST_URL_FUZZER_SEEDS_DIR}/${NAME}
COMMAND ${CMAKE_COMMAND} -E make_directory ${BOOST_URL_FUZZER_MERGED_CORPUS_DIR}/${NAME}
COMMAND ${CMAKE_COMMAND} -E echo "Merging corpus from ${BOOST_URL_FUZZER_CORPUS_DIR}/${NAME} to ${BOOST_URL_FUZZER_MERGED_CORPUS_DIR}/${NAME}"
COMMAND
fuzzer_${NAME}
-merge=1
${BOOST_URL_FUZZER_MERGED_CORPUS_DIR}/${NAME}
${BOOST_URL_FUZZER_CORPUS_DIR}/${NAME}
${BOOST_URL_FUZZER_SEEDS_DIR}/${NAME}
COMMAND ${CMAKE_COMMAND} -E echo "Replacing corpus in ${BOOST_URL_FUZZER_CORPUS_DIR}/${NAME} with merged corpus from ${BOOST_URL_FUZZER_MERGED_CORPUS_DIR}/${NAME}"
COMMAND ${CMAKE_COMMAND} -E remove_directory ${BOOST_URL_FUZZER_CORPUS_DIR}/${NAME}
COMMAND ${CMAKE_COMMAND} -E make_directory ${BOOST_URL_FUZZER_CORPUS_DIR}/${NAME}
COMMAND ${CMAKE_COMMAND} -E copy_directory ${BOOST_URL_FUZZER_MERGED_CORPUS_DIR}/${NAME} ${BOOST_URL_FUZZER_CORPUS_DIR}/${NAME}
COMMAND ${CMAKE_COMMAND} -E remove_directory ${BOOST_URL_FUZZER_MERGED_CORPUS_DIR}/${NAME}
DEPENDS untar_corpus fuzzer_${NAME})
add_dependencies(fuzz_${NAME} fuzzer_${NAME})
add_dependencies(fuzz fuzz_${NAME})
set_property(TARGET fuzz_${NAME} PROPERTY ENVIRONMENT "UBSAN_OPTIONS=halt_on_error=false")
if (BOOST_URL_FUZZER_ADD_TO_CTEST)
add_test(
NAME test_fuzz_${NAME}
COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target fuzz_${NAME})
endif()
endfunction()
add_boost_url_fuzzer(parse fuzz_parse.cpp)
# Register all fuzzers
file(GLOB BOOST_URL_FUZZER_SOURCE_FILES *.cpp)
source_group("" FILES ${BOOST_URL_FUZZER_SOURCE_FILES})
foreach(BOOST_URL_FUZZER_SOURCE_FILE ${BOOST_URL_FUZZER_SOURCE_FILES})
file(RELATIVE_PATH BOOST_URL_FUZZER_SOURCE_FILE ${CMAKE_CURRENT_SOURCE_DIR} ${BOOST_URL_FUZZER_SOURCE_FILE})
string(REGEX REPLACE "(.*).cpp" "\\1" RULE_NAME ${BOOST_URL_FUZZER_SOURCE_FILE})
add_boost_url_fuzzer(${RULE_NAME} ${BOOST_URL_FUZZER_SOURCE_FILE})
endforeach()

View File

@@ -1,185 +0,0 @@
#
# Copyright (c) 2023 alandefreitas (alandefreitas@gmail.com)
#
# Distributed under the Boost Software License, Version 1.0.
# https://www.boost.org/LICENSE_1_0.txt
#
# This code has been adapted from libs/json/fuzzing/Jamfile
#
# Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
# Copyright (c) 2019 Paul Dreik
# Copyright (c) 2021 Dmitry Arkhipov (grisumbras@gmail.com)
#
# Distributed under the Boost Software License, Version 1.0. (See accompanying
# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#
# Official repository: https://github.com/boostorg/json
#
import common ;
import link ;
import os ;
import path ;
import property ;
import sequence ;
# set the maximum size of the input, to avoid
# big inputs which blow up the corpus size
.MAXLEN = [ os.environ MAXLEN ] ;
.MAXLEN ?= -max_len=4000 ;
# set a timelimit (you may want to adjust this if you run locally)
.MAXTIME = [ os.environ MAXTIME ] ;
.MAXTIME ?= -max_total_time=30 ;
# If doing fuzzing locally (not in CI), adjust this to utilize more
# of your cpu.
#JOBS="-jobs=32"
.JOBS = [ os.environ JOBS ] ;
# make sure ubsan stops in case anything is found
.UBSAN_OPTIONS = [
common.variable-setting-command UBSAN_OPTIONS : halt_on_error=1
] ;
local corpus.tar = [ glob-ex . : corpus.tar ] ;
if $(corpus.tar)
{
# if an old corpus exists, use it
make old-corpus
: $(corpus.tar)
: @untar-corpus
: <location>oldcorpus
;
}
else
{
alias old-corpus ;
}
explicit old-corpus ;
local initial-corpus = [ glob-tree-ex ../test : *.json ] ;
local variants = parse ;
for local variant in parse
{
local $(variant)-runs ;
local fuzzer = fuzzer_$(variant) ;
lib $(fuzzer) : fuzz_$(variant).cpp /boost/url//boost_url ;
exe $(fuzzer)
: fuzz_$(variant).cpp /boost/url//url_sources
: requirements
<toolset>clang
<conditional>@fuzzer-props
;
# make sure the old crashes pass without problems
local old-runs = [ glob-tree-ex old_crashes/$(variant) : * ] ;
if $(old-runs)
{
run $(fuzzer)
: target-name $(variant)-run-crashes
: input-files [ SORT $(old-runs) ]
;
$(variant)-runs += $(variant)-run-crashes ;
}
make oldcorpus/$(variant)
: old-corpus
: common.MkDir
: <location>.
;
explicit oldcorpus/$(variant) ;
# make an initial corpus from the test data already in the repo
local seed-corpus ;
for file in $(initial-corpus)
{
local copied = $(variant)/$(file:D=) ;
make $(copied) : $(file) : common.copy : <location>seedcorpus ;
explicit $(copied) ;
seed-corpus += $(copied) ;
}
make seedcorpus/$(variant)
: $(seed-corpus)
: common.MkDir
: <location>.
;
explicit seedcorpus/$(variant) ;
# run the fuzzer for a short while
make out/$(variant)
: $(fuzzer)
oldcorpus/$(variant)
seedcorpus/$(variant)
: @run-fuzzer
: <location>.
<flags>$(.MAXTIME)
<flags>$(.MAXLEN)
<flags>$(.JOBS)
;
$(variant)-runs += out/$(variant) ;
# minimize the corpus
make cmin/$(variant)
: $(fuzzer)
oldcorpus/$(variant)
out/$(variant)
: @run-fuzzer
: <location>.
<flags>-merge=1
<flags>$(.MAXLEN)
;
$(variant)-runs += cmin/$(variant) ;
alias $(variant)-run : $($(variant)-runs) ;
explicit $($(variant)-runs) ;
}
alias run : $(variants)-run ;
explicit run $(variants)-run ;
rule fuzzer-props ( props * )
{
local toolset = [ property.select toolset : $(props) ] ;
if clang = $(toolset:G=)
{
return
<debug-symbols>on
<optimization>speed
<address-sanitizer>on
<undefined-sanitizer>norecover
<cxxflags>-fsanitize=fuzzer
<linkflags>-fsanitize=fuzzer
;
}
else
{
return <build>no ;
}
}
rule run-fuzzer ( target : sources * : props * )
{
local flags = [ property.select flags : $(props) ] ;
FLAGS on $(target) = $(flags:G=) ;
local dir = [ path.make [ on $(target) return $(LOCATE) ] ] ;
dir = $(dir)/$(target:G=) ;
common.MkDir $(dir) ;
DEPENDS $(target) : $(dir) ;
}
actions run-fuzzer
{
$(.UBSAN_OPTIONS)
$(>[1]) $(<) $(>[2]) $(>[3]) $(FLAGS)
}
.TOUCH_FILE = [ common.file-touch-command ] ;
actions untar-corpus
{
tar xf $(>) -C $(<:D)
$(.TOUCH_FILE) $(<)
}

View File

@@ -0,0 +1,26 @@
//
// Copyright (c) 2023 alandefreitas (alandefreitas@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
//
#include <boost/url/parse.hpp>
#include <boost/core/detail/string_view.hpp>
#include <boost/core/ignore_unused.hpp>
using namespace boost::urls;
namespace core = boost::core;
extern "C"
int
LLVMFuzzerTestOneInput(
const uint8_t* data,
size_t size)
{
core::string_view s{reinterpret_cast<
const char*>(data), size};
boost::ignore_unused(parse_absolute_uri(s));
return 0;
}

View File

@@ -1,97 +0,0 @@
#!/bin/sh
#
# Copyright (c) 2023 alandefreitas (alandefreitas@gmail.com)
#
# Distributed under the Boost Software License, Version 1.0.
# https://www.boost.org/LICENSE_1_0.txt
#
# This code has been adapted from libs/json/fuzzing/fuzz.sh
# By Paul Dreik 2019-2020 for the boost json project
# License: Boost 1.0
set -e
# Current directory
fuzzdir=$(dirname $0)
me=$(basename $0)
cd $fuzzdir
# Find Clang
if [ -z $CLANG ]; then
for clangver in -15 -14 -13 -12 -11 -10 -9 -8 -7 -6 -6.0 ""; do
CLANG=clang++$clangver
if which $CLANG >/dev/null; then
break
fi
done
fi
if ! which $CLANG >/dev/null; then
if ! -x $CLANG; then
echo $me: sorry, could not find clang $CLANG
exit 1
fi
fi
echo "\$CLANG: $CLANG"
# Fuzzer parameters
MAXLEN="-max_len=4000" # max input size, to avoid blowing up the corpus size
JOBS= # Adjust this to utilize more of your cpu: JOBS="-jobs=32"
MAXTIME="-max_total_time=30" # seconds
# Compile and run each fuzzer
variants="parse"
for variant in $variants; do
# Paths
srcfile=fuzz_$variant.cpp
fuzzer=./fuzzer_$variant
seedcorpus=seedcorpus/$variant
echo "Fuzz \"$variant\""
echo "\$srcfile= $srcfile"
echo "\$fuzzer= $fuzzer"
echo "\$seedcorpus= $seedcorpus"
# Compile
if [ ! -e $fuzzer -o $srcfile -nt $fuzzer ]; then
$CLANG \
-std=c++11 \
-O3 \
-g \
-fsanitize=fuzzer,address,undefined \
-fno-sanitize-recover=undefined \
-I../../include \
-o $fuzzer \
../../src/src.cpp \
$srcfile
fi
# Create initial corpus from old crashes in repo
if [ -d old_crashes/$variant ]; then
find old_crashes/$variant -type f -print0 | xargs -0 --no-run-if-empty $fuzzer
fi
if [ ! -d $seedcorpus ]; then
mkdir -p $seedcorpus
find ../test -name "*.json" -type f -print0 | xargs -0 --no-run-if-empty cp -f -t $seedcorpus/
fi
# Potentially merge file with old corpus
if [ -e corpus.tar ]; then
mkdir -p oldcorpus
tar xf corpus.tar -C oldcorpus || echo "corpus.tar was broken! ignoring it"
OLDCORPUS=oldcorpus/cmin/$variant
mkdir -p $OLDCORPUS
else
OLDCORPUS=
fi
# Run fuzzer with corpus
export UBSAN_OPTIONS="halt_on_error=1"
mkdir -p out/$variant
$fuzzer out/$variant $OLDCORPUS $seedcorpus/ $MAXTIME $MAXLEN $JOBS
# Minimize the corpus
mkdir -p cmin/$variant
$fuzzer cmin/$variant $OLDCORPUS out/$variant $seedcorpus/ -merge=1 $MAXLEN
done

View File

@@ -1,67 +0,0 @@
//
// Copyright (c) 2023 alandefreitas (alandefreitas@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
//
#include <boost/url/parse.hpp>
#include <boost/core/detail/string_view.hpp>
using namespace boost::urls;
namespace core = boost::core;
enum class parser {
absolute_uri,
origin_form,
relative_ref,
uri,
uri_reference
};
bool
fuzz_parse(parser p, core::string_view sv)
{
boost::system::result<url_view> r;
switch (p)
{
case parser::absolute_uri:
r = parse_absolute_uri(sv);
break;
case parser::origin_form:
r = parse_origin_form(sv);
break;
case parser::relative_ref:
r = parse_relative_ref(sv);
break;
case parser::uri:
r = parse_uri(sv);
break;
case parser::uri_reference:
r = parse_uri_reference(sv);
break;
}
return r.has_value();
}
extern "C"
int
LLVMFuzzerTestOneInput(
const uint8_t* data,
size_t size)
{
if (size == 0)
return -1;
try
{
auto p = static_cast<parser>(data[0] % 5);
core::string_view s{reinterpret_cast<
const char*>(data + 1), size - 1};
fuzz_parse(p, s);
}
catch(...)
{
}
return 0;
}

26
test/fuzz/origin_form.cpp Normal file
View File

@@ -0,0 +1,26 @@
//
// Copyright (c) 2023 alandefreitas (alandefreitas@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
//
#include <boost/url/parse.hpp>
#include <boost/core/detail/string_view.hpp>
#include <boost/core/ignore_unused.hpp>
using namespace boost::urls;
namespace core = boost::core;
extern "C"
int
LLVMFuzzerTestOneInput(
const uint8_t* data,
size_t size)
{
core::string_view s{reinterpret_cast<
const char*>(data), size};
boost::ignore_unused(parse_origin_form(s));
return 0;
}

View File

@@ -0,0 +1,26 @@
//
// Copyright (c) 2023 alandefreitas (alandefreitas@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
//
#include <boost/url/parse.hpp>
#include <boost/core/detail/string_view.hpp>
#include <boost/core/ignore_unused.hpp>
using namespace boost::urls;
namespace core = boost::core;
extern "C"
int
LLVMFuzzerTestOneInput(
const uint8_t* data,
size_t size)
{
core::string_view s{reinterpret_cast<
const char*>(data), size};
boost::ignore_unused(parse_relative_ref(s));
return 0;
}

BIN
test/fuzz/seeds.tar Normal file

Binary file not shown.

26
test/fuzz/uri.cpp Normal file
View File

@@ -0,0 +1,26 @@
//
// Copyright (c) 2023 alandefreitas (alandefreitas@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
//
#include <boost/url/parse.hpp>
#include <boost/core/detail/string_view.hpp>
#include <boost/core/ignore_unused.hpp>
using namespace boost::urls;
namespace core = boost::core;
extern "C"
int
LLVMFuzzerTestOneInput(
const uint8_t* data,
size_t size)
{
core::string_view s{reinterpret_cast<
const char*>(data), size};
boost::ignore_unused(parse_uri(s));
return 0;
}

View File

@@ -0,0 +1,26 @@
//
// Copyright (c) 2023 alandefreitas (alandefreitas@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
//
#include <boost/url/parse.hpp>
#include <boost/core/detail/string_view.hpp>
#include <boost/core/ignore_unused.hpp>
using namespace boost::urls;
namespace core = boost::core;
extern "C"
int
LLVMFuzzerTestOneInput(
const uint8_t* data,
size_t size)
{
core::string_view s{reinterpret_cast<
const char*>(data), size};
boost::ignore_unused(parse_uri_reference(s));
return 0;
}