aflplusplus: Add optional build with nyx mode (#514406)

This commit is contained in:
Moritz Sanft
2026-04-28 14:30:15 +00:00
committed by GitHub
8 changed files with 1767 additions and 3 deletions
+6
View File
@@ -7757,6 +7757,12 @@
githubId = 411447;
name = "Leo Gaspard";
};
ekzyis = {
email = "ramdip.singhgill@gmail.com";
github = "ekzyis";
githubId = 27162016;
name = "Ramdip Gill";
};
elasticdog = {
email = "aaron@elasticdog.com";
github = "elasticdog";
+46 -3
View File
@@ -17,6 +17,7 @@
wine ? null,
cmocka,
llvmPackages,
withNyx ? false,
}:
# wine fuzzing is only known to work for win32 binaries, and using a mixture of
@@ -24,6 +25,10 @@
# a full 32bit version of this package if you want to do wine fuzzing
assert (wine != null) -> (stdenv.targetPlatform.system == "i686-linux");
# nyx mode is only available on x86_64-linux,
# see nyx_mode/build_nyx_support.sh in source code of aflplusplus
assert withNyx -> (stdenv.targetPlatform.system == "x86_64-linux");
let
aflplusplus-qemu = callPackage ./qemu.nix { };
qemu-exe-name =
@@ -35,6 +40,17 @@ let
throw "aflplusplus: no support for ${stdenv.targetPlatform.system}!";
libdislocator = callPackage ./libdislocator.nix { inherit aflplusplus; };
libtokencap = callPackage ./libtokencap.nix { inherit aflplusplus; };
libnyx =
if withNyx then callPackage ./nyx_mode/libnyx/libnyx.nix { inherit aflplusplus; } else null;
qemu-nyx =
if withNyx then callPackage ./nyx_mode/QEMU-Nyx/qemu-nyx.nix { inherit aflplusplus; } else null;
nyx-packer =
if withNyx then
callPackage ./nyx_mode/packer/packer.nix { inherit aflplusplus qemu-nyx; }
else
null;
aflplusplus = stdenvNoCC.mkDerivation rec {
pname = "aflplusplus";
version = "4.35c";
@@ -43,7 +59,12 @@ let
owner = "AFLplusplus";
repo = "AFLplusplus";
tag = "v${version}";
hash = "sha256-j5YH39JKcjYuDqyl+KRMtgn3UoeWEW1z7m4ysf2uilc=";
hash =
if withNyx then
"sha256-srHrYPEb0UAP/G9cOxJOZ9D6v9pxqez28suPsa70E2M="
else
"sha256-j5YH39JKcjYuDqyl+KRMtgn3UoeWEW1z7m4ysf2uilc=";
fetchSubmodules = withNyx;
};
enableParallelBuilding = true;
@@ -68,6 +89,10 @@ let
# warning: "_FORTIFY_SOURCE" redefined
hardeningDisable = [ "fortify" ];
# We build nyx mode dependencies ourselves, so this patch skips
# build_nyx_support.sh in the aflplusplus source code. It also skips
# test-nyx-mode.sh because we can't test nyx mode in the sandbox.
patches = lib.optional withNyx ./nyx_mode/nyx_mode.patch;
postPatch = ''
# Don't care about this.
rm Android.bp
@@ -94,6 +119,9 @@ let
substituteInPlace GNUmakefile.llvm \
--replace-fail "\$(LLVM_BINDIR)/clang" "${clang}/bin/clang"
''
+ lib.optionalString withNyx ''
patchShebangs nyx_mode/build_nyx_support.sh
'';
env.NIX_CFLAGS_COMPILE = toString [
@@ -145,7 +173,15 @@ let
--replace-fail "cgdelete" "${libcgroup}/bin/cgdelete"
patchShebangs $out/bin
''
+ lib.optionalString withNyx ''
# Use same FHS as if built from source using build_nyx_support.sh. This
# means libnyx.so must be next to afl binaries and nyx_mode dependencies
# are in nyx_mode/.
cp ${libnyx}/lib/libnyx.so $out/bin
mkdir $out/nyx_mode
ln -s ${nyx-packer} $out/nyx_mode/packer
ln -s ${qemu-nyx} $out/nyx_mode/QEMU-Nyx
''
+ lib.optionalString (wine != null) ''
substitute afl-wine-trace $out/bin/afl-wine-trace \
@@ -166,6 +202,7 @@ let
file
cmocka
];
doInstallCheck = true;
installCheckPhase = ''
runHook preInstallCheck
@@ -191,7 +228,13 @@ let
'';
passthru = {
inherit libdislocator libtokencap;
inherit
libdislocator
libtokencap
libnyx
nyx-packer
qemu-nyx
;
qemu = aflplusplus-qemu;
};
@@ -0,0 +1,77 @@
{
stdenv,
lib,
fetchFromGitHub,
python3,
pkg-config,
flex,
bison,
glib,
pixman,
aflplusplus,
}:
# this derivation assumes x86_64-linux
assert stdenv.targetPlatform.system == "x86_64-linux";
stdenv.mkDerivation {
version = builtins.readFile (aflplusplus.src + "/nyx_mode/QEMU_NYX_VERSION");
pname = "QEMU-Nyx";
src = aflplusplus.src;
postUnpack = ''
sourceRoot="$sourceRoot/nyx_mode/QEMU-Nyx"
'';
# same flags for ./configure as ./compile_qemu_nyx.sh static would set
configureFlags = [
"--target-list=x86_64-softmmu"
"--disable-docs"
"--disable-gtk"
"--disable-werror"
"--disable-capstone"
"--disable-libssh"
"--disable-tools"
"--enable-nyx"
"--enable-nyx-static"
];
nativeBuildInputs = [
python3
pkg-config
flex
bison
];
buildInputs = [
glib
pixman
];
enableParallelBuilding = true;
preConfigure = ''
CAPSTONE_ROOT=$PWD/capstone_v4
LIBXDC_ROOT=$PWD/libxdc
make -C $CAPSTONE_ROOT -j$(nproc)
make -C $LIBXDC_ROOT -j$(nproc) clean
# For some reason the Makefile of libxdc clears LDFLAGS; we remove that line
# so ld can find libcapstone.so.4
sed -i '3d' $LIBXDC_ROOT/Makefile
NO_LTO=1 LDFLAGS="-L$CAPSTONE_ROOT -L$LIBXDC_ROOT" CFLAGS="-I$CAPSTONE_ROOT/include/" make -C $LIBXDC_ROOT -j$(nproc)
export LIBS="-L$CAPSTONE_ROOT -L$LIBXDC_ROOT/"
export QEMU_CFLAGS="-I$CAPSTONE_ROOT/include/ -I$LIBXDC_ROOT/ $QEMU_CFLAGS"
'';
meta = {
homepage = "https://github.com/nyx-fuzz/QEMU-Nyx";
description = "Nyx's fork of QEMU";
license = lib.licenses.gpl2Only;
platforms = lib.platforms.x86_64;
maintainers = with lib.maintainers; [ ekzyis ];
};
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,39 @@
{
lib,
stdenv,
rustPlatform,
aflplusplus,
python3,
}:
rustPlatform.buildRustPackage {
version = builtins.readFile (aflplusplus.src + "/nyx_mode/LIBNYX_VERSION");
pname = "libnyx";
src = aflplusplus.src;
postUnpack = ''
sourceRoot="$sourceRoot/nyx_mode/libnyx/libnyx"
cp ${./Cargo.lock} "$sourceRoot/Cargo.lock"
'';
cargoLock = {
lockFile = ./Cargo.lock;
};
doCheck = false;
installPhase = ''
runHook preInstall
mkdir -p $out/lib
cp "target/${stdenv.hostPlatform.rust.rustcTarget}/release/liblibnyx.so" $out/lib/libnyx.so
runHook postInstall
'';
meta = {
homepage = "https://github.com/nyx-fuzz/libnyx";
description = "Rust library to build hypervisor-based snapshot fuzzers";
license = lib.licenses.gpl2Only;
platforms = lib.platforms.linux;
maintainers = with lib.maintainers; [ ekzyis ];
};
}
@@ -0,0 +1,98 @@
diff --git a/nyx_mode/build_nyx_support.sh b/nyx_mode/build_nyx_support.sh
index 9b75b7bf..b7ec92e8 100755
--- a/nyx_mode/build_nyx_support.sh
+++ b/nyx_mode/build_nyx_support.sh
@@ -7,7 +7,8 @@ echo " Nyx build script"
echo "================================================="
echo
-echo "[*] Performing basic sanity checks..."
+echo "[+] Nyx mode dependencies are built in other derivations, skipping ..."
+exit 1
if [ "$CI" = "true" ]; then
diff --git a/test/test-nyx-mode.sh b/test/test-nyx-mode.sh
index 7aaa8c5b..5f624ada 100755
--- a/test/test-nyx-mode.sh
+++ b/test/test-nyx-mode.sh
@@ -4,78 +4,7 @@
$ECHO "$BLUE[*] Testing: nyx_mode"
-test "$CI" = "true" && {
- $ECHO "$YELLOW[-] nyx_mode cannot be tested in the Github CI, skipping ..."
-} || {
-
- unset AFL_CC
-
- test -e ../libnyx.so && {
- ../afl-cc -o test-instr ../test-instr.c > errors 2>&1
- test -e test-instr && {
- {
- rm -rf nyx-test in out
- $ECHO "$GREY[*] running nyx_packer"
- python3 ../nyx_mode/packer/packer/nyx_packer.py \
- ./test-instr \
- nyx-test \
- afl \
- instrumentation \
- --fast_reload_mode \
- --purge > /dev/null 2>&1
-
- test -e nyx-test/test-instr && {
-
- $ECHO "$GREY[*] running nyx_config_gen"
- python3 ../nyx_mode/packer/packer/nyx_config_gen.py nyx-test Kernel > /dev/null 2>&1
-
- test -e nyx-test/config.ron && {
- sudo modprobe -r kvm-intel
- sudo modprobe -r kvm
- sudo modprobe kvm enable_vmware_backdoor=y
- sudo modprobe kvm-intel
- #cat /sys/module/kvm/parameters/enable_vmware_backdoor
-
- mkdir -p in
- echo 00000 > in/in
- $ECHO "$GREY[*] running afl-fuzz for nyx_mode, this will take approx 10 seconds"
- {
- AFL_DEBUG=1 ../afl-fuzz -i in -o out -V05 -X -- ./nyx-test >>errors 2>&1
- } >>errors 2>&1
- test -n "$( ls out/default/queue/id:000002* 2>/dev/null )" && {
- $ECHO "$GREEN[+] afl-fuzz is working correctly with nyx_mode"
- RUNTIME=`grep execs_done out/default/fuzzer_stats | awk '{print$3}'`
- rm -rf errors nyx-test test-instr in out
- } || {
- echo CUT------------------------------------------------------------------CUT
- cat errors
- echo CUT------------------------------------------------------------------CUT
- $ECHO "$RED[!] afl-fuzz is not working correctly with nyx_mode"
- CODE=1
- }
- } || {
- $ECHO "$RED[!] nyx_packer failed, likely install requirements not met."
- CODE=1
- }
- } || {
- $ECHO "$RED[!] nyx_packer failed, likely install requirements not met."
- CODE=1
- }
- #rm -rf test-instr in out errors nyx-test
- }
- } || {
- echo CUT------------------------------------------------------------------CUT
- cat errors
- echo CUT------------------------------------------------------------------CUT
- $ECHO "$RED[!] afl-cc compilation of test targets failed - what is going on??"
- CODE=1
- }
- } || {
- $ECHO "$YELLOW[-] nyx_mode is not compiled, cannot test"
- INCOMPLETE=1
- }
-
-}
+$ECHO "$GREY[-] nyx_mode cannot be tested inside the sandbox"
. ./test-post.sh
@@ -0,0 +1,156 @@
{
stdenv,
lib,
callPackage,
fetchFromGitHub,
cpio,
gzip,
glibc,
pkgsi686Linux,
pax-utils,
makeWrapper,
python3,
aflplusplus,
qemu-nyx,
}:
# this derivation assumes x86_64-linux
assert stdenv.targetPlatform.system == "x86_64-linux";
let
python3WithPkgs = python3.withPackages (ps: [
ps.msgpack
ps.jinja2
]);
in
stdenv.mkDerivation {
version = builtins.readFile (aflplusplus.src + "/nyx_mode/PACKER_VERSION");
pname = "nyx-packer";
src = aflplusplus.src;
postUnpack = ''
sourceRoot="$sourceRoot/nyx_mode/packer"
'';
patches = [
# this patch does following things:
# * write default config to ~/.nyx/nyx.ini because nix store is read-only
# * fix https://github.com/nyx-fuzz/packer/issues/35
# * apply Matt Morehouse's patch (https://github.com/nyx-fuzz/packer/pull/34)
./packer.patch
];
strictDeps = true;
# compile_64.sh / compile_loader use -O0; _FORTIFY_SOURCE needs optimization
hardeningDisable = [ "fortify" ];
nativeBuildInputs = [
cpio
gzip
makeWrapper
];
buildInputs = [
glibc
glibc.static
pkgsi686Linux.glibc
];
dontConfigure = true;
postPatch = ''
substituteInPlace packer/common/config.py \
--replace-fail \
'"QEMU-PT_PATH": "../../QEMU-Nyx/x86_64-softmmu/qemu-system-x86_64"' \
'"QEMU-PT_PATH": "${qemu-nyx}/bin/qemu-system-x86_64"'
# fix paths to shared libraries that assume debian FHS
substituteInPlace linux_initramfs/pack.sh \
--replace-fail \
'cp -L /lib/ld-linux.so.2' \
'cp -L ${pkgsi686Linux.glibc}/lib/ld-linux.so.2' \
--replace-fail \
'cp -L /lib/x86_64-linux-gnu/libdl.so.2' \
'cp -L ${glibc}/lib/libdl.so.2' \
--replace-fail \
'cp -L /lib/x86_64-linux-gnu/libc.so.6' \
'cp -L ${glibc}/lib/libc.so.6' \
--replace-fail \
'cp -L /lib64/ld-linux-x86-64.so.2' \
'cp -L ${glibc}/lib/ld-linux-x86-64.so.2' \
--replace-fail \
'cp -L /lib64/libdl.so.2' \
'cp -L ${glibc}/lib/libdl.so.2' \
--replace-fail \
'cp -L /lib64/libc.so.6' \
'cp -L ${glibc}/lib/libc.so.6' \
--replace-fail \
'cp -L /lib32/libc.so.6' \
'cp -L ${pkgsi686Linux.glibc}/lib/libc.so.6' \
--replace-fail \
'cp -L /lib32/libdl.so.2' \
'cp -L ${pkgsi686Linux.glibc}/lib/libdl.so.2' \
--replace-fail \
'cp /lib/x86_64-linux-gnu/libnss_compat.so.2' \
'cp -L ${glibc}/lib/libnss_compat.so.2' \
--replace-fail \
'cp /lib64/libnss_compat.so.2' \
'cp ${glibc}/lib/libnss_compat.so.2'
'';
buildPhase = ''
runHook preBuild
pushd packer/linux_x86_64-userspace
echo "+ bash -e compile_64.sh"
bash -e compile_64.sh
popd
pushd linux_initramfs
echo "+ bash -e pack.sh"
bash -e pack.sh
popd
runHook postBuild
'';
installPhase = ''
runHook preInstall
# copy build tree so python scripts can assume regular relative paths
cp -r . "$out" && chmod -R u+w "$out"
PATH="${python3WithPkgs}/bin:$PATH" patchShebangs --build $out/packer
wrapProgram $out/packer/nyx_packer.py \
--suffix PATH : ${
lib.makeBinPath [
pax-utils
qemu-nyx
]
}
wrapProgram $out/packer/nyx_config_gen.py \
--suffix PATH : ${
lib.makeBinPath [
pax-utils
qemu-nyx
]
}
runHook postInstall
'';
postFixup = ''
# packer binaries are meant to run inside the vm
find "$out/packer/linux_x86_64-userspace/bin64" -type f \
-exec patchelf --set-interpreter /lib64/ld-linux-x86-64.so.2 {} \;
'';
meta = {
homepage = "https://github.com/nyx-fuzz/packer";
description = "image packer for Nyx VMs";
license = lib.licenses.gpl2Only;
platforms = lib.platforms.x86_64;
maintainers = with lib.maintainers; [ ekzyis ];
};
}
@@ -0,0 +1,103 @@
diff --git a/linux_initramfs/pack.sh b/linux_initramfs/pack.sh
index e042bb5..f04c0a1 100644
--- a/linux_initramfs/pack.sh
+++ b/linux_initramfs/pack.sh
@@ -40,15 +40,17 @@ mkdir rootTemplate/lib/i386-linux-gnu/
mkdir rootTemplate/lib/x86_64-linux-gnu/
cp -L /lib/ld-linux.so.2 rootTemplate/lib/ld-linux.so.2
-cp -L /lib64/ld-linux-x86-64.so.2 rootTemplate/lib64/ld-linux-x86-64.so.2
cp -L /lib/x86_64-linux-gnu/libdl.so.2 rootTemplate/lib/x86_64-linux-gnu/libdl.so.2
cp -L /lib/x86_64-linux-gnu/libc.so.6 rootTemplate//lib/x86_64-linux-gnu/libc.so.6
+cp -L /lib64/ld-linux-x86-64.so.2 rootTemplate/lib64/ld-linux-x86-64.so.2
+cp -L /lib64/libdl.so.2 rootTemplate/lib64/libdl.so.2
+cp -L /lib64/libc.so.6 rootTemplate/lib64/libc.so.6
cp -L /lib32/libc.so.6 rootTemplate//lib32/libc.so.6
-cp -L /lib/ld-linux.so.2 rootTemplate/lib/ld-linux.so.2
cp -L /lib32/libdl.so.2 rootTemplate/lib32/libdl.so.2
# fix nasty nss bugs (getpwnam_r, ...)
cp /lib/x86_64-linux-gnu/libnss_compat.so.2 rootTemplate//lib/x86_64-linux-gnu/
+cp /lib64/libnss_compat.so.2 rootTemplate/lib64/
cp -r "rootTemplate" "init"
sed '/START/c\./loader' init/init_template > init/init
diff --git a/linux_initramfs/rootTemplate/init_template b/linux_initramfs/rootTemplate/init_template
index 0d29602..f555082 100755
--- a/linux_initramfs/rootTemplate/init_template
+++ b/linux_initramfs/rootTemplate/init_template
@@ -19,7 +19,7 @@ adduser --gecos "ubuntu" --disabled-password --ingroup ubuntu ubuntu
echo "ubuntu:ubuntu" | chpasswd
# multiarch support
-export LD_LIBRARY_PATH=/lib32
+export LD_LIBRARY_PATH=/lib64:/lib32
START
#./loader
diff --git a/packer/common/config.py b/packer/common/config.py
index 74f2b2e..e0fcf3a 100644
--- a/packer/common/config.py
+++ b/packer/common/config.py
@@ -39,7 +39,7 @@ default_config = {
"QEMU-PT_PATH": "../../QEMU-Nyx/x86_64-softmmu/qemu-system-x86_64",
"KERNEL": "../linux_initramfs/bzImage-linux-4.15-rc7",
"INIT_RAMFS": "../linux_initramfs/init.cpio.gz",
- "DEFAULT_FUZZER_CONFIG_FOLDER": "./fuzzer_configs/",
+ "DEFAULT_FUZZER_CONFIG_FOLDER": os.path.expanduser("~/.nyx/fuzzer_configs/"),
"DEFAULT_VM_HDA": "",
"DEFAULT_VM_PRESNAPSHOT": "",
}
@@ -149,6 +149,9 @@ class ConfigReader(object):
def __init_config(self, config_file):
if not os.path.exists(config_file):
print("Configuration \"%s\" not found -> Creating..."%(os.path.realpath(config_file)))
+ config_dir = os.path.dirname(os.path.abspath(config_file))
+ os.makedirs(config_dir, exist_ok=True)
+ os.makedirs(config_dir+"/fuzzer_configs/", exist_ok=True)
f = open(config_file, "w")
config = configparser.ConfigParser()
config["Packer"] = {}
@@ -261,7 +264,7 @@ class PackerConfiguration:
self.load_old_state = False
def __load_config(self):
- self.config_values = ConfigReader(os.path.dirname(os.path.realpath(__file__))+"/../nyx.ini", self.__config_section, self.__config_default).get_values()
+ self.config_values = ConfigReader(os.path.expanduser("~/.nyx/nyx.ini"), self.__config_section, self.__config_default).get_values()
def __load_arguments(self):
modes = ["afl", "spec"]
@@ -344,7 +347,7 @@ class ConfigGeneratorConfiguration:
self.load_old_state = False
def __load_config(self):
- self.config_values = ConfigReader(os.path.dirname(os.path.realpath(__file__)) +"/../nyx.ini", self.__config_section, self.__config_default).get_values()
+ self.config_values = ConfigReader(os.path.expanduser("~/.nyx/nyx.ini"), self.__config_section, self.__config_default).get_values()
def __load_arguments(self):
diff --git a/packer/common/self_check.py b/packer/common/self_check.py
index 10f2405..e16ac37 100644
--- a/packer/common/self_check.py
+++ b/packer/common/self_check.py
@@ -170,12 +170,14 @@ def check_apple_ignore_msrs(config):
def check_nyx_ini():
- if not os.path.exists(os.path.dirname(sys.argv[0])+"/nyx.ini") and not os.path.exists("nyx.ini"):
- from common.config import FuzzerConfiguration
- FuzzerConfiguration(skip_args=True).create_initial_config()
- print(WARNING + WARNING_PREFIX + "nyx.ini file does not exist. Creating..." + ENDC)
- return False
- return True
+ # comment this code out because FuzzerConfiguration does not exist
+ # see https://github.com/nyx-fuzz/packer/issues/35
+ # if not os.path.exists(os.path.dirname(sys.argv[0])+"/nyx.ini") and not os.path.exists("nyx.ini"):
+ # from common.config import FuzzerConfiguration
+ # FuzzerConfiguration(skip_args=True).create_initial_config()
+ # print(WARNING + WARNING_PREFIX + "nyx.ini file does not exist. Creating..." + ENDC)
+ # return False
+ return os.path.exists(os.path.expanduser("~/.nyx/nyx.ini"))
def check_qemu_version(config):