mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-08-22 01:49:48 +00:00
[#3605] Integrate a new fuzzing solution in Kea
The solution is based on clusterfuzzlite, libfuzzer, and oss-fuzz technologies. - Add the .clusterfuzzlite directory. - Add the fuzz CI stage and fuzzing CI jobs. - Add the fuzzing targets in the fuzz directory. - Document fuzzing in doxygen.
This commit is contained in:
parent
8195f702e7
commit
a96168e762
6
.clusterfuzzlite/Dockerfile
Normal file
6
.clusterfuzzlite/Dockerfile
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
FROM registry.gitlab.isc.org/isc-projects/kea:fuzz-latest
|
||||||
|
|
||||||
|
# Copy repo and link build.sh so that it runs from a location relative to the Kea repo.
|
||||||
|
WORKDIR "${SRC}"
|
||||||
|
COPY . "${SRC}/kea"
|
||||||
|
RUN ln -s "${SRC}/kea/.clusterfuzzlite/build.sh" "${SRC}/build.sh"
|
58
.clusterfuzzlite/build.sh
Executable file
58
.clusterfuzzlite/build.sh
Executable file
@ -0,0 +1,58 @@
|
|||||||
|
#!/bin/bash -eu
|
||||||
|
|
||||||
|
# https://reports.kea.isc.org/new-fuzzer.html
|
||||||
|
|
||||||
|
script_path="$(dirname "$(readlink -f "${0}")")"
|
||||||
|
cd "${script_path}/.."
|
||||||
|
|
||||||
|
# Use a wrapper function to allow "return 1" instead of "exit 1" which may have
|
||||||
|
# unforeseen consequences in case this script is sourced.
|
||||||
|
install_kea() {
|
||||||
|
# ccache
|
||||||
|
export CCACHE_DIR=/cache
|
||||||
|
export PATH="/usr/lib/ccache:$PATH"
|
||||||
|
export KEA_BUILD_DIR="${KEA_BUILD_DIR-/builds/isc-projects/kea}"
|
||||||
|
|
||||||
|
cxxflags=
|
||||||
|
autoreconf -i
|
||||||
|
if test "${SANITIZER}" = 'none'; then
|
||||||
|
cxxflags="${cxxflags} -fno-sanitize=all"
|
||||||
|
enable_fuzzing='--enable-fuzzing'
|
||||||
|
else
|
||||||
|
cxxflags="${cxxflags} -fsanitize=${SANITIZER}"
|
||||||
|
enable_fuzzing='--enable-fuzzing=ci'
|
||||||
|
fi
|
||||||
|
export CXXFLAGS="${cxxflags}"
|
||||||
|
export LDFLAGS='-L/usr/lib/gcc/x86_64-linux-gnu/9 -lstdc++fs'
|
||||||
|
if ! ./configure --enable-boost-headers-only --prefix='/opt/kea' "${enable_fuzzing}" --with-gtest=/usr/src/googletest/googletest; then
|
||||||
|
printf './configure failed. Here is config.log:\n'
|
||||||
|
cat config.log
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
make -j "$(nproc)"
|
||||||
|
make install
|
||||||
|
|
||||||
|
# Copy internal libraries.
|
||||||
|
# SC2156 (warning): Injecting filenames is fragile and insecure. Use parameters.
|
||||||
|
# shellcheck disable=SC2156
|
||||||
|
find "/opt/kea/lib" -mindepth 1 -maxdepth 1 -exec sh -c "cp {} ${KEA_BUILD_DIR}" ';'
|
||||||
|
|
||||||
|
# Copy the binaries.
|
||||||
|
for fuzzer in fuzz-config-kea-dhcp4 fuzz-packets-kea-dhcp4 fuzz-unix-socket-kea-dhcp4 \
|
||||||
|
fuzz-config-kea-dhcp6 fuzz-packets-kea-dhcp6 fuzz-unix-socket-kea-dhcp6 \
|
||||||
|
fuzz-http-endpoint \
|
||||||
|
; do
|
||||||
|
cp "/opt/kea/sbin/${fuzzer}" "${OUT}/${fuzzer}"
|
||||||
|
# copy all required libraries
|
||||||
|
echo "ldd ${OUT}/${fuzzer}: "
|
||||||
|
ldd "${OUT}/${fuzzer}"
|
||||||
|
EXTENDED_PATH=$(readelf -d "${OUT}/${fuzzer}" | grep 'R.*PATH' | cut -d '[' -f 2 | cut -d ']' -f 1)
|
||||||
|
patchelf --set-rpath "/usr/lib/x86_64-linux-gnu:/lib/x86_64-linux-gnu:${EXTENDED_PATH}" "${OUT}/${fuzzer}"
|
||||||
|
readelf -d "${OUT}/${fuzzer}" | grep 'R.*PATH' || true
|
||||||
|
for i in $(ldd "${OUT}/${fuzzer}" | cut -f 2 | cut -d ' ' -f 3); do
|
||||||
|
cp "${i}" "${KEA_BUILD_DIR}"
|
||||||
|
done
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
install_kea
|
1
.clusterfuzzlite/project.yaml
Normal file
1
.clusterfuzzlite/project.yaml
Normal file
@ -0,0 +1 @@
|
|||||||
|
language: c++
|
36
.clusterfuzzlite/run-locally.sh
Executable file
36
.clusterfuzzlite/run-locally.sh
Executable file
@ -0,0 +1,36 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Change to parent directory, so that the script can be called from anywhere.
|
||||||
|
parent_path=$(cd "$(dirname "${0}")" && pwd)
|
||||||
|
cd "${parent_path}" || exit 1
|
||||||
|
|
||||||
|
mkdir -p build/out
|
||||||
|
mkdir -p build/work
|
||||||
|
|
||||||
|
cd .. || exit 2
|
||||||
|
|
||||||
|
docker build -t kea-fuzzing -f .clusterfuzzlite/Dockerfile .
|
||||||
|
|
||||||
|
docker_run() {
|
||||||
|
docker run \
|
||||||
|
--interactive \
|
||||||
|
--privileged \
|
||||||
|
--platform linux/amd64 \
|
||||||
|
--rm \
|
||||||
|
--shm-size=2g \
|
||||||
|
-e ARCHITECTURE=x86_64 \
|
||||||
|
-e CIFUZZ=true \
|
||||||
|
-e FUZZING_ARGS='-rss_limit_mb=8192' \
|
||||||
|
-e FUZZING_ENGINE=libfuzzer \
|
||||||
|
-e FUZZING_LANGUAGE=c++ \
|
||||||
|
-e KEA_BUILD_DIR=/src \
|
||||||
|
-e SANITIZER=address \
|
||||||
|
-v "${parent_path}/build/out:/out" \
|
||||||
|
-v "${parent_path}/build/work:/work" \
|
||||||
|
kea-fuzzing \
|
||||||
|
"${@}"
|
||||||
|
}
|
||||||
|
|
||||||
|
docker_run
|
||||||
|
|
||||||
|
docker_run compile
|
197
.gitlab-ci.yml
197
.gitlab-ci.yml
@ -2,32 +2,52 @@ variables:
|
|||||||
# Locale settings do not affect the build, but might affect tests.
|
# Locale settings do not affect the build, but might affect tests.
|
||||||
LC_ALL: C
|
LC_ALL: C
|
||||||
|
|
||||||
CI_REGISTRY_IMAGE: registry.gitlab.isc.org/isc-projects/kea
|
# Fuzzing
|
||||||
|
CFL_ARTIFACTS_DIR: '/tmp/cfl-artifacts'
|
||||||
|
CFL_CACHE_DIR: '/ccache/cfl-cache'
|
||||||
|
CFL_IMAGE: 'gcr.io/oss-fuzz-base/clusterfuzzlite-run-fuzzers'
|
||||||
|
CFL_PLATFORM: gitlab
|
||||||
|
FUZZ_SECONDS: 600 # 10 min (ClusterFuzzLite defaults)
|
||||||
|
FUZZING_ARGS: '-rss_limit_mb=8192'
|
||||||
|
LD_LIBRARY_PATH: "/opt/kea/lib:/usr/lib/x86_64-linux-gnu:/lib/x86_64-linux-gnu:/builds/isc-projects/kea"
|
||||||
|
PARALLEL_FUZZING: true
|
||||||
|
CCACHE_BASEDIR: "${CI_PROJECT_DIR}"
|
||||||
|
CCACHE_DIR: "${CI_PROJECT_DIR}/ccache"
|
||||||
|
|
||||||
# Setting this variable will affect all Security templates
|
# SAST
|
||||||
# (SAST, Dependency Scanning, ...)
|
|
||||||
SECURE_ANALYZERS_PREFIX: "registry.gitlab.com/gitlab-org/security-products/analyzers"
|
SECURE_ANALYZERS_PREFIX: "registry.gitlab.com/gitlab-org/security-products/analyzers"
|
||||||
|
|
||||||
# Leave only bandit, flawfinder, semgrep.
|
# Leave only bandit, flawfinder, semgrep.
|
||||||
SAST_EXCLUDED_ANALYZERS: "eslint, spotbugs"
|
SAST_EXCLUDED_ANALYZERS: "eslint, spotbugs"
|
||||||
|
|
||||||
image: "${CI_REGISTRY_IMAGE}:latest"
|
image: 'registry.gitlab.isc.org/isc-projects/kea:latest'
|
||||||
|
|
||||||
stages:
|
stages:
|
||||||
- test
|
- test
|
||||||
|
- fuzz
|
||||||
|
|
||||||
|
# Do not run the test stage on pipeline schedule trigger.
|
||||||
|
.base_rules_for_test_jobs: &rules_for_test_stage
|
||||||
|
rules:
|
||||||
|
- if: $CI_PIPELINE_SOURCE != 'schedule'
|
||||||
|
when: always
|
||||||
|
- if: $CI_PIPELINE_SOURCE == 'schedule'
|
||||||
|
when: never
|
||||||
|
|
||||||
are-database-scripts-in-sync:
|
are-database-scripts-in-sync:
|
||||||
stage: test
|
stage: test
|
||||||
|
<<: *rules_for_test_stage
|
||||||
script:
|
script:
|
||||||
- ./src/share/database/scripts/utils/are-scripts-in-sync.py
|
- ./src/share/database/scripts/utils/are-scripts-in-sync.py
|
||||||
|
|
||||||
check-for-json-errors-in-doc:
|
check-for-json-errors-in-doc:
|
||||||
stage: test
|
stage: test
|
||||||
|
<<: *rules_for_test_stage
|
||||||
script:
|
script:
|
||||||
- ./tools/check-for-json-errors-in-doc.sh
|
- ./tools/check-for-json-errors-in-doc.sh
|
||||||
|
|
||||||
danger:
|
danger:
|
||||||
stage: test
|
stage: test
|
||||||
|
<<: *rules_for_test_stage
|
||||||
before_script:
|
before_script:
|
||||||
- export CI_MERGE_REQUEST_ID=$(git ls-remote -q origin merge-requests\*\head | grep $CI_COMMIT_SHA | sed 's/.*refs\/merge-requests\/\([0-9]*\)\/head/\1/g')
|
- export CI_MERGE_REQUEST_ID=$(git ls-remote -q origin merge-requests\*\head | grep $CI_COMMIT_SHA | sed 's/.*refs\/merge-requests\/\([0-9]*\)\/head/\1/g')
|
||||||
- export CI_PROJECT_PATH=$CI_PROJECT_ID #some version of gitlab has problems with searching by project path
|
- export CI_PROJECT_PATH=$CI_PROJECT_ID #some version of gitlab has problems with searching by project path
|
||||||
@ -38,26 +58,31 @@ danger:
|
|||||||
|
|
||||||
duplicate-includes:
|
duplicate-includes:
|
||||||
stage: test
|
stage: test
|
||||||
|
<<: *rules_for_test_stage
|
||||||
script:
|
script:
|
||||||
- ./tools/check-for-duplicate-includes.sh
|
- ./tools/check-for-duplicate-includes.sh
|
||||||
|
|
||||||
duplicate-log-messages:
|
duplicate-log-messages:
|
||||||
stage: test
|
stage: test
|
||||||
|
<<: *rules_for_test_stage
|
||||||
script:
|
script:
|
||||||
- ./tools/check-messages.py
|
- ./tools/check-messages.py
|
||||||
|
|
||||||
uninstalled-headers:
|
uninstalled-headers:
|
||||||
stage: test
|
stage: test
|
||||||
|
<<: *rules_for_test_stage
|
||||||
script:
|
script:
|
||||||
- ./tools/find-uninstalled-headers.py
|
- ./tools/find-uninstalled-headers.py
|
||||||
|
|
||||||
missing-api-commands:
|
missing-api-commands:
|
||||||
stage: test
|
stage: test
|
||||||
|
<<: *rules_for_test_stage
|
||||||
script:
|
script:
|
||||||
- ./tools/check-for-missing-api-commands.sh
|
- ./tools/check-for-missing-api-commands.sh
|
||||||
|
|
||||||
missing-config-h-include:
|
missing-config-h-include:
|
||||||
stage: test
|
stage: test
|
||||||
|
<<: *rules_for_test_stage
|
||||||
script:
|
script:
|
||||||
- FILES=$(./tools/add-config-h.sh -n)
|
- FILES=$(./tools/add-config-h.sh -n)
|
||||||
- printf '%s\n' "${FILES}"
|
- printf '%s\n' "${FILES}"
|
||||||
@ -65,6 +90,7 @@ missing-config-h-include:
|
|||||||
|
|
||||||
missing-git-attribute:
|
missing-git-attribute:
|
||||||
stage: test
|
stage: test
|
||||||
|
<<: *rules_for_test_stage
|
||||||
script:
|
script:
|
||||||
- git_diff=$(git diff)
|
- git_diff=$(git diff)
|
||||||
- if test -n "${git_diff}"; then printf '%s\n\ngit diff should be empty here under all circumstances. CI broken?\n' "${git_diff}"; exit 1; fi
|
- if test -n "${git_diff}"; then printf '%s\n\ngit diff should be empty here under all circumstances. CI broken?\n' "${git_diff}"; exit 1; fi
|
||||||
@ -74,6 +100,7 @@ missing-git-attribute:
|
|||||||
|
|
||||||
shellcheck:
|
shellcheck:
|
||||||
stage: test
|
stage: test
|
||||||
|
<<: *rules_for_test_stage
|
||||||
script:
|
script:
|
||||||
- ./tools/shellcheck-all.sh
|
- ./tools/shellcheck-all.sh
|
||||||
|
|
||||||
@ -87,11 +114,14 @@ shellcheck:
|
|||||||
- if test -z "${PYTHON_SCRIPTS}"; then echo "No python scripts to check. Exiting early."; exit 0; fi
|
- if test -z "${PYTHON_SCRIPTS}"; then echo "No python scripts to check. Exiting early."; exit 0; fi
|
||||||
|
|
||||||
bandit:
|
bandit:
|
||||||
|
stage: test
|
||||||
|
<<: *rules_for_test_stage
|
||||||
script:
|
script:
|
||||||
- bandit -r ./src -x ./.git
|
- bandit -r ./src -x ./.git
|
||||||
|
|
||||||
pycodestyle:
|
pycodestyle:
|
||||||
stage: test
|
stage: test
|
||||||
|
<<: *rules_for_test_stage
|
||||||
script:
|
script:
|
||||||
# - *get_modified_files
|
# - *get_modified_files
|
||||||
# - INPUT="${MODIFIED_FILES}"
|
# - INPUT="${MODIFIED_FILES}"
|
||||||
@ -100,6 +130,7 @@ pycodestyle:
|
|||||||
|
|
||||||
pylint:
|
pylint:
|
||||||
stage: test
|
stage: test
|
||||||
|
<<: *rules_for_test_stage
|
||||||
script:
|
script:
|
||||||
# - *get_modified_files
|
# - *get_modified_files
|
||||||
# - INPUT="${MODIFIED_FILES}"
|
# - INPUT="${MODIFIED_FILES}"
|
||||||
@ -108,6 +139,150 @@ pylint:
|
|||||||
# If we reached this point, it means pylint passed. Run again with all warnings enabled, but ignore the return code to show a list of improvements that the developer could do, even when CI is passing.
|
# If we reached this point, it means pylint passed. Run again with all warnings enabled, but ignore the return code to show a list of improvements that the developer could do, even when CI is passing.
|
||||||
- pylint --jobs "$(nproc || gnproc || echo 1)" --rcfile ./.gitlab/ci/pylint.rc --enable all ${PYTHON_SCRIPTS} || true
|
- pylint --jobs "$(nproc || gnproc || echo 1)" --rcfile ./.gitlab/ci/pylint.rc --enable all ${PYTHON_SCRIPTS} || true
|
||||||
|
|
||||||
|
|
||||||
|
############################## Fuzzing ##############################
|
||||||
|
|
||||||
|
# Fuzz code changes. Fuzzes all merge requests.
|
||||||
|
fuzz:
|
||||||
|
image:
|
||||||
|
name: "${CFL_IMAGE}"
|
||||||
|
entrypoint: ['']
|
||||||
|
stage: fuzz
|
||||||
|
tags:
|
||||||
|
- docker-fuzz
|
||||||
|
needs: []
|
||||||
|
parallel:
|
||||||
|
matrix:
|
||||||
|
- SANITIZER: [address, undefined]
|
||||||
|
rules:
|
||||||
|
# On merge request.
|
||||||
|
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
||||||
|
variables:
|
||||||
|
MODE: "code-change"
|
||||||
|
when: manual
|
||||||
|
allow_failure: true
|
||||||
|
# And on push to master.
|
||||||
|
- if: $CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH
|
||||||
|
when: always
|
||||||
|
before_script:
|
||||||
|
# Get GitLab's container id.
|
||||||
|
- export CFL_CONTAINER_ID=`docker ps -q -f "label=com.gitlab.gitlab-runner.job.id=$CI_JOB_ID" -f "label=com.gitlab.gitlab-runner.type=build"`
|
||||||
|
script:
|
||||||
|
# local cfl-cache to mounted volume
|
||||||
|
- if ! test -L cfl-cache; then ln -s /cfl-cache cfl-cache; fi
|
||||||
|
# Will build and run the fuzzers.
|
||||||
|
- python3 "/opt/oss-fuzz/infra/cifuzz/cifuzz_combined_entrypoint.py"
|
||||||
|
artifacts:
|
||||||
|
# Upload artifacts when a crash makes the job fail.
|
||||||
|
when: always
|
||||||
|
expire_in: 30 days
|
||||||
|
paths:
|
||||||
|
- "${CFL_ARTIFACTS_DIR}"
|
||||||
|
|
||||||
|
# Batch fuzzing enables continuous, regular fuzzing on your latest HEAD
|
||||||
|
# and allows a corpus of inputs to build up over time, which greatly improves
|
||||||
|
# the effectiveness of fuzzing. Batch fuzzing should be run on a schedule.
|
||||||
|
fuzz-batch:
|
||||||
|
image:
|
||||||
|
name: "${CFL_IMAGE}"
|
||||||
|
entrypoint: ['']
|
||||||
|
stage: fuzz
|
||||||
|
needs: []
|
||||||
|
tags:
|
||||||
|
- docker-fuzz
|
||||||
|
variables:
|
||||||
|
FUZZ_SECONDS: 86400 # 24 hours
|
||||||
|
rules:
|
||||||
|
- if: $MODE == "batch"
|
||||||
|
before_script:
|
||||||
|
- export CFL_CONTAINER_ID=`docker ps -q -f "label=com.gitlab.gitlab-runner.job.id=$CI_JOB_ID" -f "label=com.gitlab.gitlab-runner.type=build"`
|
||||||
|
script:
|
||||||
|
- python3 "/opt/oss-fuzz/infra/cifuzz/cifuzz_combined_entrypoint.py"
|
||||||
|
artifacts:
|
||||||
|
when: always
|
||||||
|
expire_in: 30 days
|
||||||
|
paths:
|
||||||
|
- "${CFL_ARTIFACTS_DIR}"
|
||||||
|
|
||||||
|
# Corpus pruning is a helper function that minimizes the corpuses by
|
||||||
|
# removing corpus files (testcases) that do not increase the fuzzer’s
|
||||||
|
# code coverage.
|
||||||
|
fuzz-prune:
|
||||||
|
image:
|
||||||
|
name: "${CFL_IMAGE}"
|
||||||
|
entrypoint: ['']
|
||||||
|
stage: fuzz
|
||||||
|
needs: []
|
||||||
|
tags:
|
||||||
|
- docker-fuzz
|
||||||
|
rules:
|
||||||
|
- if: $MODE == "prune"
|
||||||
|
before_script:
|
||||||
|
- export CFL_CONTAINER_ID=`docker ps -q -f "label=com.gitlab.gitlab-runner.job.id=$CI_JOB_ID" -f "label=com.gitlab.gitlab-runner.type=build"`
|
||||||
|
script:
|
||||||
|
- python3 "/opt/oss-fuzz/infra/cifuzz/cifuzz_combined_entrypoint.py"
|
||||||
|
artifacts:
|
||||||
|
when: always
|
||||||
|
expire_in: 30 days
|
||||||
|
paths:
|
||||||
|
- "${CFL_ARTIFACTS_DIR}"
|
||||||
|
|
||||||
|
# Continuous builds are used when a crash is found during MR fuzzing to determine
|
||||||
|
# whether the crash was newly introduced. If the crash was not newly introduced,
|
||||||
|
# MR fuzzing will not report it. This means that there will be fewer unrelated
|
||||||
|
# failures when running code change fuzzing.
|
||||||
|
fuzz-build:
|
||||||
|
image:
|
||||||
|
name: "${CFL_IMAGE}"
|
||||||
|
entrypoint: ['']
|
||||||
|
stage: fuzz
|
||||||
|
needs: []
|
||||||
|
tags:
|
||||||
|
- docker-fuzz
|
||||||
|
rules:
|
||||||
|
# Use $CI_DEFAULT_BRANCH or $CFL_BRANCH.
|
||||||
|
- if: $CI_COMMIT_BRANCH == $CFL_BRANCH && $CI_PIPELINE_SOURCE == "push"
|
||||||
|
variables:
|
||||||
|
MODE: "code-change"
|
||||||
|
UPLOAD_BUILD: "true"
|
||||||
|
before_script:
|
||||||
|
- export CFL_CONTAINER_ID=`docker ps -q -f "label=com.gitlab.gitlab-runner.job.id=$CI_JOB_ID" -f "label=com.gitlab.gitlab-runner.type=build"`
|
||||||
|
script:
|
||||||
|
- python3 "/opt/oss-fuzz/infra/cifuzz/cifuzz_combined_entrypoint.py"
|
||||||
|
artifacts:
|
||||||
|
when: always
|
||||||
|
expire_in: 30 days
|
||||||
|
paths:
|
||||||
|
- "${CFL_ARTIFACTS_DIR}"
|
||||||
|
|
||||||
|
# scheduled job - generates periodic coverage reports
|
||||||
|
fuzz-coverage:
|
||||||
|
image:
|
||||||
|
name: "${CFL_IMAGE}"
|
||||||
|
entrypoint: ['']
|
||||||
|
stage: fuzz
|
||||||
|
needs: []
|
||||||
|
tags:
|
||||||
|
- docker-fuzz
|
||||||
|
variables:
|
||||||
|
SANITIZER: "coverage"
|
||||||
|
rules:
|
||||||
|
- if: $MODE == "coverage"
|
||||||
|
before_script:
|
||||||
|
- export CFL_CONTAINER_ID=`cut -c9- < /proc/1/cpuset`
|
||||||
|
script:
|
||||||
|
- python3 "/opt/oss-fuzz/infra/cifuzz/cifuzz_combined_entrypoint.py"
|
||||||
|
after_script:
|
||||||
|
- shasum /opt/kea/sbin/*
|
||||||
|
- shasum /tmp/not-out/*/*
|
||||||
|
- shasum ${OUT}/*/*
|
||||||
|
artifacts:
|
||||||
|
when: always
|
||||||
|
expire_in: 30 days
|
||||||
|
paths:
|
||||||
|
- "${CFL_ARTIFACTS_DIR}"
|
||||||
|
|
||||||
|
|
||||||
############################### SAST ################################
|
############################### SAST ################################
|
||||||
# Read more about this feature here: https://docs.gitlab.com/ee/user/application_security/sast/
|
# Read more about this feature here: https://docs.gitlab.com/ee/user/application_security/sast/
|
||||||
#
|
#
|
||||||
@ -119,9 +294,16 @@ include:
|
|||||||
|
|
||||||
.sast-analyzer:
|
.sast-analyzer:
|
||||||
extends: sast
|
extends: sast
|
||||||
|
stage: test
|
||||||
|
<<: *rules_for_test_stage
|
||||||
allow_failure: true
|
allow_failure: true
|
||||||
script:
|
script:
|
||||||
- /analyzer run
|
- /analyzer run
|
||||||
|
rules:
|
||||||
|
- if: $SAST_DISABLED
|
||||||
|
when: never
|
||||||
|
- if: $CI_PIPELINE_SOURCE == 'schedule'
|
||||||
|
when: never
|
||||||
|
|
||||||
flawfinder-sast:
|
flawfinder-sast:
|
||||||
extends: .sast-analyzer
|
extends: .sast-analyzer
|
||||||
@ -131,11 +313,12 @@ flawfinder-sast:
|
|||||||
SAST_ANALYZER_IMAGE_TAG: latest
|
SAST_ANALYZER_IMAGE_TAG: latest
|
||||||
SAST_ANALYZER_IMAGE: "$SECURE_ANALYZERS_PREFIX/flawfinder:$SAST_ANALYZER_IMAGE_TAG"
|
SAST_ANALYZER_IMAGE: "$SECURE_ANALYZERS_PREFIX/flawfinder:$SAST_ANALYZER_IMAGE_TAG"
|
||||||
rules:
|
rules:
|
||||||
- if: $SAST_DISABLED
|
|
||||||
when: never
|
|
||||||
- if: $SAST_EXCLUDED_ANALYZERS =~ /flawfinder/
|
- if: $SAST_EXCLUDED_ANALYZERS =~ /flawfinder/
|
||||||
when: never
|
when: never
|
||||||
- if: $CI_COMMIT_BRANCH
|
- if: $CI_COMMIT_BRANCH
|
||||||
exists:
|
exists:
|
||||||
- '**/*.cc'
|
- '**/*.cc'
|
||||||
- '**/*.h'
|
- '**/*.h'
|
||||||
|
|
||||||
|
semgrep-sast:
|
||||||
|
extends: .sast-analyzer
|
||||||
|
@ -2,7 +2,7 @@ ACLOCAL_AMFLAGS = -I m4macros ${ACLOCAL_FLAGS}
|
|||||||
# ^^^^^^^^ This has to be the first line and cannot come later in this
|
# ^^^^^^^^ This has to be the first line and cannot come later in this
|
||||||
# Makefile.am due to some bork in some versions of autotools.
|
# Makefile.am due to some bork in some versions of autotools.
|
||||||
|
|
||||||
SUBDIRS = tools . ext src doc m4macros @PREMIUM_DIR@ @CONTRIB_DIR@
|
SUBDIRS = tools . ext src fuzz doc m4macros @PREMIUM_DIR@ @CONTRIB_DIR@
|
||||||
|
|
||||||
USE_LCOV=@USE_LCOV@
|
USE_LCOV=@USE_LCOV@
|
||||||
LCOV=@LCOV@
|
LCOV=@LCOV@
|
||||||
@ -173,7 +173,6 @@ cppcheck:
|
|||||||
docs:
|
docs:
|
||||||
$(MAKE) -C doc/sphinx
|
$(MAKE) -C doc/sphinx
|
||||||
|
|
||||||
|
|
||||||
# These steps are necessary during installation
|
# These steps are necessary during installation
|
||||||
install-exec-hook:
|
install-exec-hook:
|
||||||
mkdir -p $(DESTDIR)${localstatedir}/log/
|
mkdir -p $(DESTDIR)${localstatedir}/log/
|
||||||
@ -188,3 +187,5 @@ CLEANFILES = $(abs_top_builddir)/logger_lockfile
|
|||||||
# config.h may be included by headers supplied for building user-written
|
# config.h may be included by headers supplied for building user-written
|
||||||
# hooks libraries, so we need to include it in the distribution.
|
# hooks libraries, so we need to include it in the distribution.
|
||||||
pkginclude_HEADERS = config.h kea_version.h
|
pkginclude_HEADERS = config.h kea_version.h
|
||||||
|
|
||||||
|
.PHONY: clean-coverage coverage cppcheck docs report-coverage
|
||||||
|
@ -1428,6 +1428,10 @@ AC_ARG_ENABLE([fuzzing],
|
|||||||
AC_MSG_RESULT("no. Fuzzing requires C++17 support.")
|
AC_MSG_RESULT("no. Fuzzing requires C++17 support.")
|
||||||
AC_MSG_ERROR("Fuzzing requires C++17 support.")
|
AC_MSG_ERROR("Fuzzing requires C++17 support.")
|
||||||
fi
|
fi
|
||||||
|
if test "${enable_gtest}" = 'no'; then
|
||||||
|
AC_MSG_RESULT("no. Fuzzing requires gtest to be enabled.")
|
||||||
|
AC_MSG_ERROR("Fuzzing requires gtest to be enabled.")
|
||||||
|
fi
|
||||||
enable_fuzzing=${enableval}],
|
enable_fuzzing=${enableval}],
|
||||||
[enable_fuzzing=no]
|
[enable_fuzzing=no]
|
||||||
)
|
)
|
||||||
@ -1503,6 +1507,7 @@ if (echo ${runstatedir} | grep -q localstatedir); then
|
|||||||
runstatedir="$(eval echo ${runstatedir})"
|
runstatedir="$(eval echo ${runstatedir})"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
AC_CONFIG_FILES([kea_version.h])
|
||||||
AC_CONFIG_FILES([Makefile])
|
AC_CONFIG_FILES([Makefile])
|
||||||
AC_CONFIG_FILES([doc/Makefile])
|
AC_CONFIG_FILES([doc/Makefile])
|
||||||
AC_CONFIG_FILES([doc/sphinx/Makefile])
|
AC_CONFIG_FILES([doc/sphinx/Makefile])
|
||||||
@ -1510,7 +1515,9 @@ AC_CONFIG_FILES([doc/devel/Makefile])
|
|||||||
AC_CONFIG_FILES([ext/Makefile])
|
AC_CONFIG_FILES([ext/Makefile])
|
||||||
AC_CONFIG_FILES([ext/gtest/Makefile])
|
AC_CONFIG_FILES([ext/gtest/Makefile])
|
||||||
AC_CONFIG_FILES([ext/coroutine/Makefile])
|
AC_CONFIG_FILES([ext/coroutine/Makefile])
|
||||||
AC_CONFIG_FILES([kea_version.h])
|
AC_CONFIG_FILES([fuzz/Makefile])
|
||||||
|
AC_CONFIG_FILES([fuzz/input/Makefile])
|
||||||
|
AC_CONFIG_FILES([fuzz/tests/Makefile])
|
||||||
AC_CONFIG_FILES([m4macros/Makefile])
|
AC_CONFIG_FILES([m4macros/Makefile])
|
||||||
AC_CONFIG_FILES([src/Makefile])
|
AC_CONFIG_FILES([src/Makefile])
|
||||||
AC_CONFIG_FILES([src/bin/Makefile])
|
AC_CONFIG_FILES([src/bin/Makefile])
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
// Copyright (C) 2017-2021 Internet Systems Consortium, Inc. ("ISC")
|
// Copyright (C) 2017-2024 Internet Systems Consortium, Inc. ("ISC")
|
||||||
//
|
//
|
||||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
||||||
@page fuzzer Fuzzing Kea
|
@page fuzzer Fuzzing Kea
|
||||||
|
|
||||||
@section fuzzIntro Introduction
|
@section fuzzIntro Introduction
|
||||||
@ -13,13 +14,125 @@ Fuzzing is a software-testing technique whereby a program is presented with a
|
|||||||
variety of generated data as input and is monitored for abnormal conditions
|
variety of generated data as input and is monitored for abnormal conditions
|
||||||
such as crashes or hangs.
|
such as crashes or hangs.
|
||||||
|
|
||||||
Fuzz testing of Kea uses the AFL (American Fuzzy Lop) program. In this, Kea is
|
There are two ways to fuzz Kea.
|
||||||
built using an AFL-supplied program that not only compiles the software but
|
|
||||||
also instruments it. When run, AFL generates test cases and monitors the
|
|
||||||
execution of Kea as it processes them. AFL will adjust the input based on
|
|
||||||
these measurements, seeking to discover and test new execution paths.
|
|
||||||
|
|
||||||
@section fuzzTypes Types of Kea Fuzzing
|
Option 1. With the libfuzzer harness function LLVMFuzzerTestOneInput.
|
||||||
|
|
||||||
|
Option 2. With the AFL (American Fuzzy Lop) compiler.
|
||||||
|
|
||||||
|
@section LLVMFuzzerTestOneInput Using the LLVMFuzzerTestOneInput Harness Function
|
||||||
|
|
||||||
|
This mode of fuzzing works with virtually any compiler.
|
||||||
|
|
||||||
|
There are four types of fuzzers implemented with this mode:
|
||||||
|
- Config fuzzer
|
||||||
|
- HTTP endpoint fuzzer
|
||||||
|
- Packet fuzzer
|
||||||
|
- Unix socket fuzzer
|
||||||
|
|
||||||
|
There are two binaries under test:
|
||||||
|
- `kea-dhcp4`
|
||||||
|
- `kea-dhcp6`
|
||||||
|
|
||||||
|
Combining the binaries and the fuzzer types results in seven fuzzing binaries:
|
||||||
|
- `fuzz/fuzz-config-kea-dhcp4`
|
||||||
|
- `fuzz/fuzz-config-kea-dhcp6`
|
||||||
|
- `fuzz/fuzz-http-endpoint`
|
||||||
|
- `fuzz/fuzz-packets-kea-dhcp4`
|
||||||
|
- `fuzz/fuzz-packets-kea-dhcp6`
|
||||||
|
- `fuzz/fuzz-unix-socket-kea-dhcp4`
|
||||||
|
- `fuzz/fuzz-unix-socket-kea-dhcp6`
|
||||||
|
|
||||||
|
@subsection HowToBuild How to Build the LLVM Fuzzer
|
||||||
|
|
||||||
|
Use the "--enable-fuzzing" during the configure step. Then just compile as usual.
|
||||||
|
|
||||||
|
@code
|
||||||
|
./configure --enable-fuzzing
|
||||||
|
make
|
||||||
|
@endcode
|
||||||
|
|
||||||
|
You can check that `config.report` shows these lines:
|
||||||
|
|
||||||
|
@code
|
||||||
|
Developer:
|
||||||
|
[...]
|
||||||
|
Fuzzing: yes
|
||||||
|
AFL: no
|
||||||
|
@endcode
|
||||||
|
|
||||||
|
Compiling with AFL is permitted, but is not required.
|
||||||
|
|
||||||
|
@subsection HowToRun How to Run the LLVM Fuzzer
|
||||||
|
|
||||||
|
Each of these binaries has two ways to run. It tries to find a directory called
|
||||||
|
`input/<name>` relative to the binary where `<name>` is the name of the binary.
|
||||||
|
|
||||||
|
- The first mode: if the directory exists, it recursively takes all the files
|
||||||
|
from that directory and provides them as fuzz input one-by-one. All the fuzzers
|
||||||
|
have an empty file and a one-byte file as inputs committed to the repository.
|
||||||
|
Config fuzzers also have all the files in `doc/examples/kea[46]` symlinked.
|
||||||
|
|
||||||
|
- The second mode: if the directory doesn't exist, then it accepts input from
|
||||||
|
stdin, just like the old fuzzer did. In this mode, a fuzzer engine can be run on
|
||||||
|
it. This is the mode used in CI.
|
||||||
|
|
||||||
|
After compiling, all the fuzzers can be run with `make check` in the `fuzz`
|
||||||
|
directory. The reasoning behind this is that while writing code, developers can
|
||||||
|
quickly check if anything is broken. Obviously, this is not real fuzzing as long
|
||||||
|
since the input from the `fuzz/input` directory is the same, but it rather tests
|
||||||
|
if the fuzzers were broken during development.
|
||||||
|
|
||||||
|
`make check` runs these fuzzers with `sudo`. It may interrupt the process asking
|
||||||
|
for a password on systems that don't have passwordless root set up.
|
||||||
|
|
||||||
|
@subsection FuzzingStructure The Code Structure of the LLVM Fuzzer
|
||||||
|
|
||||||
|
The following functions are required to be implemented in each new fuzzer:
|
||||||
|
|
||||||
|
- `int LLVMFuzzerInitialize();` - Does initialization that is required by the
|
||||||
|
fuzzing. Is only run once. Is run automatically. It does not need to be run
|
||||||
|
explicitly by the fuzzing engine.
|
||||||
|
|
||||||
|
- `int LLVMFuzzerTearDown();` - Cleans up the setup like removing leftover
|
||||||
|
files. Is automatically run at the beginning and the end of the fuzzing. It does
|
||||||
|
not need to be run explicitly by the fuzzing engine.
|
||||||
|
|
||||||
|
- `int LLVMFuzzerTestOneInput(uint8_t const* data, size_t size);` - Implements
|
||||||
|
the actual fuzzing. Takes the parameter input and achieves the object of the
|
||||||
|
fuzzing with it. It needs to start with
|
||||||
|
`static bool initialized(DoInitialization());` to do the initialization only
|
||||||
|
once. This function is the only one that needs to be run explicitly by the
|
||||||
|
fuzzing engine.
|
||||||
|
|
||||||
|
The following functions are common to all fuzzers:
|
||||||
|
|
||||||
|
- `int main(int, char* argv[])` - Implements the input searching mentioned
|
||||||
|
above.
|
||||||
|
|
||||||
|
- `bool DoInitialization();` - Sets up logging to prevent spurious logging and
|
||||||
|
calls `int LLVMFuzzerInitialize();`.
|
||||||
|
|
||||||
|
- `void writeToFile(std::string const& file, std::string const& content);` -
|
||||||
|
A helpful function to write to file used in some fuzzers.
|
||||||
|
|
||||||
|
@subsection FuzzingConsiderations Development Considerations About LLVM Fuzzing in Kea
|
||||||
|
|
||||||
|
Exceptions make it difficult to maintain a fuzzer. We have to triage some of the
|
||||||
|
exceptions. For example, JSONError is thrown when an invalid JSON is provided as
|
||||||
|
input in config fuzzing. That leads to a core dump and can be interpreted as a
|
||||||
|
crash by the fuzzing engine, which is not really what we're interested in
|
||||||
|
because this exception is caught in the kea-dhcp[46] binaries. The old way of
|
||||||
|
fuzzing may have been better from this point of view, because there was the
|
||||||
|
guarantee that the right exceptions were caught and nothing more and we didn't
|
||||||
|
have to pay attention to what exceptions needed to be ignored and which weren't.
|
||||||
|
|
||||||
|
@section usingAFL Using AFL
|
||||||
|
|
||||||
|
In this, Kea is built using an AFL-supplied program that not only compiles the
|
||||||
|
software but also instruments it. When run, AFL generates test cases and
|
||||||
|
monitors the execution of Kea as it processes them. AFL will adjust the input
|
||||||
|
based on these measurements, seeking to discover and test new execution paths.
|
||||||
|
|
||||||
@subsection fuzzTypeNetwork Fuzzing with Network Packets
|
@subsection fuzzTypeNetwork Fuzzing with Network Packets
|
||||||
|
|
||||||
@ -38,7 +151,7 @@ dictionary of valid keywords and runs Kea in configuration file check mode on
|
|||||||
them. As with network packet fuzzing, the behaviour of Kea is monitored and
|
them. As with network packet fuzzing, the behaviour of Kea is monitored and
|
||||||
the content of subsequent files adjusted accordingly.
|
the content of subsequent files adjusted accordingly.
|
||||||
|
|
||||||
@section fuzzBuild Building Kea for Fuzzing
|
@subsection fuzzBuild Building Kea for Fuzzing
|
||||||
|
|
||||||
Whatever tests are done, Kea needs to be built with fuzzing in mind. The steps
|
Whatever tests are done, Kea needs to be built with fuzzing in mind. The steps
|
||||||
for this are:
|
for this are:
|
||||||
@ -53,15 +166,15 @@ for this are:
|
|||||||
|
|
||||||
-# Build Kea. Kea should be compiled and built as usual, although the
|
-# Build Kea. Kea should be compiled and built as usual, although the
|
||||||
following additional steps should be observed:
|
following additional steps should be observed:
|
||||||
- Set the environment variable CXX to point to the afl-clang-fast++
|
- Set the environment variable CXX to point to the afl-clang-fast
|
||||||
compiler.
|
compiler.
|
||||||
- Specify a value of "--prefix" on the command line to set the directory
|
- Specify a value of "--prefix" on the command line to set the directory
|
||||||
into which Kea is installed.
|
into which Kea is installed.
|
||||||
- Add the "--enable-fuzz" switch to the "configure" command line.
|
- Add the "--enable-fuzzing" switch to the "configure" command line.
|
||||||
.
|
.
|
||||||
For example:
|
For example:
|
||||||
@code
|
@code
|
||||||
CXX=/opt/afl/afl-clang-fast++ ./configure --enable-fuzz --prefix=$HOME/installed
|
CXX=afl-clang-fast ./configure --enable-fuzzing --prefix=$HOME/installed
|
||||||
make
|
make
|
||||||
@endcode
|
@endcode
|
||||||
|
|
||||||
@ -80,8 +193,6 @@ for this are:
|
|||||||
simpler to install the programs in the directories set by "--prefix" and run
|
simpler to install the programs in the directories set by "--prefix" and run
|
||||||
them from there.
|
them from there.
|
||||||
|
|
||||||
@section fuzzRun Running the Fuzzer
|
|
||||||
|
|
||||||
@subsection fuzzRunNetwork Fuzzing with Network Packets
|
@subsection fuzzRunNetwork Fuzzing with Network Packets
|
||||||
|
|
||||||
-# In this type of fuzzing, Kea is processing packets from the fuzzer over a
|
-# In this type of fuzzing, Kea is processing packets from the fuzzer over a
|
||||||
@ -95,20 +206,20 @@ for this are:
|
|||||||
using the loopback interface "lo" and IPv4 address 10.53.0.1, the
|
using the loopback interface "lo" and IPv4 address 10.53.0.1, the
|
||||||
configuration file would contain the following snippet:
|
configuration file would contain the following snippet:
|
||||||
@code
|
@code
|
||||||
|
{
|
||||||
"Dhcp4": {
|
"Dhcp4": {
|
||||||
:
|
|
||||||
"interfaces-config": {
|
"interfaces-config": {
|
||||||
"interfaces": ["lo/10.53.0.1"]
|
"interfaces": [
|
||||||
|
"lo/10.53.0.1"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"subnet4": [
|
"subnet4": [
|
||||||
{
|
{
|
||||||
:
|
"interface": "lo"
|
||||||
"interface": "lo",
|
|
||||||
:
|
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
:
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
@endcode
|
@endcode
|
||||||
|
|
||||||
-# The specification of the interface and address in the configuration file
|
-# The specification of the interface and address in the configuration file
|
||||||
@ -140,7 +251,7 @@ for this are:
|
|||||||
data. Ensure that only the payload of the UDP packet is exported.
|
data. Ensure that only the payload of the UDP packet is exported.
|
||||||
- The "-o" switch specifies a directory (in this example called "fuzz-out")
|
- The "-o" switch specifies a directory (in this example called "fuzz-out")
|
||||||
that AFL will use to hold packets it has generated and packets that it has
|
that AFL will use to hold packets it has generated and packets that it has
|
||||||
found causes crashes or hangs.
|
found causing crashes or hangs.
|
||||||
- "--" Separates the AFL command line from that of Kea.
|
- "--" Separates the AFL command line from that of Kea.
|
||||||
- "./kea-dhcp6" is the program being fuzzed. As mentioned above, this
|
- "./kea-dhcp6" is the program being fuzzed. As mentioned above, this
|
||||||
should be an executable image, and it will be simpler to fuzz one
|
should be an executable image, and it will be simpler to fuzz one
|
||||||
@ -164,7 +275,7 @@ for this are:
|
|||||||
@subsection fuzzRunConfig Fuzzing with Configuration Files
|
@subsection fuzzRunConfig Fuzzing with Configuration Files
|
||||||
|
|
||||||
AFL can be used to check the parsing of the configuration files. In this type
|
AFL can be used to check the parsing of the configuration files. In this type
|
||||||
of fuzzing, AFL generates configuration files which is passes to Kea to check.
|
of fuzzing, AFL generates configuration files which it passes to Kea to check.
|
||||||
Steps for this fuzzing are:
|
Steps for this fuzzing are:
|
||||||
|
|
||||||
-# Build Kea as described above.
|
-# Build Kea as described above.
|
||||||
@ -201,8 +312,6 @@ Steps for this fuzzing are:
|
|||||||
will replace these with the name of a file it has created when starting
|
will replace these with the name of a file it has created when starting
|
||||||
Kea.
|
Kea.
|
||||||
|
|
||||||
@section Fuzzing Internals
|
|
||||||
|
|
||||||
@subsection fuzzInternalNetwork Fuzzing with Network Packets
|
@subsection fuzzInternalNetwork Fuzzing with Network Packets
|
||||||
|
|
||||||
The AFL fuzzer delivers packets to Kea's stdin. Although the part of Kea
|
The AFL fuzzer delivers packets to Kea's stdin. Although the part of Kea
|
||||||
@ -217,7 +326,7 @@ while (not shutting down) {
|
|||||||
Read and process one packet
|
Read and process one packet
|
||||||
}
|
}
|
||||||
@endcode
|
@endcode
|
||||||
When --enable-fuzz is specified, this is conceptually modified to:
|
When --enable-fuzzing is specified, this is conceptually modified to:
|
||||||
@code{.unparsed}
|
@code{.unparsed}
|
||||||
while (not shutting down) {
|
while (not shutting down) {
|
||||||
Read stdin and copy data to address/port on which Kea is listening
|
Read stdin and copy data to address/port on which Kea is listening
|
||||||
@ -225,10 +334,10 @@ while (not shutting down) {
|
|||||||
}
|
}
|
||||||
@endcode
|
@endcode
|
||||||
|
|
||||||
Implementation is via an object of class "Fuzz". When created, it identifies
|
Implementation is via an object of class "PacketFuzzer". When created, it
|
||||||
an interface, address and port on which Kea is listening and creates the
|
identifies an interface, address and port on which Kea is listening and creates
|
||||||
appropriate address structures for these. The port is passed as an argument to
|
the appropriate address structures for these. The port is passed as an argument
|
||||||
the constructor because at the point at which the object is constructed, that
|
to the constructor because at the point at which the object is constructed, that
|
||||||
information is readily available. The interface and address are picked up from
|
information is readily available. The interface and address are picked up from
|
||||||
the environment variables mentioned above. Consideration was given to
|
the environment variables mentioned above. Consideration was given to
|
||||||
extracting the interface and address information from the configuration file,
|
extracting the interface and address information from the configuration file,
|
||||||
@ -264,7 +373,7 @@ leaks).
|
|||||||
No changes were required to Kea source code to fuzz configuration files. In
|
No changes were required to Kea source code to fuzz configuration files. In
|
||||||
fact, other than compiling with afl-clang++ and installing the resultant
|
fact, other than compiling with afl-clang++ and installing the resultant
|
||||||
executable, no other steps are required. In particular, there is no need to
|
executable, no other steps are required. In particular, there is no need to
|
||||||
use the "--enable-fuzz" switch in the configuration command line (although
|
use the "--enable-fuzzing" switch in the configuration command line (although
|
||||||
doing so will not cause any problems).
|
doing so will not cause any problems).
|
||||||
|
|
||||||
@subsection fuzzThreads Changes Required for Multi-Threaded Kea
|
@subsection fuzzThreads Changes Required for Multi-Threaded Kea
|
||||||
@ -278,17 +387,15 @@ above was adopted for the single-threaded Kea 1.6. Should Kea be modified to
|
|||||||
become multi-threaded, the fuzzing code will need to be changed back to reading
|
become multi-threaded, the fuzzing code will need to be changed back to reading
|
||||||
the AFL input in the background.
|
the AFL input in the background.
|
||||||
|
|
||||||
@section fuzzNotes Notes
|
|
||||||
|
|
||||||
@subsection fuzzNotesUnitTests Unit Test Failures
|
@subsection fuzzNotesUnitTests Unit Test Failures
|
||||||
|
|
||||||
If unit tests are built when --enable-fuzzing is specified, note that tests
|
If unit tests are built when --enable-fuzzing is specified and with the AFL
|
||||||
which check or use the DHCP servers (i.e. the unit tests in src/bin/dhcp4,
|
compiler, note that tests which check or use the DHCP servers (i.e. the unit
|
||||||
src/bin/dhcp6 and src/bin/kea-admin) will fail. With no AFL-related
|
tests in src/bin/dhcp4, src/bin/dhcp6 and src/bin/kea-admin) will fail.
|
||||||
environment variables defined, a C++ exception will be thrown with the
|
With no AFL-related environment variables defined, a C++ exception will be
|
||||||
description "no fuzzing interface has been set". However, if the
|
thrown with the description "no fuzzing interface has been set".
|
||||||
KEA_AFL_INTERFACE and KEA_AFL_ADDRESS variables are set to valid values, the
|
However, if the `KEA_AFL_INTERFACE` and `KEA_AFL_ADDRESS` variables are set to
|
||||||
tests will hang.
|
valid values, the tests will hang.
|
||||||
|
|
||||||
Both these results are expected and should cause no concern. The exception is
|
Both these results are expected and should cause no concern. The exception is
|
||||||
thrown by the fuzzing object constructor when it attempts to create the address
|
thrown by the fuzzing object constructor when it attempts to create the address
|
||||||
@ -299,5 +406,4 @@ the test. (Should random input be supplied on stdin, e.g. from the keyboard,
|
|||||||
the test will most likely fail as the input is unlikely to be that expected by
|
the test will most likely fail as the input is unlikely to be that expected by
|
||||||
the test.)
|
the test.)
|
||||||
|
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
@ -168,8 +168,6 @@ Messages printed on debuglevel 40
|
|||||||
- DHCP4_CLASSES_ASSIGNED_AFTER_SUBNET_SELECTION
|
- DHCP4_CLASSES_ASSIGNED_AFTER_SUBNET_SELECTION
|
||||||
- DHCP4_CLASS_ASSIGNED
|
- DHCP4_CLASS_ASSIGNED
|
||||||
- DHCP4_CLASS_UNCONFIGURED
|
- DHCP4_CLASS_UNCONFIGURED
|
||||||
- DHCP4_CLASS_UNDEFINED
|
|
||||||
- DHCP4_CLASS_UNTESTABLE
|
|
||||||
- DHCP4_DHCP4O6_HOOK_SUBNET4_SELECT_DROP
|
- DHCP4_DHCP4O6_HOOK_SUBNET4_SELECT_DROP
|
||||||
- DHCP4_DHCP4O6_HOOK_SUBNET4_SELECT_SKIP
|
- DHCP4_DHCP4O6_HOOK_SUBNET4_SELECT_SKIP
|
||||||
- DHCP4_DHCP4O6_PACKET_RECEIVED
|
- DHCP4_DHCP4O6_PACKET_RECEIVED
|
||||||
@ -186,6 +184,8 @@ Messages printed on debuglevel 40
|
|||||||
- DHCP4_LEASE_QUERY_RECEIVED
|
- DHCP4_LEASE_QUERY_RECEIVED
|
||||||
- DHCP4_LEASE_QUERY_RESPONSE_SENT
|
- DHCP4_LEASE_QUERY_RESPONSE_SENT
|
||||||
- DHCP4_PACKET_QUEUE_FULL
|
- DHCP4_PACKET_QUEUE_FULL
|
||||||
|
- DHCP4_REQUIRED_CLASS_NO_TEST
|
||||||
|
- DHCP4_REQUIRED_CLASS_UNDEFINED
|
||||||
- DHCP4_SHUTDOWN
|
- DHCP4_SHUTDOWN
|
||||||
- DHCP4_SHUTDOWN_REQUEST
|
- DHCP4_SHUTDOWN_REQUEST
|
||||||
- DHCP6_BUFFER_RECEIVED
|
- DHCP6_BUFFER_RECEIVED
|
||||||
@ -193,8 +193,6 @@ Messages printed on debuglevel 40
|
|||||||
- DHCP6_CLASSES_ASSIGNED_AFTER_SUBNET_SELECTION
|
- DHCP6_CLASSES_ASSIGNED_AFTER_SUBNET_SELECTION
|
||||||
- DHCP6_CLASS_ASSIGNED
|
- DHCP6_CLASS_ASSIGNED
|
||||||
- DHCP6_CLASS_UNCONFIGURED
|
- DHCP6_CLASS_UNCONFIGURED
|
||||||
- DHCP6_CLASS_UNDEFINED
|
|
||||||
- DHCP6_CLASS_UNTESTABLE
|
|
||||||
- DHCP6_DHCP4O6_PACKET_RECEIVED
|
- DHCP6_DHCP4O6_PACKET_RECEIVED
|
||||||
- DHCP6_FLEX_ID
|
- DHCP6_FLEX_ID
|
||||||
- DHCP6_HOOK_BUFFER_SEND_SKIP
|
- DHCP6_HOOK_BUFFER_SEND_SKIP
|
||||||
@ -213,6 +211,8 @@ Messages printed on debuglevel 40
|
|||||||
- DHCP6_LEASE_QUERY_REPLY_SENT
|
- DHCP6_LEASE_QUERY_REPLY_SENT
|
||||||
- DHCP6_PACKET_PROCESS_FAIL
|
- DHCP6_PACKET_PROCESS_FAIL
|
||||||
- DHCP6_PACKET_QUEUE_FULL
|
- DHCP6_PACKET_QUEUE_FULL
|
||||||
|
- DHCP6_REQUIRED_CLASS_NO_TEST
|
||||||
|
- DHCP6_REQUIRED_CLASS_UNDEFINED
|
||||||
- DHCP6_REQUIRED_OPTIONS_CHECK_FAIL
|
- DHCP6_REQUIRED_OPTIONS_CHECK_FAIL
|
||||||
- DHCP6_SHUTDOWN
|
- DHCP6_SHUTDOWN
|
||||||
- DHCP6_SHUTDOWN_REQUEST
|
- DHCP6_SHUTDOWN_REQUEST
|
||||||
|
7
fuzz/.gitignore
vendored
Normal file
7
fuzz/.gitignore
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
/fuzz_config_kea_dhcp4
|
||||||
|
/fuzz_config_kea_dhcp6
|
||||||
|
/fuzz_http_endpoint
|
||||||
|
/fuzz_packets_kea_dhcp4
|
||||||
|
/fuzz_packets_kea_dhcp6
|
||||||
|
/fuzz_unix_socket_kea_dhcp4
|
||||||
|
/fuzz_unix_socket_kea_dhcp6
|
88
fuzz/Makefile.am
Normal file
88
fuzz/Makefile.am
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
SUBDIRS = . input tests
|
||||||
|
|
||||||
|
if FUZZING
|
||||||
|
|
||||||
|
AM_CPPFLAGS =
|
||||||
|
AM_CPPFLAGS += -I$(top_builddir)/src/bin -I$(top_srcdir)/src/bin
|
||||||
|
AM_CPPFLAGS += -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
|
||||||
|
AM_CPPFLAGS += -DKEA_FUZZ_DIR_INSTALLATION=\"$(datarootdir)/$(PACKAGE_NAME)/fuzzing\"
|
||||||
|
AM_CPPFLAGS += -DKEA_FUZZ_DIR_SOURCES=\"$(abs_top_builddir)/fuzz\"
|
||||||
|
AM_CPPFLAGS += -DKEA_LFC_INSTALLATION=\"$(prefix)/sbin/kea-lfc\"
|
||||||
|
AM_CPPFLAGS += -DKEA_LFC_SOURCES=\"$(abs_top_builddir)/src/bin/lfc/kea-lfc\"
|
||||||
|
AM_CPPFLAGS += $(BOOST_INCLUDES)
|
||||||
|
AM_CPPFLAGS += $(GTEST_INCLUDES)
|
||||||
|
|
||||||
|
AM_CXXFLAGS =
|
||||||
|
AM_CXXFLAGS += $(KEA_CXXFLAGS)
|
||||||
|
|
||||||
|
CLEANFILES = *.gcno *.gcda
|
||||||
|
|
||||||
|
sbin_PROGRAMS =
|
||||||
|
sbin_PROGRAMS += fuzz_config_kea_dhcp4
|
||||||
|
sbin_PROGRAMS += fuzz_config_kea_dhcp6
|
||||||
|
sbin_PROGRAMS += fuzz_http_endpoint
|
||||||
|
sbin_PROGRAMS += fuzz_packets_kea_dhcp4
|
||||||
|
sbin_PROGRAMS += fuzz_packets_kea_dhcp6
|
||||||
|
sbin_PROGRAMS += fuzz_unix_socket_kea_dhcp4
|
||||||
|
sbin_PROGRAMS += fuzz_unix_socket_kea_dhcp6
|
||||||
|
|
||||||
|
V6_LDADD = $(top_builddir)/src/bin/dhcp6/libdhcp6.la
|
||||||
|
V4_LDADD = $(top_builddir)/src/bin/dhcp4/libdhcp4.la
|
||||||
|
LDADD =
|
||||||
|
LDADD += $(top_builddir)/src/lib/dhcpsrv/libkea-dhcpsrv.la
|
||||||
|
LDADD += $(top_builddir)/src/lib/process/libkea-process.la
|
||||||
|
LDADD += $(top_builddir)/src/lib/eval/libkea-eval.la
|
||||||
|
LDADD += $(top_builddir)/src/lib/dhcp_ddns/libkea-dhcp_ddns.la
|
||||||
|
LDADD += $(top_builddir)/src/lib/stats/libkea-stats.la
|
||||||
|
LDADD += $(top_builddir)/src/lib/config/libkea-cfgclient.la
|
||||||
|
LDADD += $(top_builddir)/src/lib/http/libkea-http.la
|
||||||
|
LDADD += $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la
|
||||||
|
LDADD += $(top_builddir)/src/lib/hooks/libkea-hooks.la
|
||||||
|
LDADD += $(top_builddir)/src/lib/database/libkea-database.la
|
||||||
|
LDADD += $(top_builddir)/src/lib/testutils/libkea-testutils.la
|
||||||
|
LDADD += $(top_builddir)/src/lib/cc/libkea-cc.la
|
||||||
|
LDADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la
|
||||||
|
LDADD += $(top_builddir)/src/lib/log/libkea-log.la
|
||||||
|
LDADD += $(top_builddir)/src/lib/util/libkea-util.la
|
||||||
|
LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
|
||||||
|
LDADD += $(LOG4CPLUS_LIBS) $(CRYPTO_LIBS) $(BOOST_LIBS)
|
||||||
|
LDADD += $(GTEST_LDADD)
|
||||||
|
|
||||||
|
AM_LDFLAGS = $(GTEST_LDFLAGS)
|
||||||
|
|
||||||
|
common_sources = fuzz.cc fuzz.h
|
||||||
|
if FUZZING_IN_CI
|
||||||
|
AM_CXXFLAGS += -fsanitize=fuzzer -gdwarf-4
|
||||||
|
else
|
||||||
|
common_sources += main.cc
|
||||||
|
endif
|
||||||
|
|
||||||
|
fuzz_config_kea_dhcp4_SOURCES = $(common_sources)
|
||||||
|
fuzz_config_kea_dhcp4_SOURCES += fuzz_config_kea_dhcp4.cc
|
||||||
|
fuzz_config_kea_dhcp4_LDADD = $(V4_LDADD) $(LDADD)
|
||||||
|
|
||||||
|
fuzz_config_kea_dhcp6_SOURCES = $(common_sources)
|
||||||
|
fuzz_config_kea_dhcp6_SOURCES += fuzz_config_kea_dhcp6.cc
|
||||||
|
fuzz_config_kea_dhcp6_LDADD = $(V6_LDADD) $(LDADD)
|
||||||
|
|
||||||
|
fuzz_http_endpoint_SOURCES = $(common_sources)
|
||||||
|
fuzz_http_endpoint_SOURCES += fuzz_http_endpoint.cc
|
||||||
|
fuzz_http_endpoint_LDADD = $(LDADD)
|
||||||
|
|
||||||
|
fuzz_packets_kea_dhcp4_SOURCES = $(common_sources)
|
||||||
|
fuzz_packets_kea_dhcp4_SOURCES += fuzz_packets_kea_dhcp4.cc
|
||||||
|
fuzz_packets_kea_dhcp4_LDADD = $(V4_LDADD) $(LDADD)
|
||||||
|
|
||||||
|
fuzz_packets_kea_dhcp6_SOURCES = $(common_sources)
|
||||||
|
fuzz_packets_kea_dhcp6_SOURCES += fuzz_packets_kea_dhcp6.cc
|
||||||
|
fuzz_packets_kea_dhcp6_LDADD = $(V6_LDADD) $(LDADD)
|
||||||
|
|
||||||
|
fuzz_unix_socket_kea_dhcp4_SOURCES = $(common_sources)
|
||||||
|
fuzz_unix_socket_kea_dhcp4_SOURCES += fuzz_unix_socket_kea_dhcp4.cc
|
||||||
|
fuzz_unix_socket_kea_dhcp4_LDADD = $(V4_LDADD) $(LDADD)
|
||||||
|
|
||||||
|
fuzz_unix_socket_kea_dhcp6_SOURCES = $(common_sources)
|
||||||
|
fuzz_unix_socket_kea_dhcp6_SOURCES += fuzz_unix_socket_kea_dhcp6.cc
|
||||||
|
fuzz_unix_socket_kea_dhcp6_LDADD = $(V6_LDADD) $(LDADD)
|
||||||
|
|
||||||
|
endif # FUZZING
|
1
fuzz/dict.dat
Normal file
1
fuzz/dict.dat
Normal file
@ -0,0 +1 @@
|
|||||||
|
PD_POOLS="pd-pools"
|
76
fuzz/fuzz.cc
Normal file
76
fuzz/fuzz.cc
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
// Copyright (C) 2024 Internet Systems Consortium, Inc. ("ISC")
|
||||||
|
//
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
#include <config.h>
|
||||||
|
|
||||||
|
#include <fuzz.h>
|
||||||
|
|
||||||
|
#include <log/logger_support.h>
|
||||||
|
#include <process/daemon.h>
|
||||||
|
#include <util/filesystem.h>
|
||||||
|
#include <util/encode/encode.h>
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
using namespace isc::process;
|
||||||
|
using namespace isc::util::encode;
|
||||||
|
using namespace isc::util::file;
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
string KEA_LFC = isFile(KEA_LFC_INSTALLATION) ? KEA_LFC_INSTALLATION : KEA_LFC_SOURCES;
|
||||||
|
// string KEA_FUZZ_DIR = isFile(KEA_FUZZ_DIR_INSTALLATION) ? KEA_FUZZ_DIR_INSTALLATION : KEA_FUZZ_DIR_SOURCES;
|
||||||
|
TemporaryDirectory TEMP_DIR = TemporaryDirectory();
|
||||||
|
string KEA_FUZZ_DIR = TEMP_DIR.dirName();
|
||||||
|
|
||||||
|
bool
|
||||||
|
DoInitialization() {
|
||||||
|
LLVMFuzzerTearDown();
|
||||||
|
|
||||||
|
// Spoof the logger just enough to not get LoggingNotInitialized thrown.
|
||||||
|
// We explicitly don't want any logging during fuzzing for performance reasons.
|
||||||
|
setenv("KEA_LOCKFILE_DIR", KEA_FUZZ_DIR.c_str(), 0);
|
||||||
|
setenv("KEA_LFC_EXECUTABLE", "/bin/true", 0);
|
||||||
|
if (!getenv("DEBUG")) {
|
||||||
|
setenv("KEA_LOGGER_DESTINATION", "/dev/null", 0);
|
||||||
|
}
|
||||||
|
setenv("KEA_PIDFILE_DIR", KEA_FUZZ_DIR.c_str(), 0);
|
||||||
|
if (!isc::log::isLoggingInitialized()) {
|
||||||
|
isc::log::initLogger("fuzzer");
|
||||||
|
Daemon::loggerInit("fuzzer", /* verbose = */ false);
|
||||||
|
Daemon::setDefaultLoggerName("fuzzer");
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeToFile(string const& file, string const& content) {
|
||||||
|
// Create the config file.
|
||||||
|
ofstream out(file, ios::out | ios::trunc);
|
||||||
|
assert(out.is_open());
|
||||||
|
out << content;
|
||||||
|
out.close();
|
||||||
|
assert(!out.is_open());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool byteStreamToPacketData(uint8_t const* data, size_t size, vector<uint8_t>& byte_stream) {
|
||||||
|
string str(data, data + size);
|
||||||
|
if (!str.empty() && str.at(str.size() - 1) == '\n') {
|
||||||
|
str = str.substr(0, str.size() - 1);
|
||||||
|
}
|
||||||
|
if (str.find_first_not_of("0123456789abcdefABCDEF") != string::npos) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (str.size() % 2) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
decodeHex(str, byte_stream);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // extern "C"
|
35
fuzz/fuzz.h
Normal file
35
fuzz/fuzz.h
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// Copyright (C) 2024 Internet Systems Consortium, Inc. ("ISC")
|
||||||
|
//
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
extern std::string KEA_FUZZ_DIR;
|
||||||
|
extern std::string KEA_LFC;
|
||||||
|
|
||||||
|
bool
|
||||||
|
DoInitialization();
|
||||||
|
|
||||||
|
int
|
||||||
|
LLVMFuzzerInitialize();
|
||||||
|
|
||||||
|
int
|
||||||
|
LLVMFuzzerTearDown();
|
||||||
|
|
||||||
|
int
|
||||||
|
LLVMFuzzerTestOneInput(uint8_t const* data, size_t size);
|
||||||
|
|
||||||
|
void
|
||||||
|
writeToFile(std::string const& file, std::string const& content);
|
||||||
|
|
||||||
|
bool
|
||||||
|
byteStreamToPacketData(uint8_t const* data, size_t size, std::vector<uint8_t>& byte_stream);
|
||||||
|
|
||||||
|
} // extern "C"
|
72
fuzz/fuzz_config_kea_dhcp4.cc
Normal file
72
fuzz/fuzz_config_kea_dhcp4.cc
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
// Copyright (C) 2024 Internet Systems Consortium, Inc. ("ISC")
|
||||||
|
//
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
#include <config.h>
|
||||||
|
|
||||||
|
#include <fuzz.h>
|
||||||
|
|
||||||
|
#include <cc/command_interpreter.h>
|
||||||
|
#include <cc/user_context.h>
|
||||||
|
#include <dhcp4/ctrl_dhcp4_srv.h>
|
||||||
|
#include <dhcp4/json_config_parser.h>
|
||||||
|
#include <dhcp4/parser_context.h>
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <util/filesystem.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
using namespace isc;
|
||||||
|
using namespace isc::config;
|
||||||
|
using namespace isc::data;
|
||||||
|
using namespace isc::dhcp;
|
||||||
|
using namespace isc::process;
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
static pid_t const PID(getpid());
|
||||||
|
static string const PID_STR(to_string(PID));
|
||||||
|
static string const KEA_DHCP4_CONF(KEA_FUZZ_DIR + "/kea-dhcp4-" + PID_STR + ".conf");
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
int
|
||||||
|
LLVMFuzzerInitialize() {
|
||||||
|
static bool initialized(DoInitialization());
|
||||||
|
assert(initialized);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
LLVMFuzzerTearDown() {
|
||||||
|
try {
|
||||||
|
remove(KEA_DHCP4_CONF.c_str());
|
||||||
|
} catch (...) {
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
LLVMFuzzerTestOneInput(uint8_t const* data, size_t size) {
|
||||||
|
// Create the config file.
|
||||||
|
string const string_config(reinterpret_cast<char const*>(data), size);
|
||||||
|
writeToFile(KEA_DHCP4_CONF, string_config);
|
||||||
|
|
||||||
|
// Configure the server.
|
||||||
|
ControlledDhcpv4Srv server;
|
||||||
|
try {
|
||||||
|
server.init(KEA_DHCP4_CONF);
|
||||||
|
} catch (BadValue const&) {
|
||||||
|
} catch (Dhcp4ParseError const&) {
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // extern "C"
|
72
fuzz/fuzz_config_kea_dhcp6.cc
Normal file
72
fuzz/fuzz_config_kea_dhcp6.cc
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
// Copyright (C) 2024 Internet Systems Consortium, Inc. ("ISC")
|
||||||
|
//
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
#include <config.h>
|
||||||
|
|
||||||
|
#include <fuzz.h>
|
||||||
|
|
||||||
|
#include <cc/command_interpreter.h>
|
||||||
|
#include <cc/user_context.h>
|
||||||
|
#include <dhcp6/ctrl_dhcp6_srv.h>
|
||||||
|
#include <dhcp6/json_config_parser.h>
|
||||||
|
#include <dhcp6/parser_context.h>
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <util/filesystem.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
using namespace isc;
|
||||||
|
using namespace isc::config;
|
||||||
|
using namespace isc::data;
|
||||||
|
using namespace isc::dhcp;
|
||||||
|
using namespace isc::process;
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
static pid_t const PID(getpid());
|
||||||
|
static string const PID_STR(to_string(PID));
|
||||||
|
static string const KEA_DHCP6_CONF(KEA_FUZZ_DIR + "/kea-dhcp6-" + PID_STR + ".conf");
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
int
|
||||||
|
LLVMFuzzerInitialize() {
|
||||||
|
static bool initialized(DoInitialization());
|
||||||
|
assert(initialized);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
LLVMFuzzerTearDown() {
|
||||||
|
try {
|
||||||
|
remove(KEA_DHCP6_CONF.c_str());
|
||||||
|
} catch (...) {
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
LLVMFuzzerTestOneInput(uint8_t const* data, size_t size) {
|
||||||
|
// Create the config file.
|
||||||
|
string const string_config(reinterpret_cast<char const*>(data), size);
|
||||||
|
writeToFile(KEA_DHCP6_CONF, string_config);
|
||||||
|
|
||||||
|
// Configure the server.
|
||||||
|
ControlledDhcpv6Srv server;
|
||||||
|
try {
|
||||||
|
server.init(KEA_DHCP6_CONF);
|
||||||
|
} catch (BadValue const&) {
|
||||||
|
} catch (Dhcp6ParseError const&) {
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // extern "C"
|
174
fuzz/fuzz_http_endpoint.cc
Normal file
174
fuzz/fuzz_http_endpoint.cc
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
// Copyright (C) 2024 Internet Systems Consortium, Inc. ("ISC")
|
||||||
|
//
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
#include <config.h>
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <iostream>
|
||||||
|
#include <list>
|
||||||
|
|
||||||
|
#include <fuzz.h>
|
||||||
|
|
||||||
|
#include <asiolink/io_service.h>
|
||||||
|
#include <asiolink/interval_timer.h>
|
||||||
|
#include <cc/data.h>
|
||||||
|
#include <config/cmd_http_listener.h>
|
||||||
|
#include <http/listener.h>
|
||||||
|
#include <http/post_request_json.h>
|
||||||
|
#include <http/response.h>
|
||||||
|
#include <http/response_json.h>
|
||||||
|
#include <http/tests/response_test.h>
|
||||||
|
#include <http/testutils/test_http_client.h>
|
||||||
|
#include <process/d_controller.h>
|
||||||
|
#include <util/filesystem.h>
|
||||||
|
#include <util/multi_threading_mgr.h>
|
||||||
|
|
||||||
|
using namespace isc::asiolink;
|
||||||
|
using namespace isc::config;
|
||||||
|
using namespace isc::data;
|
||||||
|
using namespace isc::process;
|
||||||
|
using namespace isc::http;
|
||||||
|
using namespace isc::http::test;
|
||||||
|
using namespace isc::util;
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
void timeoutHandler() {
|
||||||
|
cerr << "Timeout occurred while fuzzing!" << endl;
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Represents HTTP POST request with JSON body.
|
||||||
|
///
|
||||||
|
/// In addition to the requirements specified by the @ref PostHttpRequest
|
||||||
|
/// this class requires that the "Content-Type" is "application/json".
|
||||||
|
///
|
||||||
|
/// This class provides methods to parse and retrieve JSON data structures.
|
||||||
|
struct PostHttpRequestBytes : PostHttpRequest {
|
||||||
|
/// @brief Constructor for inbound HTTP request.
|
||||||
|
explicit PostHttpRequestBytes() : PostHttpRequest() {
|
||||||
|
requireHeaderValue("Content-Type", "application/json");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Constructor for outbound HTTP request.
|
||||||
|
///
|
||||||
|
/// This constructor adds "Content-Type" header with the value of
|
||||||
|
/// "application/json" to the context.
|
||||||
|
///
|
||||||
|
/// @param method HTTP method, e.g. POST.
|
||||||
|
/// @param uri URI.
|
||||||
|
/// @param version HTTP version.
|
||||||
|
/// @param host_header Host header to be included in the request. The default
|
||||||
|
/// is the empty Host header.
|
||||||
|
/// @param basic_auth Basic HTTP authentication credential. The default
|
||||||
|
/// is no authentication.
|
||||||
|
explicit PostHttpRequestBytes(const Method& method,
|
||||||
|
const string& uri,
|
||||||
|
const HttpVersion& version,
|
||||||
|
const HostHttpHeader& host_header = HostHttpHeader(),
|
||||||
|
const BasicHttpAuthPtr& basic_auth = BasicHttpAuthPtr())
|
||||||
|
: PostHttpRequest(method, uri, version, host_header, basic_auth) {
|
||||||
|
requireHeaderValue("Content-Type", "application/json");
|
||||||
|
context()->headers_.push_back(HttpHeaderContext("Content-Type", "application/json"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Sets JSON body for an outbound message.
|
||||||
|
///
|
||||||
|
/// @param body JSON structure to be used as a body.
|
||||||
|
void setBodyAsBytes(vector<uint8_t> const& input) {
|
||||||
|
context_->body_ = string(input.begin(), input.end());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using PostHttpRequestBytesPtr = boost::shared_ptr<PostHttpRequestBytes>;
|
||||||
|
|
||||||
|
ThreadPool<function<void()>> THREAD_POOL;
|
||||||
|
|
||||||
|
static pid_t const PID(getpid());
|
||||||
|
static string const PID_STR(to_string(PID));
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
int
|
||||||
|
LLVMFuzzerInitialize() {
|
||||||
|
static bool initialized(DoInitialization());
|
||||||
|
assert(initialized);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
LLVMFuzzerTearDown() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
LLVMFuzzerTestOneInput(uint8_t const* data, size_t size) {
|
||||||
|
string const address("127.0.0.1");
|
||||||
|
int const port(18000);
|
||||||
|
int const timeout(100000);
|
||||||
|
|
||||||
|
CmdHttpListener listener(IOAddress(address), port);
|
||||||
|
MultiThreadingMgr::instance().setMode(true);
|
||||||
|
|
||||||
|
// Start the server.
|
||||||
|
listener.start();
|
||||||
|
|
||||||
|
// Create a client and specify the URL on which the server can be reached.
|
||||||
|
IOServicePtr io_service(new IOService());
|
||||||
|
IntervalTimer run_io_service_timer(io_service);
|
||||||
|
HttpClient client(io_service, false);
|
||||||
|
stringstream ss;
|
||||||
|
ss << "http://" << address << ":" << port;
|
||||||
|
Url url(ss.str());
|
||||||
|
|
||||||
|
// Initiate request to the server.
|
||||||
|
PostHttpRequestBytesPtr request(new PostHttpRequestBytes(
|
||||||
|
HttpRequest::Method::HTTP_POST, "/", HttpVersion(1, 1)));
|
||||||
|
|
||||||
|
// Body is a map with a specified parameter included.
|
||||||
|
vector<uint8_t> const body(data, data + size);
|
||||||
|
request->setBodyAsBytes(body);
|
||||||
|
request->finalize();
|
||||||
|
HttpResponseJsonPtr response(new HttpResponseJson());
|
||||||
|
client.asyncSendRequest(
|
||||||
|
url, TlsContextPtr(), request, response,
|
||||||
|
[](boost::system::error_code const&,
|
||||||
|
HttpResponsePtr const&,
|
||||||
|
string const&) {
|
||||||
|
});
|
||||||
|
|
||||||
|
// Actually trigger the requests. The requests should be handlded by the
|
||||||
|
// server one after another. While the first request is being processed
|
||||||
|
// the server should queue another one.
|
||||||
|
io_service->getInternalIOService().reset();
|
||||||
|
run_io_service_timer.setup(&timeoutHandler, timeout, IntervalTimer::ONE_SHOT);
|
||||||
|
io_service->runOne();
|
||||||
|
io_service->getInternalIOService().reset();
|
||||||
|
io_service->poll();
|
||||||
|
|
||||||
|
// Make sure that the received responses are different. We check that by
|
||||||
|
// comparing value of the sequence parameters.
|
||||||
|
if (getenv("DEBUG")) {
|
||||||
|
if (response) {
|
||||||
|
cout << response->getBody() << endl;
|
||||||
|
} else {
|
||||||
|
cout << "no response" << endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
listener.stop();
|
||||||
|
io_service->poll();
|
||||||
|
client.stop();
|
||||||
|
MultiThreadingMgr::instance().setMode(false);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // extern "C"
|
121
fuzz/fuzz_packets_kea_dhcp4.cc
Normal file
121
fuzz/fuzz_packets_kea_dhcp4.cc
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
// Copyright (C) 2024 Internet Systems Consortium, Inc. ("ISC")
|
||||||
|
//
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
#include <config.h>
|
||||||
|
|
||||||
|
#include <fuzz.h>
|
||||||
|
|
||||||
|
#include <cc/command_interpreter.h>
|
||||||
|
#include <cc/user_context.h>
|
||||||
|
#include <dhcp4/ctrl_dhcp4_srv.h>
|
||||||
|
#include <dhcp4/json_config_parser.h>
|
||||||
|
#include <dhcp4/parser_context.h>
|
||||||
|
#include <dhcpsrv/packet_fuzzer.h>
|
||||||
|
#include <util/encode/encode.h>
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <util/filesystem.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
using namespace isc;
|
||||||
|
using namespace isc::config;
|
||||||
|
using namespace isc::util;
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
static string const KEA_DHCP4_CONF(KEA_FUZZ_DIR + "/kea-dhcp4.conf");
|
||||||
|
static string KEA_DHCP4_FUZZING_INTERFACE;
|
||||||
|
static string KEA_DHCP4_FUZZING_ADDRESS;
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
int
|
||||||
|
LLVMFuzzerInitialize() {
|
||||||
|
static bool initialized(DoInitialization());
|
||||||
|
assert(initialized);
|
||||||
|
|
||||||
|
setenv("KEA_DHCP4_FUZZING_ROTATE_PORT", "true", 0);
|
||||||
|
|
||||||
|
char const* interface(getenv("KEA_DHCP4_FUZZING_INTERFACE"));
|
||||||
|
KEA_DHCP4_FUZZING_INTERFACE = string(interface ? interface : "lo");
|
||||||
|
|
||||||
|
char const* address(getenv("KEA_DHCP4_FUZZING_ADDRESS"));
|
||||||
|
KEA_DHCP4_FUZZING_ADDRESS = string(address ? address : "127.0.0.1");
|
||||||
|
|
||||||
|
writeToFile(KEA_DHCP4_CONF, R"(
|
||||||
|
{
|
||||||
|
"Dhcp4": {
|
||||||
|
"interfaces-config": {
|
||||||
|
"dhcp-socket-type": "udp",
|
||||||
|
"interfaces": [
|
||||||
|
")" + KEA_DHCP4_FUZZING_INTERFACE + R"("
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"lease-database": {
|
||||||
|
"persist": false,
|
||||||
|
"type": "memfile"
|
||||||
|
},
|
||||||
|
"subnet4": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"pools": [
|
||||||
|
{
|
||||||
|
"pool": "10.0.0.0/8"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"subnet": "10.0.0.0/8"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)");
|
||||||
|
|
||||||
|
// Iterate through the interfaces and expect no errors.
|
||||||
|
for (IfacePtr const& interface : IfaceMgr::instance().getIfaces()) {
|
||||||
|
for (string const& error : interface->getErrors()) {
|
||||||
|
cout << error << endl;
|
||||||
|
}
|
||||||
|
assert(interface->getErrors().empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
LLVMFuzzerTearDown() {
|
||||||
|
try {
|
||||||
|
remove(KEA_DHCP4_CONF.c_str());
|
||||||
|
} catch (...) {
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
LLVMFuzzerTestOneInput(uint8_t const* data, size_t size) {
|
||||||
|
vector<uint8_t> byte_stream;
|
||||||
|
bool const valid(byteStreamToPacketData(data, size, byte_stream));
|
||||||
|
if (!valid) {
|
||||||
|
cout << "Invalid input. Skipping..." << endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ControlledDhcpv4Srv server;
|
||||||
|
server.init(KEA_DHCP4_CONF);
|
||||||
|
|
||||||
|
// Fuzz.
|
||||||
|
PacketFuzzer fuzzer(ControlledDhcpv4Srv::getInstance()->getServerPort(),
|
||||||
|
KEA_DHCP4_FUZZING_INTERFACE, KEA_DHCP4_FUZZING_ADDRESS);
|
||||||
|
fuzzer.transfer(byte_stream.data(), byte_stream.size());
|
||||||
|
ControlledDhcpv4Srv::getInstance()->runOne();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // extern "C"
|
124
fuzz/fuzz_packets_kea_dhcp6.cc
Normal file
124
fuzz/fuzz_packets_kea_dhcp6.cc
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
// Copyright (C) 2024 Internet Systems Consortium, Inc. ("ISC")
|
||||||
|
//
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
#include <config.h>
|
||||||
|
|
||||||
|
#include <fuzz.h>
|
||||||
|
|
||||||
|
#include <cc/command_interpreter.h>
|
||||||
|
#include <cc/user_context.h>
|
||||||
|
#include <dhcp6/ctrl_dhcp6_srv.h>
|
||||||
|
#include <dhcp6/json_config_parser.h>
|
||||||
|
#include <dhcp6/parser_context.h>
|
||||||
|
#include <dhcpsrv/packet_fuzzer.h>
|
||||||
|
#include <util/encode/encode.h>
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <util/filesystem.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
using namespace isc;
|
||||||
|
using namespace isc::config;
|
||||||
|
using namespace isc::util;
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
static string const KEA_DHCP6_CONF(KEA_FUZZ_DIR + "/kea-dhcp6.conf");
|
||||||
|
static string KEA_DHCP6_FUZZING_INTERFACE;
|
||||||
|
static string KEA_DHCP6_FUZZING_ADDRESS;
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
int
|
||||||
|
LLVMFuzzerInitialize() {
|
||||||
|
static bool initialized(DoInitialization());
|
||||||
|
assert(initialized);
|
||||||
|
|
||||||
|
setenv("KEA_DHCP6_FUZZING_ROTATE_PORT", "true", 0);
|
||||||
|
|
||||||
|
char const* interface(getenv("KEA_DHCP6_FUZZING_INTERFACE"));
|
||||||
|
KEA_DHCP6_FUZZING_INTERFACE = string(interface ? interface : "lo");
|
||||||
|
|
||||||
|
char const* address(getenv("KEA_DHCP6_FUZZING_ADDRESS"));
|
||||||
|
KEA_DHCP6_FUZZING_ADDRESS = string(address ? address : "::1");
|
||||||
|
|
||||||
|
writeToFile(KEA_DHCP6_CONF, R"(
|
||||||
|
{
|
||||||
|
"Dhcp6": {
|
||||||
|
"interfaces-config": {
|
||||||
|
"interfaces": [
|
||||||
|
")" + KEA_DHCP6_FUZZING_INTERFACE + R"("
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"lease-database": {
|
||||||
|
"persist": false,
|
||||||
|
"type": "memfile"
|
||||||
|
},
|
||||||
|
"server-id": {
|
||||||
|
"type": "EN",
|
||||||
|
"persist": false
|
||||||
|
},
|
||||||
|
"subnet6": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"pools": [
|
||||||
|
{
|
||||||
|
"pool": "2001:db8::/80"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"subnet": "2001:db8::/64"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)");
|
||||||
|
|
||||||
|
// Iterate through the interfaces and expect no errors.
|
||||||
|
for (IfacePtr const& interface : IfaceMgr::instance().getIfaces()) {
|
||||||
|
for (string const& error : interface->getErrors()) {
|
||||||
|
cout << error << endl;
|
||||||
|
}
|
||||||
|
assert(interface->getErrors().empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
LLVMFuzzerTearDown() {
|
||||||
|
try {
|
||||||
|
remove(KEA_DHCP6_CONF.c_str());
|
||||||
|
} catch (...) {
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
LLVMFuzzerTestOneInput(uint8_t const* data, size_t size) {
|
||||||
|
vector<uint8_t> byte_stream;
|
||||||
|
bool const valid(byteStreamToPacketData(data, size, byte_stream));
|
||||||
|
if (!valid) {
|
||||||
|
cout << "Invalid input. Skipping..." << endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ControlledDhcpv6Srv server;
|
||||||
|
server.init(KEA_DHCP6_CONF);
|
||||||
|
|
||||||
|
// Fuzz.
|
||||||
|
PacketFuzzer fuzzer(ControlledDhcpv6Srv::getInstance()->getServerPort(),
|
||||||
|
KEA_DHCP6_FUZZING_INTERFACE, KEA_DHCP6_FUZZING_ADDRESS);
|
||||||
|
fuzzer.transfer(byte_stream.data(), byte_stream.size());
|
||||||
|
ControlledDhcpv6Srv::getInstance()->runOne();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // extern "C"
|
122
fuzz/fuzz_unix_socket_kea_dhcp4.cc
Normal file
122
fuzz/fuzz_unix_socket_kea_dhcp4.cc
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
// Copyright (C) 2024 Internet Systems Consortium, Inc. ("ISC")
|
||||||
|
//
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
#include <config.h>
|
||||||
|
|
||||||
|
#include <fuzz.h>
|
||||||
|
|
||||||
|
#include <asiolink/io_service.h>
|
||||||
|
#include <cc/data.h>
|
||||||
|
#include <config/command_mgr.h>
|
||||||
|
#include <dhcp4/ctrl_dhcp4_srv.h>
|
||||||
|
#include <dhcpsrv/cfgmgr.h>
|
||||||
|
#include <testutils/unix_control_client.h>
|
||||||
|
|
||||||
|
#include <util/filesystem.h>
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
using namespace isc::asiolink;
|
||||||
|
using namespace isc::config;
|
||||||
|
using namespace isc::data;
|
||||||
|
using namespace isc::dhcp;
|
||||||
|
using namespace isc::dhcp::test;
|
||||||
|
using namespace isc::util;
|
||||||
|
using namespace isc::util::file;
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
static pid_t const PID(getpid());
|
||||||
|
static string const PID_STR(to_string(PID));
|
||||||
|
static string const KEA_DHCP4_CONF(KEA_FUZZ_DIR + "/kea-dhcp4-" + PID_STR + ".conf");
|
||||||
|
static string const KEA_DHCP4_CSV(KEA_FUZZ_DIR + "/kea-dhcp4-" + PID_STR + ".csv");
|
||||||
|
static string const SOCKET(KEA_FUZZ_DIR + "/kea-dhcp4-ctrl-" + PID_STR + ".sock");
|
||||||
|
|
||||||
|
static UnixControlClient CLIENT;
|
||||||
|
static IOServicePtr IO_SERVICE(new IOService());
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
int
|
||||||
|
LLVMFuzzerInitialize() {
|
||||||
|
static bool initialized(DoInitialization());
|
||||||
|
assert(initialized);
|
||||||
|
|
||||||
|
// "control-socket" is of explicit interest, but we also specify the memfile
|
||||||
|
// CSV location to make sure that we don't get an error caused by an invalid
|
||||||
|
// file path.
|
||||||
|
writeToFile(KEA_DHCP4_CONF, R"(
|
||||||
|
{
|
||||||
|
"Dhcp4": {
|
||||||
|
"control-socket": {
|
||||||
|
"socket-name": ")" + SOCKET + R"(",
|
||||||
|
"socket-type": "unix"
|
||||||
|
},
|
||||||
|
"lease-database": {
|
||||||
|
"name": ")" + KEA_DHCP4_CSV + R"(",
|
||||||
|
"type": "memfile"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)");
|
||||||
|
|
||||||
|
// Iterate through the interfaces and expect no errors.
|
||||||
|
for (IfacePtr const& interface : IfaceMgr::instance().getIfaces()) {
|
||||||
|
for (string const& error : interface->getErrors()) {
|
||||||
|
cout << error << endl;
|
||||||
|
}
|
||||||
|
assert(interface->getErrors().empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
LLVMFuzzerTearDown() {
|
||||||
|
try {
|
||||||
|
remove(KEA_DHCP4_CONF.c_str());
|
||||||
|
} catch (...) {
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
remove(KEA_DHCP4_CSV.c_str());
|
||||||
|
} catch (...) {
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
remove(SOCKET.c_str());
|
||||||
|
} catch (...) {
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
remove((SOCKET + ".lock").c_str());
|
||||||
|
} catch (...) {
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
LLVMFuzzerTestOneInput(uint8_t const* data, size_t size) {
|
||||||
|
CfgMgr::instance().clear();
|
||||||
|
ControlledDhcpv4Srv server;
|
||||||
|
server.init(KEA_DHCP4_CONF);
|
||||||
|
assert(isSocket(SOCKET));
|
||||||
|
|
||||||
|
string const command(reinterpret_cast<char const*>(data), size);
|
||||||
|
CLIENT.connectToServer(SOCKET);
|
||||||
|
CLIENT.sendCommand(command);
|
||||||
|
ControlledDhcpv4Srv::getInstance()->getIOService()->poll();
|
||||||
|
string response;
|
||||||
|
CLIENT.getResponse(response);
|
||||||
|
ControlledDhcpv4Srv::getInstance()->getIOService()->poll();
|
||||||
|
CLIENT.disconnectFromServer();
|
||||||
|
ControlledDhcpv4Srv::getInstance()->getIOService()->poll();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // extern "C"
|
128
fuzz/fuzz_unix_socket_kea_dhcp6.cc
Normal file
128
fuzz/fuzz_unix_socket_kea_dhcp6.cc
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
// Copyright (C) 2024 Internet Systems Consortium, Inc. ("ISC")
|
||||||
|
//
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
#include <config.h>
|
||||||
|
|
||||||
|
#include <fuzz.h>
|
||||||
|
|
||||||
|
#include <asiolink/io_service.h>
|
||||||
|
#include <cc/data.h>
|
||||||
|
#include <config/command_mgr.h>
|
||||||
|
#include <dhcp6/ctrl_dhcp6_srv.h>
|
||||||
|
#include <dhcpsrv/cfgmgr.h>
|
||||||
|
#include <testutils/unix_control_client.h>
|
||||||
|
|
||||||
|
#include <util/filesystem.h>
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
using namespace isc::asiolink;
|
||||||
|
using namespace isc::config;
|
||||||
|
using namespace isc::data;
|
||||||
|
using namespace isc::dhcp;
|
||||||
|
using namespace isc::dhcp::test;
|
||||||
|
using namespace isc::util;
|
||||||
|
using namespace isc::util::file;
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
static pid_t const PID(getpid());
|
||||||
|
static string const PID_STR(to_string(PID));
|
||||||
|
static string const KEA_DHCP6_CONF(KEA_FUZZ_DIR + "/kea-dhcp6-" + PID_STR + ".conf");
|
||||||
|
static string const KEA_DHCP6_CSV(KEA_FUZZ_DIR + "/kea-dhcp6-" + PID_STR + ".csv");
|
||||||
|
static string const SOCKET(KEA_FUZZ_DIR + "/kea-dhcp6-ctrl-" + PID_STR + ".sock");
|
||||||
|
|
||||||
|
static UnixControlClient CLIENT;
|
||||||
|
static IOServicePtr IO_SERVICE(new IOService());
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
int
|
||||||
|
LLVMFuzzerInitialize() {
|
||||||
|
static bool initialized(DoInitialization());
|
||||||
|
assert(initialized);
|
||||||
|
|
||||||
|
// "control-socket" is of explicit interest, but we also specify the memfile
|
||||||
|
// CSV location and the server-id to make sure that we don't get an error
|
||||||
|
// caused by an invalid file path.
|
||||||
|
writeToFile(KEA_DHCP6_CONF, R"(
|
||||||
|
{
|
||||||
|
"Dhcp6": {
|
||||||
|
"control-socket": {
|
||||||
|
"socket-name": ")" + SOCKET + R"(",
|
||||||
|
"socket-type": "unix"
|
||||||
|
},
|
||||||
|
"lease-database": {
|
||||||
|
"name": ")" + KEA_DHCP6_CSV + R"(",
|
||||||
|
"type": "memfile"
|
||||||
|
},
|
||||||
|
"server-id": {
|
||||||
|
"type": "EN",
|
||||||
|
"enterprise-id": 2495,
|
||||||
|
"identifier": "0123456789",
|
||||||
|
"persist": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)");
|
||||||
|
|
||||||
|
// Iterate through the interfaces and expect no errors.
|
||||||
|
for (IfacePtr const& interface : IfaceMgr::instance().getIfaces()) {
|
||||||
|
for (string const& error : interface->getErrors()) {
|
||||||
|
cout << error << endl;
|
||||||
|
}
|
||||||
|
assert(interface->getErrors().empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
LLVMFuzzerTearDown() {
|
||||||
|
try {
|
||||||
|
remove(KEA_DHCP6_CONF.c_str());
|
||||||
|
} catch (...) {
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
remove(KEA_DHCP6_CSV.c_str());
|
||||||
|
} catch (...) {
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
remove(SOCKET.c_str());
|
||||||
|
} catch (...) {
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
remove((SOCKET + ".lock").c_str());
|
||||||
|
} catch (...) {
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
LLVMFuzzerTestOneInput(uint8_t const* data, size_t size) {
|
||||||
|
CfgMgr::instance().clear();
|
||||||
|
ControlledDhcpv6Srv server;
|
||||||
|
server.init(KEA_DHCP6_CONF);
|
||||||
|
assert(isSocket(SOCKET));
|
||||||
|
|
||||||
|
string const command(reinterpret_cast<char const*>(data), size);
|
||||||
|
CLIENT.connectToServer(SOCKET);
|
||||||
|
CLIENT.sendCommand(command);
|
||||||
|
ControlledDhcpv6Srv::getInstance()->getIOService()->poll();
|
||||||
|
string response;
|
||||||
|
CLIENT.getResponse(response);
|
||||||
|
ControlledDhcpv6Srv::getInstance()->getIOService()->poll();
|
||||||
|
CLIENT.disconnectFromServer();
|
||||||
|
ControlledDhcpv6Srv::getInstance()->getIOService()->poll();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // extern "C"
|
43
fuzz/input/Makefile.am
Normal file
43
fuzz/input/Makefile.am
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
SUBDIRS = .
|
||||||
|
|
||||||
|
if FUZZING
|
||||||
|
|
||||||
|
fuzzdir = "${datarootdir}/${PACKAGE_NAME}/fuzz"
|
||||||
|
|
||||||
|
nobase_dist_fuzz_DATA =
|
||||||
|
nobase_dist_fuzz_DATA += fuzz-config-kea-dhcp4/empty
|
||||||
|
nobase_dist_fuzz_DATA += fuzz-config-kea-dhcp4/one-byte
|
||||||
|
nobase_dist_fuzz_DATA += fuzz-config-kea-dhcp6/empty
|
||||||
|
nobase_dist_fuzz_DATA += fuzz-config-kea-dhcp6/one-byte
|
||||||
|
nobase_dist_fuzz_DATA += fuzz-http-endpoint/config-get
|
||||||
|
nobase_dist_fuzz_DATA += fuzz-http-endpoint/config-get-with-service
|
||||||
|
nobase_dist_fuzz_DATA += fuzz-http-endpoint/empty
|
||||||
|
nobase_dist_fuzz_DATA += fuzz-http-endpoint/empty-json-map
|
||||||
|
nobase_dist_fuzz_DATA += fuzz-http-endpoint/one-byte
|
||||||
|
nobase_dist_fuzz_DATA += fuzz-http-endpoint/one-entry-json-map
|
||||||
|
nobase_dist_fuzz_DATA += fuzz-packets-kea-dhcp4/dhcp-payload-only
|
||||||
|
nobase_dist_fuzz_DATA += fuzz-packets-kea-dhcp4/empty
|
||||||
|
nobase_dist_fuzz_DATA += fuzz-packets-kea-dhcp4/full-dhcp-packet
|
||||||
|
nobase_dist_fuzz_DATA += fuzz-packets-kea-dhcp4/one-byte
|
||||||
|
nobase_dist_fuzz_DATA += fuzz-packets-kea-dhcp4/udp-header
|
||||||
|
nobase_dist_fuzz_DATA += fuzz-packets-kea-dhcp6/dhcp-payload-only
|
||||||
|
nobase_dist_fuzz_DATA += fuzz-packets-kea-dhcp6/empty
|
||||||
|
nobase_dist_fuzz_DATA += fuzz-packets-kea-dhcp6/full-dhcp-packet
|
||||||
|
nobase_dist_fuzz_DATA += fuzz-packets-kea-dhcp6/one-byte
|
||||||
|
nobase_dist_fuzz_DATA += fuzz-packets-kea-dhcp6/udp-header
|
||||||
|
nobase_dist_fuzz_DATA += fuzz-unix-socket-kea-dhcp4/config-get
|
||||||
|
nobase_dist_fuzz_DATA += fuzz-unix-socket-kea-dhcp4/config-get-with-service
|
||||||
|
nobase_dist_fuzz_DATA += fuzz-unix-socket-kea-dhcp4/empty
|
||||||
|
nobase_dist_fuzz_DATA += fuzz-unix-socket-kea-dhcp4/empty-json-map
|
||||||
|
nobase_dist_fuzz_DATA += fuzz-unix-socket-kea-dhcp4/one-byte
|
||||||
|
nobase_dist_fuzz_DATA += fuzz-unix-socket-kea-dhcp4/one-entry-json-map
|
||||||
|
nobase_dist_fuzz_DATA += fuzz-unix-socket-kea-dhcp6/config-get
|
||||||
|
nobase_dist_fuzz_DATA += fuzz-unix-socket-kea-dhcp6/config-get-with-service
|
||||||
|
nobase_dist_fuzz_DATA += fuzz-unix-socket-kea-dhcp6/empty
|
||||||
|
nobase_dist_fuzz_DATA += fuzz-unix-socket-kea-dhcp6/empty-json-map
|
||||||
|
nobase_dist_fuzz_DATA += fuzz-unix-socket-kea-dhcp6/one-byte
|
||||||
|
nobase_dist_fuzz_DATA += fuzz-unix-socket-kea-dhcp6/one-entry-json-map
|
||||||
|
nobase_dist_fuzz_DATA += kea-dhcp4.conf
|
||||||
|
nobase_dist_fuzz_DATA += kea-dhcp6.conf
|
||||||
|
|
||||||
|
endif # FUZZING
|
1
fuzz/input/fuzz-config-kea-dhcp4/doc-examples
Symbolic link
1
fuzz/input/fuzz-config-kea-dhcp4/doc-examples
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../../../doc/examples/kea4
|
0
fuzz/input/fuzz-config-kea-dhcp4/empty
Normal file
0
fuzz/input/fuzz-config-kea-dhcp4/empty
Normal file
1
fuzz/input/fuzz-config-kea-dhcp4/one-byte
Normal file
1
fuzz/input/fuzz-config-kea-dhcp4/one-byte
Normal file
@ -0,0 +1 @@
|
|||||||
|
0a
|
1
fuzz/input/fuzz-config-kea-dhcp6/doc-examples
Symbolic link
1
fuzz/input/fuzz-config-kea-dhcp6/doc-examples
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../../../doc/examples/kea6
|
0
fuzz/input/fuzz-config-kea-dhcp6/empty
Normal file
0
fuzz/input/fuzz-config-kea-dhcp6/empty
Normal file
1
fuzz/input/fuzz-config-kea-dhcp6/one-byte
Normal file
1
fuzz/input/fuzz-config-kea-dhcp6/one-byte
Normal file
@ -0,0 +1 @@
|
|||||||
|
0a
|
3
fuzz/input/fuzz-http-endpoint/config-get
Normal file
3
fuzz/input/fuzz-http-endpoint/config-get
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"command": "config-get"
|
||||||
|
}
|
4
fuzz/input/fuzz-http-endpoint/config-get-with-service
Normal file
4
fuzz/input/fuzz-http-endpoint/config-get-with-service
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"command": "config-get",
|
||||||
|
"service": [ "dhcp6" ]
|
||||||
|
}
|
0
fuzz/input/fuzz-http-endpoint/empty
Normal file
0
fuzz/input/fuzz-http-endpoint/empty
Normal file
1
fuzz/input/fuzz-http-endpoint/empty-json-map
Normal file
1
fuzz/input/fuzz-http-endpoint/empty-json-map
Normal file
@ -0,0 +1 @@
|
|||||||
|
{}
|
1
fuzz/input/fuzz-http-endpoint/one-byte
Normal file
1
fuzz/input/fuzz-http-endpoint/one-byte
Normal file
@ -0,0 +1 @@
|
|||||||
|
0a
|
3
fuzz/input/fuzz-http-endpoint/one-entry-json-map
Normal file
3
fuzz/input/fuzz-http-endpoint/one-entry-json-map
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"a": 1
|
||||||
|
}
|
1
fuzz/input/fuzz-packets-kea-dhcp4/dhcp-payload-only
Normal file
1
fuzz/input/fuzz-packets-kea-dhcp4/dhcp-payload-only
Normal file
@ -0,0 +1 @@
|
|||||||
|
210101060100000000000000000000000000000000000000000a010001000c0102030400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000638253633501013707011c02030f060c3d0701000c01020304ff
|
0
fuzz/input/fuzz-packets-kea-dhcp4/empty
Normal file
0
fuzz/input/fuzz-packets-kea-dhcp4/empty
Normal file
1
fuzz/input/fuzz-packets-kea-dhcp4/full-dhcp-packet
Normal file
1
fuzz/input/fuzz-packets-kea-dhcp4/full-dhcp-packet
Normal file
@ -0,0 +1 @@
|
|||||||
|
000400010006d6b6574a0cce713b0800450001229b384000401194910a010001ffffffff00430043010e0b210101060100000000000000000000000000000000000000000a010001000c0102030400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000638253633501013707011c02030f060c3d0701000c01020304ff
|
1
fuzz/input/fuzz-packets-kea-dhcp4/one-byte
Normal file
1
fuzz/input/fuzz-packets-kea-dhcp4/one-byte
Normal file
@ -0,0 +1 @@
|
|||||||
|
0a
|
1
fuzz/input/fuzz-packets-kea-dhcp4/udp-header
Normal file
1
fuzz/input/fuzz-packets-kea-dhcp4/udp-header
Normal file
@ -0,0 +1 @@
|
|||||||
|
000400010006d6b6574a0cce713b0800450001229b384000401194910a010001ffffffff00430043010e0b
|
1
fuzz/input/fuzz-packets-kea-dhcp6/dhcp-payload-only
Normal file
1
fuzz/input/fuzz-packets-kea-dhcp6/dhcp-payload-only
Normal file
@ -0,0 +1 @@
|
|||||||
|
010000000001000e000100012b8b4659000c010203040003000c0000000100000e10000015180006000400170018000800020000
|
0
fuzz/input/fuzz-packets-kea-dhcp6/empty
Normal file
0
fuzz/input/fuzz-packets-kea-dhcp6/empty
Normal file
1
fuzz/input/fuzz-packets-kea-dhcp6/full-dhcp-packet
Normal file
1
fuzz/input/fuzz-packets-kea-dhcp6/full-dhcp-packet
Normal file
@ -0,0 +1 @@
|
|||||||
|
000400010006d6b6574a0cce000086dd6001d3f9003c110120010db8000100000000000000000001ff02000000000000000000000001000202220223003c2d0e010000000001000e000100012b8b4659000c010203040003000c0000000100000e10000015180006000400170018000800020000
|
1
fuzz/input/fuzz-packets-kea-dhcp6/one-byte
Normal file
1
fuzz/input/fuzz-packets-kea-dhcp6/one-byte
Normal file
@ -0,0 +1 @@
|
|||||||
|
0a
|
1
fuzz/input/fuzz-packets-kea-dhcp6/udp-header
Normal file
1
fuzz/input/fuzz-packets-kea-dhcp6/udp-header
Normal file
@ -0,0 +1 @@
|
|||||||
|
000400010006d6b6574a0cce000086dd6001d3f9003c110120010db8000100000000000000000001ff02000000000000000000000001000202220223003c2d0e
|
3
fuzz/input/fuzz-unix-socket-kea-dhcp4/config-get
Normal file
3
fuzz/input/fuzz-unix-socket-kea-dhcp4/config-get
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"command": "config-get"
|
||||||
|
}
|
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"command": "config-get",
|
||||||
|
"service": [ "dhcp4" ]
|
||||||
|
}
|
0
fuzz/input/fuzz-unix-socket-kea-dhcp4/empty
Normal file
0
fuzz/input/fuzz-unix-socket-kea-dhcp4/empty
Normal file
1
fuzz/input/fuzz-unix-socket-kea-dhcp4/empty-json-map
Normal file
1
fuzz/input/fuzz-unix-socket-kea-dhcp4/empty-json-map
Normal file
@ -0,0 +1 @@
|
|||||||
|
{}
|
1
fuzz/input/fuzz-unix-socket-kea-dhcp4/one-byte
Normal file
1
fuzz/input/fuzz-unix-socket-kea-dhcp4/one-byte
Normal file
@ -0,0 +1 @@
|
|||||||
|
0a
|
3
fuzz/input/fuzz-unix-socket-kea-dhcp4/one-entry-json-map
Normal file
3
fuzz/input/fuzz-unix-socket-kea-dhcp4/one-entry-json-map
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"a": 1
|
||||||
|
}
|
3
fuzz/input/fuzz-unix-socket-kea-dhcp6/config-get
Normal file
3
fuzz/input/fuzz-unix-socket-kea-dhcp6/config-get
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"command": "config-get"
|
||||||
|
}
|
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"command": "config-get",
|
||||||
|
"service": [ "dhcp6" ]
|
||||||
|
}
|
0
fuzz/input/fuzz-unix-socket-kea-dhcp6/empty
Normal file
0
fuzz/input/fuzz-unix-socket-kea-dhcp6/empty
Normal file
1
fuzz/input/fuzz-unix-socket-kea-dhcp6/empty-json-map
Normal file
1
fuzz/input/fuzz-unix-socket-kea-dhcp6/empty-json-map
Normal file
@ -0,0 +1 @@
|
|||||||
|
{}
|
1
fuzz/input/fuzz-unix-socket-kea-dhcp6/one-byte
Normal file
1
fuzz/input/fuzz-unix-socket-kea-dhcp6/one-byte
Normal file
@ -0,0 +1 @@
|
|||||||
|
0a
|
3
fuzz/input/fuzz-unix-socket-kea-dhcp6/one-entry-json-map
Normal file
3
fuzz/input/fuzz-unix-socket-kea-dhcp6/one-entry-json-map
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"a": 1
|
||||||
|
}
|
20
fuzz/input/kea-dhcp4.conf
Normal file
20
fuzz/input/kea-dhcp4.conf
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"Dhcp4": {
|
||||||
|
"interfaces-config": {
|
||||||
|
"interfaces": [
|
||||||
|
"*"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"subnet4": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"pools": [
|
||||||
|
{
|
||||||
|
"pool": "127.0.0.0/8"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"subnet": "127.0.0.0/8"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
27
fuzz/input/kea-dhcp6.conf
Normal file
27
fuzz/input/kea-dhcp6.conf
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"Dhcp6": {
|
||||||
|
"interfaces-config": {
|
||||||
|
"interfaces": [
|
||||||
|
"*"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"subnet6": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"pd-pools": [
|
||||||
|
{
|
||||||
|
"delegated-len": 120,
|
||||||
|
"prefix": "2001:db8:1:0:2::",
|
||||||
|
"prefix-len": 80
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"pools": [
|
||||||
|
{
|
||||||
|
"pool": "::/80"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"subnet": "::/64"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
26
fuzz/input/regenerate-cpp-sources.sh
Executable file
26
fuzz/input/regenerate-cpp-sources.sh
Executable file
@ -0,0 +1,26 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
script_path=$(cd "$(dirname "${0}")" && pwd)
|
||||||
|
|
||||||
|
cd "${script_path}"
|
||||||
|
|
||||||
|
generate() {
|
||||||
|
source="${1}"
|
||||||
|
target="${2}"
|
||||||
|
|
||||||
|
source_content=$(cat "${source}")
|
||||||
|
variable_name=$(echo "${source}" | tr '[:lower:]' '[:upper:]' | sed 's/\./_/' | sed 's/-/_/')
|
||||||
|
|
||||||
|
cat > "${target}" <<HERE_DOCUMENT
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
extern std::string ${variable_name} = R"(
|
||||||
|
${source_content}
|
||||||
|
)";
|
||||||
|
HERE_DOCUMENT
|
||||||
|
}
|
||||||
|
|
||||||
|
generate kea-dhcp4.conf ../kea-dhcp4.h
|
||||||
|
generate kea-dhcp6.conf ../kea-dhcp6.h
|
24
fuzz/kea-dhcp4.h
Normal file
24
fuzz/kea-dhcp4.h
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#include <string>
|
||||||
|
|
||||||
|
extern std::string KEA_DHCP4_CONF = R"(
|
||||||
|
{
|
||||||
|
"Dhcp4": {
|
||||||
|
"interfaces-config": {
|
||||||
|
"interfaces": [
|
||||||
|
"*"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"subnet4": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"pools": [
|
||||||
|
{
|
||||||
|
"pool": "127.0.0.0/8"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"subnet": "127.0.0.0/8"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
31
fuzz/kea-dhcp6.h
Normal file
31
fuzz/kea-dhcp6.h
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
#include <string>
|
||||||
|
|
||||||
|
extern std::string KEA_DHCP6_CONF = R"(
|
||||||
|
{
|
||||||
|
"Dhcp6": {
|
||||||
|
"interfaces-config": {
|
||||||
|
"interfaces": [
|
||||||
|
"*"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"subnet6": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"pd-pools": [
|
||||||
|
{
|
||||||
|
"delegated-len": 120,
|
||||||
|
"prefix": "2001:db8:1:0:2::",
|
||||||
|
"prefix-len": 80
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"pools": [
|
||||||
|
{
|
||||||
|
"pool": "::/80"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"subnet": "::/64"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
11
fuzz/libfuzzer.sh
Executable file
11
fuzz/libfuzzer.sh
Executable file
@ -0,0 +1,11 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Copyright (C) 2022-2024 Internet Systems Consortium, Inc. ("ISC")
|
||||||
|
#
|
||||||
|
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
set -eux
|
||||||
|
|
||||||
|
exec "${2}" "${1}/$(basename "${2}").in" -max_total_time=5 -print_pcs=1 -print_final_stats=1 -print_corpus_stats=1 -print_coverage=1
|
124
fuzz/main.cc
Normal file
124
fuzz/main.cc
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
// Copyright (C) 2024 Internet Systems Consortium, Inc. ("ISC")
|
||||||
|
//
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
#include <config.h>
|
||||||
|
|
||||||
|
#include <util/filesystem.h>
|
||||||
|
#include <fuzz.h>
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <list>
|
||||||
|
#include <sstream>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
using namespace isc::util::file;
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int, char* argv[]) {
|
||||||
|
int const return_code(LLVMFuzzerInitialize());
|
||||||
|
assert(return_code == 0);
|
||||||
|
|
||||||
|
// Determine some paths.
|
||||||
|
Path const this_binary(argv[0]);
|
||||||
|
string const ancestor_path(Path(this_binary.parentPath()).parentPath());
|
||||||
|
string const filename(this_binary.filename());
|
||||||
|
stringstream ss;
|
||||||
|
ss << ancestor_path << "/input/" << filename;
|
||||||
|
Path const p(ss.str());
|
||||||
|
|
||||||
|
// Print start header.
|
||||||
|
if (isatty(fileno(stdin))) {
|
||||||
|
cout << "\033[92m[ RUN ]\033[0m " << filename << endl;
|
||||||
|
} else {
|
||||||
|
cout << "[ RUN ] " << filename << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
int exit_code(0);
|
||||||
|
string directory(p.str());
|
||||||
|
if (exists(directory)) {
|
||||||
|
// Recursively take all regular files as input.
|
||||||
|
list<string> files;
|
||||||
|
|
||||||
|
struct dirent *dp;
|
||||||
|
DIR *dfd(opendir(p.str().c_str()));
|
||||||
|
while ((dp = readdir(dfd)) != nullptr) {
|
||||||
|
string file(dp->d_name);
|
||||||
|
if (file == "." || file == "..") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
string entry(directory + '/' + dp->d_name);
|
||||||
|
|
||||||
|
if (!isFile(entry)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save file names.
|
||||||
|
files.push_back(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort the file names so that the order is the same each time.
|
||||||
|
files.sort();
|
||||||
|
|
||||||
|
for (string& f : files) {
|
||||||
|
// Read content from file.
|
||||||
|
basic_ifstream<uint8_t> file(f, ios::binary);
|
||||||
|
|
||||||
|
if (!file.is_open()) {
|
||||||
|
cerr << "ERROR: could not open file " << f << endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the file size.
|
||||||
|
file.seekg(0, std::ios::end);
|
||||||
|
streampos const bytes(file.tellg());
|
||||||
|
file.seekg(0, std::ios::beg);
|
||||||
|
|
||||||
|
// Read the entire file into a vector.
|
||||||
|
vector<uint8_t> buffer(bytes);
|
||||||
|
file.read(buffer.data(), bytes);
|
||||||
|
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
// Fuzz.
|
||||||
|
f.replace(f.find(ancestor_path), ancestor_path.size() + 1, string());
|
||||||
|
cout << "Fuzzing with " << bytes << " byte" << (bytes == 1 ? string() : "s") << " read from "
|
||||||
|
<< f << "..." << endl;
|
||||||
|
exit_code |= LLVMFuzzerTestOneInput(buffer.data(), bytes);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Read input from stdin.
|
||||||
|
cout << "Waiting on input..." << endl;
|
||||||
|
vector<uint8_t> buffer(65536);
|
||||||
|
size_t const bytes(fread(&buffer[0], sizeof(buffer[0]), buffer.size(), stdin));
|
||||||
|
|
||||||
|
// Fuzz.
|
||||||
|
cout << "Fuzzing with " << bytes << " byte" << (bytes == 1 ? "" : "s")
|
||||||
|
<< " read from stdin..." << endl;
|
||||||
|
exit_code |= LLVMFuzzerTestOneInput(buffer.data(), bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tear down the setup.
|
||||||
|
LLVMFuzzerTearDown();
|
||||||
|
|
||||||
|
// Print end header.
|
||||||
|
string const result(exit_code == 0 ? " OK " : " FAILED ");
|
||||||
|
if (isatty(fileno(stdin))) {
|
||||||
|
cout << "\033[92m[" << result << "]\033[0m " << filename << endl;
|
||||||
|
} else {
|
||||||
|
cout << "[" << result << "] " << filename << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return exit_code;
|
||||||
|
}
|
112
fuzz/setup.sh
Executable file
112
fuzz/setup.sh
Executable file
@ -0,0 +1,112 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Copyright (C) 2024 Internet Systems Consortium, Inc. ("ISC")
|
||||||
|
#
|
||||||
|
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
# shellcheck disable=all
|
||||||
|
|
||||||
|
# Disable this script altogether for now.
|
||||||
|
exit 0
|
||||||
|
|
||||||
|
# Disable this script if the interface and address are used from environment variables.
|
||||||
|
if test -n "${KEA_DHCP4_FUZZING_INTERFACE+x}" ||
|
||||||
|
test -n "${KEA_DHCP4_FUZZING_ADDRESS+x}" ||
|
||||||
|
test -n "${KEA_DHCP6_FUZZING_INTERFACE+x}" ||
|
||||||
|
test -n "${KEA_DHCP6_FUZZING_ADDRESS+x}"; then
|
||||||
|
printf 'Environment variables set. Will use those. Abandoning.\n'
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
script_path=$(cd "$(dirname "${0}")" && pwd)
|
||||||
|
|
||||||
|
cd "${script_path}" > /dev/null
|
||||||
|
|
||||||
|
# Add sudo to the fuzzers.
|
||||||
|
sudo='if ! sudo -n true; then exec sudo -- "${0}" "${@}"; fi'
|
||||||
|
for i in fuzz-*-kea-dhcp[46]; do
|
||||||
|
continue # Disable this loop for now.
|
||||||
|
if ! grep -F "${sudo}" "${i}" > /dev/null; then
|
||||||
|
sed -i "2i${sudo}" "${i}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Create kea-dhcp{v}-fuzz-* wrapper scripts which adds the afl-fuzz command prefix to kea-dhcp{v}.
|
||||||
|
for v in 4 6; do
|
||||||
|
continue # Disable this loop for now.
|
||||||
|
executable="../src/bin/dhcp${v}/kea-dhcp${v}"
|
||||||
|
|
||||||
|
for f in config packets unix-socket; do
|
||||||
|
fuzzed_executable="${executable}-fuzz-${f}"
|
||||||
|
cp "${executable}" "${fuzzed_executable}"
|
||||||
|
mkdir -p "output/config/kea-dhcp${v}"
|
||||||
|
sed -i "s# *exec \"\$progdir/\$program\"#\n\
|
||||||
|
export AFL_DEBUG='1'\n\
|
||||||
|
export AFL_DEBUG_CHILD='1'\n\
|
||||||
|
export AFL_LLVM_MAP_ADDR='true'\n\
|
||||||
|
export AFL_MAP_SIZE='10000000'\n\
|
||||||
|
export KEA_AFL_ADDRESS='10.1.0.1'\n\
|
||||||
|
export KEA_AFL_INTERFACE='vethclient'\n\
|
||||||
|
export KEA_AFL_LOOP_MAX=2\n\
|
||||||
|
exec afl-fuzz -M fuzzer1 -t 20000+ -m 50000 -i 'seeds/${f}' -o 'output/config/kea-dhcp${v}' -x /opt/dict.dat -- \"\$progdir/\$program\"\
|
||||||
|
#g" "${fuzzed_executable}"
|
||||||
|
sed -i "2i${sudo}" "${fuzzed_executable}"
|
||||||
|
done
|
||||||
|
done
|
||||||
|
|
||||||
|
cd - > /dev/null
|
||||||
|
|
||||||
|
# Run again as root.
|
||||||
|
if ! sudo -n true; then
|
||||||
|
exec sudo -- "${0}" "${@}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# afl-fuzz says:
|
||||||
|
# To avoid having crashes misinterpreted as timeouts, please log in as root
|
||||||
|
# and temporarily modify /proc/sys/kernel/core_pattern, like so:
|
||||||
|
echo core > /proc/sys/kernel/core_pattern
|
||||||
|
|
||||||
|
# afl-fuzz says:
|
||||||
|
# Whoops, your system uses on-demand CPU frequency scaling, adjusted
|
||||||
|
# between 781 and 4882 MHz. Unfortunately, the scaling algorithm in the
|
||||||
|
# kernel is imperfect and can miss the short-lived processes spawned by
|
||||||
|
# afl-fuzz. To keep things moving, run these commands as root:
|
||||||
|
echo performance | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor > /dev/null
|
||||||
|
|
||||||
|
ulimit -Sd 41932800
|
||||||
|
|
||||||
|
ulimit -c unlimited
|
||||||
|
|
||||||
|
# Create a virtual interface for the server to start listening on.
|
||||||
|
if ip link show vethclient > /dev/null 2>&1; then
|
||||||
|
ip link delete vethclient
|
||||||
|
fi
|
||||||
|
if ip link show vethserver > /dev/null 2>&1; then
|
||||||
|
ip link delete vethserver
|
||||||
|
fi
|
||||||
|
ip link add vethclient type veth peer name vethserver
|
||||||
|
ip -4 addr add 10.1.0.1/24 dev vethclient
|
||||||
|
ip -6 addr add 2001:db8:1::1/64 dev vethclient
|
||||||
|
ip link set dev vethclient up
|
||||||
|
ip link set lo up
|
||||||
|
ip -4 addr add 10.1.0.2/24 dev vethserver
|
||||||
|
ip -6 addr add 2001:db8:1::2/64 dev vethserver
|
||||||
|
ip link set dev vethserver up
|
||||||
|
ip link set lo up
|
||||||
|
|
||||||
|
# Wait for duplicate address detection to be finished so that the
|
||||||
|
# interfaces are ready.
|
||||||
|
while true; do
|
||||||
|
interface_status=$(
|
||||||
|
ip a s vethserver | grep -E 'inet6.*tentative'
|
||||||
|
ip a s vethclient | grep -E 'inet6.*tentative'
|
||||||
|
)
|
||||||
|
if test -n "${interface_status}"; then
|
||||||
|
printf 'Waiting for the following addresses to be assigned to their interfaces:\n%s\n' "${interface_status}"
|
||||||
|
sleep 1
|
||||||
|
else
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
16
fuzz/tests/Makefile.am
Normal file
16
fuzz/tests/Makefile.am
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
SUBDIRS = .
|
||||||
|
|
||||||
|
if FUZZING
|
||||||
|
|
||||||
|
check_SCRIPTS =
|
||||||
|
check_SCRIPTS += test-fuzz-config-kea-dhcp4.sh
|
||||||
|
check_SCRIPTS += test-fuzz-config-kea-dhcp6.sh
|
||||||
|
check_SCRIPTS += test-fuzz-http-endpoint.sh
|
||||||
|
check_SCRIPTS += test-fuzz-packets-kea-dhcp4.sh
|
||||||
|
check_SCRIPTS += test-fuzz-packets-kea-dhcp6.sh
|
||||||
|
check_SCRIPTS += test-fuzz-unix-socket-kea-dhcp4.sh
|
||||||
|
check_SCRIPTS += test-fuzz-unix-socket-kea-dhcp6.sh
|
||||||
|
|
||||||
|
TESTS = $(check_SCRIPTS)
|
||||||
|
|
||||||
|
endif # FUZZING
|
1
fuzz/tests/setup.sh
Symbolic link
1
fuzz/tests/setup.sh
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../setup.sh
|
11
fuzz/tests/test-fuzz-config-kea-dhcp4.sh
Executable file
11
fuzz/tests/test-fuzz-config-kea-dhcp4.sh
Executable file
@ -0,0 +1,11 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
if ! sudo -n true; then exec sudo -E -- "${0}" "${@}"; fi
|
||||||
|
|
||||||
|
script_path=$(cd "$(dirname "${0}")" && pwd)
|
||||||
|
|
||||||
|
script_basename=$(basename "${0}")
|
||||||
|
|
||||||
|
tested_binary=$(printf '%s' "${script_basename}" | sed 's/test-//g;s/.sh//g')
|
||||||
|
|
||||||
|
"${script_path}/../${tested_binary}"
|
11
fuzz/tests/test-fuzz-config-kea-dhcp6.sh
Executable file
11
fuzz/tests/test-fuzz-config-kea-dhcp6.sh
Executable file
@ -0,0 +1,11 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
if ! sudo -n true; then exec sudo -E -- "${0}" "${@}"; fi
|
||||||
|
|
||||||
|
script_path=$(cd "$(dirname "${0}")" && pwd)
|
||||||
|
|
||||||
|
script_basename=$(basename "${0}")
|
||||||
|
|
||||||
|
tested_binary=$(printf '%s' "${script_basename}" | sed 's/test-//g;s/.sh//g')
|
||||||
|
|
||||||
|
"${script_path}/../${tested_binary}"
|
11
fuzz/tests/test-fuzz-http-endpoint.sh
Executable file
11
fuzz/tests/test-fuzz-http-endpoint.sh
Executable file
@ -0,0 +1,11 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
if ! sudo -n true; then exec sudo -E -- "${0}" "${@}"; fi
|
||||||
|
|
||||||
|
script_path=$(cd "$(dirname "${0}")" && pwd)
|
||||||
|
|
||||||
|
script_basename=$(basename "${0}")
|
||||||
|
|
||||||
|
tested_binary=$(printf '%s' "${script_basename}" | sed 's/test-//g;s/.sh//g')
|
||||||
|
|
||||||
|
"${script_path}/../${tested_binary}"
|
11
fuzz/tests/test-fuzz-packets-kea-dhcp4.sh
Executable file
11
fuzz/tests/test-fuzz-packets-kea-dhcp4.sh
Executable file
@ -0,0 +1,11 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
if ! sudo -n true; then exec sudo -E -- "${0}" "${@}"; fi
|
||||||
|
|
||||||
|
script_path=$(cd "$(dirname "${0}")" && pwd)
|
||||||
|
|
||||||
|
script_basename=$(basename "${0}")
|
||||||
|
|
||||||
|
tested_binary=$(printf '%s' "${script_basename}" | sed 's/test-//g;s/.sh//g')
|
||||||
|
|
||||||
|
"${script_path}/../${tested_binary}"
|
11
fuzz/tests/test-fuzz-packets-kea-dhcp6.sh
Executable file
11
fuzz/tests/test-fuzz-packets-kea-dhcp6.sh
Executable file
@ -0,0 +1,11 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
if ! sudo -n true; then exec sudo -E -- "${0}" "${@}"; fi
|
||||||
|
|
||||||
|
script_path=$(cd "$(dirname "${0}")" && pwd)
|
||||||
|
|
||||||
|
script_basename=$(basename "${0}")
|
||||||
|
|
||||||
|
tested_binary=$(printf '%s' "${script_basename}" | sed 's/test-//g;s/.sh//g')
|
||||||
|
|
||||||
|
"${script_path}/../${tested_binary}"
|
11
fuzz/tests/test-fuzz-unix-socket-kea-dhcp4.sh
Executable file
11
fuzz/tests/test-fuzz-unix-socket-kea-dhcp4.sh
Executable file
@ -0,0 +1,11 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
if ! sudo -n true; then exec sudo -E -- "${0}" "${@}"; fi
|
||||||
|
|
||||||
|
script_path=$(cd "$(dirname "${0}")" && pwd)
|
||||||
|
|
||||||
|
script_basename=$(basename "${0}")
|
||||||
|
|
||||||
|
tested_binary=$(printf '%s' "${script_basename}" | sed 's/test-//g;s/.sh//g')
|
||||||
|
|
||||||
|
"${script_path}/../${tested_binary}"
|
11
fuzz/tests/test-fuzz-unix-socket-kea-dhcp6.sh
Executable file
11
fuzz/tests/test-fuzz-unix-socket-kea-dhcp6.sh
Executable file
@ -0,0 +1,11 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
if ! sudo -n true; then exec sudo -E -- "${0}" "${@}"; fi
|
||||||
|
|
||||||
|
script_path=$(cd "$(dirname "${0}")" && pwd)
|
||||||
|
|
||||||
|
script_basename=$(basename "${0}")
|
||||||
|
|
||||||
|
tested_binary=$(printf '%s' "${script_basename}" | sed 's/test-//g;s/.sh//g')
|
||||||
|
|
||||||
|
"${script_path}/../${tested_binary}"
|
@ -41,7 +41,7 @@
|
|||||||
#include <dhcpsrv/lease_mgr.h>
|
#include <dhcpsrv/lease_mgr.h>
|
||||||
#include <dhcpsrv/lease_mgr_factory.h>
|
#include <dhcpsrv/lease_mgr_factory.h>
|
||||||
#include <dhcpsrv/ncr_generator.h>
|
#include <dhcpsrv/ncr_generator.h>
|
||||||
#include <dhcpsrv/packet-fuzzer.h>
|
#include <dhcpsrv/packet_fuzzer.h>
|
||||||
#include <dhcpsrv/resource_handler.h>
|
#include <dhcpsrv/resource_handler.h>
|
||||||
#include <dhcpsrv/shared_network.h>
|
#include <dhcpsrv/shared_network.h>
|
||||||
#include <dhcpsrv/subnet.h>
|
#include <dhcpsrv/subnet.h>
|
||||||
@ -1144,7 +1144,7 @@ Dhcpv4Srv::run() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set up structures needed for fuzzing.
|
// Set up structures needed for fuzzing.
|
||||||
PacketFuzzer fuzzer(4, server_port_, interface, address);
|
PacketFuzzer fuzzer(server_port_, interface, address);
|
||||||
|
|
||||||
// The next line is needed as a signature for AFL to recognize that we are
|
// The next line is needed as a signature for AFL to recognize that we are
|
||||||
// running persistent fuzzing. This has to be in the main image file.
|
// running persistent fuzzing. This has to be in the main image file.
|
||||||
@ -5180,7 +5180,8 @@ void Dhcpv4Srv::discardPackets() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint16_t Dhcpv4Srv::getServerPort() const {
|
uint16_t Dhcpv4Srv::getServerPort() const {
|
||||||
char const* const randomize(getenv("KEA_DHCP4_FUZZING_RANDOMIZE_PORT"));
|
#ifdef FUZZING
|
||||||
|
char const* const randomize(getenv("KEA_DHCP4_FUZZING_ROTATE_PORT"));
|
||||||
if (randomize) {
|
if (randomize) {
|
||||||
InterprocessSyncFile file("kea-dhcp4-fuzzing-randomize-port");
|
InterprocessSyncFile file("kea-dhcp4-fuzzing-randomize-port");
|
||||||
InterprocessSyncLocker locker(file);
|
InterprocessSyncLocker locker(file);
|
||||||
@ -5209,6 +5210,7 @@ uint16_t Dhcpv4Srv::getServerPort() const {
|
|||||||
locker.unlock();
|
locker.unlock();
|
||||||
return port;
|
return port;
|
||||||
}
|
}
|
||||||
|
#endif // FUZZING
|
||||||
return server_port_;
|
return server_port_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@
|
|||||||
#include <dhcpsrv/lease_mgr.h>
|
#include <dhcpsrv/lease_mgr.h>
|
||||||
#include <dhcpsrv/lease_mgr_factory.h>
|
#include <dhcpsrv/lease_mgr_factory.h>
|
||||||
#include <dhcpsrv/ncr_generator.h>
|
#include <dhcpsrv/ncr_generator.h>
|
||||||
#include <dhcpsrv/packet-fuzzer.h>
|
#include <dhcpsrv/packet_fuzzer.h>
|
||||||
#include <dhcpsrv/subnet.h>
|
#include <dhcpsrv/subnet.h>
|
||||||
#include <dhcpsrv/subnet_selector.h>
|
#include <dhcpsrv/subnet_selector.h>
|
||||||
#include <dhcpsrv/utils.h>
|
#include <dhcpsrv/utils.h>
|
||||||
@ -616,7 +616,7 @@ Dhcpv6Srv::run() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set up structures needed for fuzzing.
|
// Set up structures needed for fuzzing.
|
||||||
PacketFuzzer fuzzer(6, server_port_, interface, address);
|
PacketFuzzer fuzzer(server_port_, interface, address);
|
||||||
|
|
||||||
// The next line is needed as a signature for AFL to recognize that we are
|
// The next line is needed as a signature for AFL to recognize that we are
|
||||||
// running persistent fuzzing. This has to be in the main image file.
|
// running persistent fuzzing. This has to be in the main image file.
|
||||||
@ -4920,7 +4920,8 @@ void Dhcpv6Srv::discardPackets() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint16_t Dhcpv6Srv::getServerPort() const {
|
uint16_t Dhcpv6Srv::getServerPort() const {
|
||||||
char const* const randomize(getenv("KEA_DHCP6_FUZZING_RANDOMIZE_PORT"));
|
#ifdef FUZZING
|
||||||
|
char const* const randomize(getenv("KEA_DHCP6_FUZZING_ROTATE_PORT"));
|
||||||
if (randomize) {
|
if (randomize) {
|
||||||
InterprocessSyncFile file("kea-dhcp6-fuzzing-randomize-port");
|
InterprocessSyncFile file("kea-dhcp6-fuzzing-randomize-port");
|
||||||
InterprocessSyncLocker locker(file);
|
InterprocessSyncLocker locker(file);
|
||||||
@ -4949,6 +4950,7 @@ uint16_t Dhcpv6Srv::getServerPort() const {
|
|||||||
locker.unlock();
|
locker.unlock();
|
||||||
return port;
|
return port;
|
||||||
}
|
}
|
||||||
|
#endif // FUZZING
|
||||||
return server_port_;
|
return server_port_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,7 +173,7 @@ libkea_dhcpsrv_la_SOURCES += parsers/simple_parser6.cc
|
|||||||
libkea_dhcpsrv_la_SOURCES += parsers/simple_parser6.h
|
libkea_dhcpsrv_la_SOURCES += parsers/simple_parser6.h
|
||||||
|
|
||||||
if FUZZING
|
if FUZZING
|
||||||
libkea_dhcpsrv_la_SOURCES += packet-fuzzer.cc packet-fuzzer.h
|
libkea_dhcpsrv_la_SOURCES += packet_fuzzer.cc packet_fuzzer.h
|
||||||
libkea_dhcpsrv_la_SOURCES += fuzz_log.cc fuzz_log.h
|
libkea_dhcpsrv_la_SOURCES += fuzz_log.cc fuzz_log.h
|
||||||
libkea_dhcpsrv_la_SOURCES += fuzz_messages.cc fuzz_messages.h
|
libkea_dhcpsrv_la_SOURCES += fuzz_messages.cc fuzz_messages.h
|
||||||
endif # FUZZING
|
endif # FUZZING
|
||||||
@ -352,7 +352,7 @@ libkea_dhcpsrv_include_HEADERS = \
|
|||||||
|
|
||||||
if FUZZING
|
if FUZZING
|
||||||
libkea_dhcpsrv_include_HEADERS += \
|
libkea_dhcpsrv_include_HEADERS += \
|
||||||
packet-fuzzer.h \
|
packet_fuzzer.h \
|
||||||
fuzz_log.h \
|
fuzz_log.h \
|
||||||
fuzz_messages.h
|
fuzz_messages.h
|
||||||
endif
|
endif
|
||||||
|
@ -94,7 +94,6 @@ LeaseMgrFactory::destroy() {
|
|||||||
.arg(getLeaseMgrPtr()->getType());
|
.arg(getLeaseMgrPtr()->getType());
|
||||||
getLeaseMgrPtr().reset();
|
getLeaseMgrPtr().reset();
|
||||||
}
|
}
|
||||||
getLeaseMgrPtr().reset();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -8,8 +8,9 @@
|
|||||||
|
|
||||||
#ifdef FUZZING
|
#ifdef FUZZING
|
||||||
|
|
||||||
|
#include <asiolink/io_address.h>
|
||||||
#include <dhcp/dhcp6.h>
|
#include <dhcp/dhcp6.h>
|
||||||
#include <dhcpsrv/packet-fuzzer.h>
|
#include <dhcpsrv/packet_fuzzer.h>
|
||||||
#include <dhcpsrv/fuzz_log.h>
|
#include <dhcpsrv/fuzz_log.h>
|
||||||
|
|
||||||
#include <boost/lexical_cast.hpp>
|
#include <boost/lexical_cast.hpp>
|
||||||
@ -26,6 +27,7 @@
|
|||||||
#include <ctime>
|
#include <ctime>
|
||||||
|
|
||||||
using namespace isc;
|
using namespace isc;
|
||||||
|
using namespace isc::asiolink;
|
||||||
using namespace isc::dhcp;
|
using namespace isc::dhcp;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
@ -35,8 +37,7 @@ constexpr size_t PacketFuzzer::MAX_SEND_SIZE;
|
|||||||
constexpr long PacketFuzzer::MAX_LOOP_COUNT;
|
constexpr long PacketFuzzer::MAX_LOOP_COUNT;
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
PacketFuzzer::PacketFuzzer(int const ipversion,
|
PacketFuzzer::PacketFuzzer(uint16_t const port,
|
||||||
uint16_t const port,
|
|
||||||
string const interface,
|
string const interface,
|
||||||
string const address)
|
string const address)
|
||||||
: loop_max_(MAX_LOOP_COUNT), sockaddr_len_(0), sockaddr_ptr_(nullptr), sockfd_(-1) {
|
: loop_max_(MAX_LOOP_COUNT), sockaddr_len_(0), sockaddr_ptr_(nullptr), sockfd_(-1) {
|
||||||
@ -55,25 +56,26 @@ PacketFuzzer::PacketFuzzer(int const ipversion,
|
|||||||
try {
|
try {
|
||||||
loop_max_ = boost::lexical_cast<long>(loop_max_ptr);
|
loop_max_ = boost::lexical_cast<long>(loop_max_ptr);
|
||||||
} catch (const boost::bad_lexical_cast&) {
|
} catch (const boost::bad_lexical_cast&) {
|
||||||
reason << "cannot convert loop count " << loop_max_ptr
|
isc_throw(FuzzInitFail,
|
||||||
<< " to an integer";
|
"cannot convert loop count " << loop_max_ptr << " to an integer");
|
||||||
isc_throw(FuzzInitFail, reason.str());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (loop_max_ <= 0) {
|
if (loop_max_ <= 0) {
|
||||||
reason << "KEA_AFL_LOOP_MAX is " << loop_max_ << ". "
|
isc_throw(FuzzInitFail, "KEA_AFL_LOOP_MAX is "
|
||||||
<< "It must be an integer greater than zero.";
|
<< loop_max_ << ". "
|
||||||
isc_throw(FuzzInitFail, reason.str());
|
<< "It must be an integer greater than zero.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IOAddress io_address(address);
|
||||||
|
|
||||||
// Set up address structures used to route the packets from AFL to Kea.
|
// Set up address structures used to route the packets from AFL to Kea.
|
||||||
createAddressStructures(ipversion, port, interface, address);
|
createAddressStructures(port, interface, io_address);
|
||||||
|
|
||||||
// Create the socket through which packets read from stdin will be sent
|
// Create the socket through which packets read from stdin will be sent
|
||||||
// to the port on which Kea is listening. This is closed in the
|
// to the port on which Kea is listening. This is closed in the
|
||||||
// destructor.
|
// destructor.
|
||||||
sockfd_ = socket((ipversion == 4) ? AF_INET : AF_INET6, SOCK_DGRAM, 0);
|
sockfd_ = socket(io_address.isV4() ? AF_INET : AF_INET6, SOCK_DGRAM, 0);
|
||||||
if (sockfd_ < 0) {
|
if (sockfd_ < 0) {
|
||||||
LOG_FATAL(fuzz_logger, FUZZ_SOCKET_CREATE_FAIL)
|
LOG_FATAL(fuzz_logger, FUZZ_SOCKET_CREATE_FAIL)
|
||||||
.arg(strerror(errno));
|
.arg(strerror(errno));
|
||||||
@ -98,38 +100,35 @@ PacketFuzzer::~PacketFuzzer() {
|
|||||||
|
|
||||||
// Set up address structures.
|
// Set up address structures.
|
||||||
void
|
void
|
||||||
PacketFuzzer::createAddressStructures(int const ipversion,
|
PacketFuzzer::createAddressStructures(uint16_t const port,
|
||||||
uint16_t const port,
|
string const& interface,
|
||||||
string const interface,
|
IOAddress const& io_address) {
|
||||||
string const address) {
|
string const address(io_address.toText());
|
||||||
stringstream reason; // Used in error messages
|
|
||||||
|
|
||||||
// Set up the appropriate data structure depending on the address given.
|
// Set up the appropriate data structure depending on the address given.
|
||||||
if (ipversion == 6 && address.find(":") != string::npos) {
|
if (io_address.isV6()) {
|
||||||
// Expecting IPv6 and the address contains a colon, so assume it is an
|
// Expecting IPv6 and the address contains a colon, so assume it is an
|
||||||
// an IPv6 address.
|
// an IPv6 address.
|
||||||
memset(&servaddr6_, 0, sizeof (servaddr6_));
|
memset(&servaddr6_, 0, sizeof (servaddr6_));
|
||||||
|
|
||||||
servaddr6_.sin6_family = AF_INET6;
|
servaddr6_.sin6_family = AF_INET6;
|
||||||
if (inet_pton(AF_INET6, address.c_str(), &servaddr6_.sin6_addr) != 1) {
|
if (inet_pton(AF_INET6, address.c_str(), &servaddr6_.sin6_addr) != 1) {
|
||||||
reason << "inet_pton() failed: can't convert "
|
isc_throw(FuzzInitFail,
|
||||||
<< address << " to an IPv6 address" << endl;
|
"inet_pton() failed: can't convert " << address << " to an IPv6 address");
|
||||||
isc_throw(FuzzInitFail, reason.str());
|
|
||||||
}
|
}
|
||||||
servaddr6_.sin6_port = htons(port);
|
servaddr6_.sin6_port = htons(port);
|
||||||
|
|
||||||
// Interface ID is needed for IPv6 address structures.
|
// Interface ID is needed for IPv6 address structures.
|
||||||
servaddr6_.sin6_scope_id = if_nametoindex(interface.c_str());
|
servaddr6_.sin6_scope_id = if_nametoindex(interface.c_str());
|
||||||
if (servaddr6_.sin6_scope_id == 0) {
|
if (servaddr6_.sin6_scope_id == 0) {
|
||||||
reason << "error retrieving interface ID for "
|
isc_throw(FuzzInitFail,
|
||||||
<< interface << ": " << strerror(errno);
|
"error retrieving interface ID for " << interface << ": " << strerror(errno));
|
||||||
isc_throw(FuzzInitFail, reason.str());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sockaddr_ptr_ = reinterpret_cast<sockaddr*>(&servaddr6_);
|
sockaddr_ptr_ = reinterpret_cast<sockaddr*>(&servaddr6_);
|
||||||
sockaddr_len_ = sizeof(servaddr6_);
|
sockaddr_len_ = sizeof(servaddr6_);
|
||||||
|
|
||||||
} else if (ipversion == 4 && address.find(".") != string::npos) {
|
} else if (io_address.isV4()) {
|
||||||
// Expecting an IPv4 address and it contains a dot, so assume it is.
|
// Expecting an IPv4 address and it contains a dot, so assume it is.
|
||||||
// This check is done after the IPv6 check, as it is possible for an
|
// This check is done after the IPv6 check, as it is possible for an
|
||||||
// IPv4 address to be embedded in an IPv6 one.
|
// IPv4 address to be embedded in an IPv6 one.
|
||||||
@ -137,9 +136,8 @@ PacketFuzzer::createAddressStructures(int const ipversion,
|
|||||||
|
|
||||||
servaddr4_.sin_family = AF_INET;
|
servaddr4_.sin_family = AF_INET;
|
||||||
if (inet_pton(AF_INET, address.c_str(), &servaddr4_.sin_addr) != 1) {
|
if (inet_pton(AF_INET, address.c_str(), &servaddr4_.sin_addr) != 1) {
|
||||||
reason << "inet_pton() failed: can't convert "
|
isc_throw(FuzzInitFail,
|
||||||
<< address << " to an IPv6 address" << endl;
|
"inet_pton() failed: can't convert " << address << " to an IPv4 address");
|
||||||
isc_throw(FuzzInitFail, reason.str());
|
|
||||||
}
|
}
|
||||||
servaddr4_.sin_port = htons(port);
|
servaddr4_.sin_port = htons(port);
|
||||||
|
|
||||||
@ -147,12 +145,9 @@ PacketFuzzer::createAddressStructures(int const ipversion,
|
|||||||
sockaddr_len_ = sizeof(servaddr4_);
|
sockaddr_len_ = sizeof(servaddr4_);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
reason << "Expected IP version (" << ipversion << ") is not "
|
// Should never happen.
|
||||||
<< "4 or 6, or the given address " << address << " does not "
|
isc_throw(FuzzInitFail, "unknown IOAddress IP version");
|
||||||
<< "match the IP version expected";
|
|
||||||
isc_throw(FuzzInitFail, reason.str());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
#ifdef FUZZING
|
#ifdef FUZZING
|
||||||
|
|
||||||
|
#include <asiolink/io_address.h>
|
||||||
#include <exceptions/exceptions.h>
|
#include <exceptions/exceptions.h>
|
||||||
|
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
@ -16,10 +17,7 @@
|
|||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include <condition_variable>
|
|
||||||
#include <mutex>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <thread>
|
|
||||||
|
|
||||||
namespace isc {
|
namespace isc {
|
||||||
|
|
||||||
@ -69,12 +67,9 @@ public:
|
|||||||
/// Sets up data structures to access the address/port being used to
|
/// Sets up data structures to access the address/port being used to
|
||||||
/// transfer data from AFL to Kea.
|
/// transfer data from AFL to Kea.
|
||||||
///
|
///
|
||||||
/// @param ipversion Either 4 or 6 depending on what IP version the
|
|
||||||
/// server responds to.
|
|
||||||
/// @param port Port on which the server is listening, and hence the
|
/// @param port Port on which the server is listening, and hence the
|
||||||
/// port to which the fuzzer will send input from AFL.
|
/// port to which the fuzzer will send input from AFL.
|
||||||
PacketFuzzer(int const ipversion,
|
PacketFuzzer(uint16_t const port,
|
||||||
uint16_t const port,
|
|
||||||
std::string const interface,
|
std::string const interface,
|
||||||
std::string const address);
|
std::string const address);
|
||||||
|
|
||||||
@ -110,19 +105,15 @@ private:
|
|||||||
/// Create the address structures describing the address/port on whick Kea
|
/// Create the address structures describing the address/port on whick Kea
|
||||||
/// is listening for packets from AFL.
|
/// is listening for packets from AFL.
|
||||||
///
|
///
|
||||||
/// @param ipversion Either 4 or 6 depending on which IP version address
|
/// @param port Port to be used.
|
||||||
/// is expected.
|
/// @param interface Interface through which the fuzzer is sending packets to Kea.
|
||||||
/// @param interface Interface through which the fuzzer is sending packets
|
/// @param io_address Address on the interface that will be used.
|
||||||
/// to Kea.
|
|
||||||
/// @param address Address on the interface that will be used.
|
|
||||||
/// @param port Port to be used.
|
|
||||||
///
|
///
|
||||||
/// @throws FuzzInitFail Thrown if the address is not in the expected
|
/// @throws FuzzInitFail Thrown if the address is not in the expected
|
||||||
/// format.
|
/// format.
|
||||||
void createAddressStructures(int const ipversion,
|
void createAddressStructures(uint16_t const port,
|
||||||
uint16_t const port,
|
std::string const& interface,
|
||||||
std::string const interface,
|
isc::asiolink::IOAddress const& io_address);
|
||||||
std::string const address);
|
|
||||||
|
|
||||||
// Other member variables.
|
// Other member variables.
|
||||||
long loop_max_; //< Maximum number of loop iterations
|
long loop_max_; //< Maximum number of loop iterations
|
@ -131,6 +131,10 @@ libdhcpsrv_unittests_SOURCES += tracking_lease_mgr_unittest.cc
|
|||||||
libdhcpsrv_unittests_SOURCES += network_state_unittest.cc
|
libdhcpsrv_unittests_SOURCES += network_state_unittest.cc
|
||||||
libdhcpsrv_unittests_SOURCES += network_unittest.cc
|
libdhcpsrv_unittests_SOURCES += network_unittest.cc
|
||||||
|
|
||||||
|
if FUZZING
|
||||||
|
libdhcpsrv_unittests_SOURCES += packet_fuzzer_unittest.cc
|
||||||
|
endif
|
||||||
|
|
||||||
libdhcpsrv_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
|
libdhcpsrv_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
|
||||||
|
|
||||||
libdhcpsrv_unittests_CXXFLAGS = $(AM_CXXFLAGS)
|
libdhcpsrv_unittests_CXXFLAGS = $(AM_CXXFLAGS)
|
||||||
|
32
src/lib/dhcpsrv/tests/packet_fuzzer_unittest.cc
Normal file
32
src/lib/dhcpsrv/tests/packet_fuzzer_unittest.cc
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
// Copyright (C) 2024 Internet Systems Consortium, Inc. ("ISC")
|
||||||
|
//
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
#include <config.h>
|
||||||
|
|
||||||
|
#include <asiolink/io_error.h>
|
||||||
|
#include <dhcpsrv/packet_fuzzer.h>
|
||||||
|
#include <testutils/gtest_utils.h>
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
namespace isc {
|
||||||
|
namespace dhcp {
|
||||||
|
namespace test {
|
||||||
|
|
||||||
|
TEST(PacketFuzzerTest, constructor) {
|
||||||
|
PacketFuzzer(67, "testeth", "127.0.0.1");
|
||||||
|
// v6 requires valid interface. Skip positive test case.
|
||||||
|
|
||||||
|
// Negative test cases
|
||||||
|
EXPECT_THROW_MSG(PacketFuzzer(547, "invalid_eth%", "fe80::1"), FuzzInitFail,
|
||||||
|
"error retrieving interface ID for invalid_eth%: No such device");
|
||||||
|
EXPECT_THROW_MSG(PacketFuzzer(1234, "testeth", "abcd"), isc::asiolink::IOError,
|
||||||
|
"Failed to convert string to address 'abcd': Invalid argument");
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace test
|
||||||
|
} // namespace dhcp
|
||||||
|
} // namespace isc
|
@ -183,7 +183,7 @@ public:
|
|||||||
class MultiThreadingInvalidOperation : public Exception {
|
class MultiThreadingInvalidOperation : public Exception {
|
||||||
public:
|
public:
|
||||||
MultiThreadingInvalidOperation(const char* file, size_t line, const char* what) :
|
MultiThreadingInvalidOperation(const char* file, size_t line, const char* what) :
|
||||||
isc::Exception(file, line, what) {};
|
isc::Exception(file, line, what) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
///
|
///
|
||||||
|
@ -183,18 +183,20 @@ Path::replaceParentPath(string const& replacement) {
|
|||||||
TemporaryDirectory::TemporaryDirectory() {
|
TemporaryDirectory::TemporaryDirectory() {
|
||||||
char dir[]("/tmp/kea-tmpdir-XXXXXX");
|
char dir[]("/tmp/kea-tmpdir-XXXXXX");
|
||||||
char const* dir_name = mkdtemp(dir);
|
char const* dir_name = mkdtemp(dir);
|
||||||
if(!dir_name) {
|
if (!dir_name) {
|
||||||
isc_throw(Unexpected, "mkdtemp failed " << dir << ": " << strerror(errno));
|
isc_throw(Unexpected, "mkdtemp failed " << dir << ": " << strerror(errno));
|
||||||
}
|
}
|
||||||
dir_name_ = string(dir_name);
|
dir_name_ = string(dir_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
TemporaryDirectory::~TemporaryDirectory() {
|
TemporaryDirectory::~TemporaryDirectory() {
|
||||||
rmdir(dir_name_.c_str());
|
|
||||||
DIR *dir(opendir(dir_name_.c_str()));
|
DIR *dir(opendir(dir_name_.c_str()));
|
||||||
|
if (!dir) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
struct dirent *i;
|
struct dirent *i;
|
||||||
string filepath;
|
string filepath;
|
||||||
|
|
||||||
while ((i = readdir(dir))) {
|
while ((i = readdir(dir))) {
|
||||||
if (strcmp(i->d_name, ".") == 0 || strcmp(i->d_name, "..") == 0) {
|
if (strcmp(i->d_name, ".") == 0 || strcmp(i->d_name, "..") == 0) {
|
||||||
continue;
|
continue;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user