diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 2b95630abd..c7b94ba32e 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -364,7 +364,7 @@ jobs: shell: bash run: RUN_ON_CI=1 LLVM_CONFIG=llvm-config-${{env.MAIN_LLVM_VERSION}} ./scripts/test_fuzzer.sh ${{ matrix.fuzzer }} - fuzzers-qemu: + fuzzers-qemu-user: needs: - changes if: ${{ needs.changes.outputs.qemu == 'true' }} @@ -376,7 +376,31 @@ jobs: - ./fuzzers/binary_only/qemu_cmin - ./fuzzers/binary_only/qemu_coverage - ./fuzzers/binary_only/qemu_launcher + arch: + - aarch64 + - arm + - i386 + - ppc + - x86_64 + runs-on: [ self-hosted, qemu ] + container: registry.gitlab.com/qemu-project/qemu/qemu/ubuntu2204:latest + steps: + - uses: actions/checkout@v4 + - uses: ./.github/workflows/qemu-fuzzer-tester-prepare + - name: Build and run example QEMU fuzzers (Linux) + if: runner.os == 'Linux' + shell: bash + run: ARCH=${{ matrix.arch }} RUN_ON_CI=1 LLVM_CONFIG=llvm-config-${{env.MAIN_LLVM_VERSION}} ./scripts/test_fuzzer.sh ${{ matrix.fuzzer }} + + fuzzers-qemu-system: + needs: + - changes + if: ${{ needs.changes.outputs.qemu == 'true' }} + strategy: + matrix: + os: [ubuntu-24.04] + fuzzer: # Full-system - ./fuzzers/full_system/qemu_baremetal - ./fuzzers/full_system/qemu_linux_kernel diff --git a/Dockerfile b/Dockerfile index 0e4e80bc16..b467e1a0ca 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,17 +1,21 @@ # syntax=docker/dockerfile:1.2 -FROM rust:1.76.0 AS libafl +FROM rust:1.85.0 AS libafl LABEL "maintainer"="afl++ team " LABEL "about"="LibAFL Docker image" -# Install cargo-binstall to download the sccache build +# Install cargo-binstall RUN curl -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh | bash -# install sccache to cache subsequent builds of dependencies -RUN cargo binstall --no-confirm sccache + +# We now use just to build things rather than cargo-make +RUN cargo binstall --no-confirm just +# Nexttest allows us to run tests which panic in an environment where we can't unwind +RUN cargo binstall --no-confirm cargo-nextest +# Cargo fuzz is useful for fuzz testing our implementations +RUN cargo binstall -y cargo-fuzz +# Taplo allows us to format toml files +RUN cargo binstall -y taplo-cli ENV HOME=/root -ENV SCCACHE_CACHE_SIZE="1G" -ENV SCCACHE_DIR=$HOME/.cache/sccache -ENV RUSTC_WRAPPER="/usr/local/cargo/bin/sccache" ENV IS_DOCKER="1" RUN sh -c 'echo set encoding=utf-8 > /root/.vimrc' \ echo "export PS1='"'[LibAFL \h] \w$(__git_ps1) \$ '"'" >> ~/.bashrc && \ @@ -21,14 +25,71 @@ RUN sh -c 'echo set encoding=utf-8 > /root/.vimrc' \ RUN rustup default nightly RUN rustup component add rustfmt clippy +RUN rustup target add armv7-unknown-linux-gnueabi +RUN rustup target add aarch64-unknown-linux-gnu +RUN rustup target add i686-unknown-linux-gnu +RUN rustup target add powerpc-unknown-linux-gnu + # Install clang 18, common build tools ENV LLVM_VERSION=18 -RUN apt update && apt install -y build-essential gdb git wget python3-venv ninja-build lsb-release software-properties-common gnupg cmake +RUN dpkg --add-architecture i386 +RUN apt-get update && \ + apt-get install -y \ + build-essential \ + cmake \ + curl \ + g++-aarch64-linux-gnu \ + g++-arm-linux-gnueabi \ + g++-i686-linux-gnu \ + g++-mipsel-linux-gnu \ + g++-powerpc-linux-gnu \ + gcc-aarch64-linux-gnu \ + gcc-arm-linux-gnueabi \ + gcc-i686-linux-gnu \ + gcc-mipsel-linux-gnu \ + gcc-powerpc-linux-gnu \ + gdb \ + gdb-multiarch \ + git \ + gnupg \ + libc6-dev:i386 \ + libclang-dev \ + libgcc-12-dev:i386 \ + libglib2.0-dev \ + lsb-release \ + ninja-build \ + python3 \ + python3-pip \ + python3-venv \ + software-properties-common \ + wget RUN set -ex &&\ wget https://apt.llvm.org/llvm.sh &&\ chmod +x llvm.sh &&\ ./llvm.sh ${LLVM_VERSION} +RUN git config --global core.pager cat + +# Install a modern version of QEMU + +WORKDIR /root +ENV QEMU_VER=9.2.1 +RUN wget https://download.qemu.org/qemu-${QEMU_VER}.tar.xz +RUN tar xvJf qemu-${QEMU_VER}.tar.xz +WORKDIR /root/qemu-${QEMU_VER} +RUN ./configure --target-list="\ + arm-linux-user,\ + aarch64-linux-user,\ + i386-linux-user,\ + ppc-linux-user,\ + mips-linux-user,\ + arm-softmmu,\ + aarch64-softmmu,\ + i386-softmmu,\ + ppc-softmmu,\ + mips-softmmu" +RUN make -j +RUN make install # Copy a dummy.rs and Cargo.toml first, so that dependencies are cached WORKDIR /libafl diff --git a/just/envs/.env.i386 b/just/envs/.env.i386 index 0f06b4bce9..8b4afd471e 100644 --- a/just/envs/.env.i386 +++ b/just/envs/.env.i386 @@ -1,3 +1,3 @@ -CROSS_CC="x86_64-linux-gnu-gcc" -CROSS_CXX="x86_64-linux-gnu-g++" -CROSS_CFLAGS="-m32" \ No newline at end of file +CROSS_CC="i686-linux-gnu-gcc" +CROSS_CXX="i686-linux-gnu-g++" +CROSS_CFLAGS="-m32" diff --git a/just/envs/.env.ppc b/just/envs/.env.ppc index b6c4ea03fd..f6fa2090b8 100644 --- a/just/envs/.env.ppc +++ b/just/envs/.env.ppc @@ -1,3 +1,3 @@ CROSS_CC="powerpc-linux-gnu-gcc" -CROSS_CXX="powerpc-linux-gnu-gcc" -CROSS_CFLAGS="" \ No newline at end of file +CROSS_CXX="powerpc-linux-gnu-g++" +CROSS_CFLAGS="" diff --git a/just/libafl-cc-libpng.just b/just/libafl-cc-libpng.just index da253ef241..1e8bc8f553 100644 --- a/just/libafl-cc-libpng.just +++ b/just/libafl-cc-libpng.just @@ -1,6 +1,7 @@ import "libafl-cc.just" -OPTIMIZATIONS := env("OPTIMIZATIONS", "yes") +ARCH := env("ARCH", "x86_64") +OPTIMIZATIONS := env("OPTIMIZATIONS", if ARCH == "ppc" { "no" } else { "yes" }) LIBPNG_ROOT := DEPS_DIR / "libpng-1.6.37" LIBPNG_BUILD := TARGET_DIR / "build-png" diff --git a/just/libafl-qemu-libpng.just b/just/libafl-qemu-libpng.just index e68a2cca23..644718c590 100644 --- a/just/libafl-qemu-libpng.just +++ b/just/libafl-qemu-libpng.just @@ -3,7 +3,7 @@ import "libafl-qemu.just" # Useful rules to build libpng for multiple architecture. ARCH := env("ARCH", "x86_64") -OPTIMIZATIONS := env("OPTIMIZATIONS", "yes") +OPTIMIZATIONS := env("OPTIMIZATIONS", if ARCH == "ppc" { "no" } else { "yes" }) DEPS_DIR := TARGET_DIR / "deps" @@ -81,4 +81,4 @@ libpng: arch_dir zlib libpng_wget --enable-hardware-optimizations={{ OPTIMIZATIONS }} \ --host={{ ARCH }} - make -j -C {{ TARGET_DIR }}/build-png/ \ No newline at end of file + make -j -C {{ TARGET_DIR }}/build-png/ diff --git a/libafl_qemu/src/arch/aarch64.rs b/libafl_qemu/src/arch/aarch64.rs index b3cb844882..4017a79c26 100644 --- a/libafl_qemu/src/arch/aarch64.rs +++ b/libafl_qemu/src/arch/aarch64.rs @@ -117,7 +117,7 @@ impl crate::ArchExtras for crate::CPU { 7 => self.read_reg(Regs::X7), _ => { const SIZE: usize = size_of::(); - let stack_ptr: GuestAddr = self.read_reg(Regs::Rsp)?; + let stack_ptr: GuestAddr = self.read_reg(Regs::Sp)?; /* * Stack is full and descending. SP points to return address, arguments * are in reverse order above that. 8th argument is at SP + 8. diff --git a/libafl_qemu/src/arch/i386.rs b/libafl_qemu/src/arch/i386.rs index 51af79884b..bb85184096 100644 --- a/libafl_qemu/src/arch/i386.rs +++ b/libafl_qemu/src/arch/i386.rs @@ -67,7 +67,7 @@ impl crate::ArchExtras for crate::CPU { fn read_return_address(&self) -> Result { let stack_ptr: GuestReg = self.read_reg(Regs::Esp)?; let mut ret_addr = [0; size_of::()]; - unsafe { self.read_mem(stack_ptr, &mut ret_addr) }; + self.read_mem(stack_ptr, &mut ret_addr)?; Ok(GuestReg::from_le_bytes(ret_addr).into()) } @@ -78,7 +78,7 @@ impl crate::ArchExtras for crate::CPU { let stack_ptr: GuestReg = self.read_reg(Regs::Esp)?; let val: GuestReg = val.into(); let ret_addr = val.to_le_bytes(); - unsafe { self.write_mem(stack_ptr, &ret_addr) }; + self.write_mem(stack_ptr, &ret_addr)?; Ok(()) } @@ -89,23 +89,17 @@ impl crate::ArchExtras for crate::CPU { ) -> Result { QemuRWError::check_conv(QemuRWErrorKind::Read, CallingConvention::Cdecl, conv)?; - match idx { - _ => { - const SIZE: usize = size_of::(); - let stack_ptr: GuestAddr = self.read_reg(Regs::Sp)?; - /* - * Stack is full and descending. SP points to return address, arguments - * are in reverse order above that. - */ + const SIZE: usize = size_of::(); + let stack_ptr: GuestAddr = self.read_reg(Regs::Sp)?; + /* + * Stack is full and descending. SP points to return address, arguments + * are in reverse order above that. + */ - let offset = (SIZE as GuestAddr) * (GuestAddr::from(idx) + 1); - let mut val = [0u8; SIZE]; - unsafe { - self.read_mem(stack_ptr + offset, &mut val); - } - Ok(GuestReg::from_le_bytes(val).into()) - } - } + let offset = (SIZE as GuestAddr) * (GuestAddr::from(idx) + 1); + let mut val = [0u8; SIZE]; + self.read_mem(stack_ptr + offset, &mut val)?; + Ok(GuestReg::from_le_bytes(val).into()) } fn write_function_argument_with_cc( @@ -119,24 +113,17 @@ impl crate::ArchExtras for crate::CPU { { QemuRWError::check_conv(QemuRWErrorKind::Write, CallingConvention::Cdecl, conv)?; - match idx { - _ => { - let val: GuestReg = val.into(); - let stack_ptr: GuestAddr = self.read_reg(Regs::Sp)?; - /* - * Stack is full and descending. SP points to return address, arguments - * are in reverse order above that. - */ - let size: GuestAddr = size_of::() as GuestAddr; - let offset = size * (GuestAddr::from(idx) + 1); + let val: GuestReg = val.into(); + let stack_ptr: GuestAddr = self.read_reg(Regs::Sp)?; + /* + * Stack is full and descending. SP points to return address, arguments + * are in reverse order above that. + */ + let size: GuestAddr = size_of::() as GuestAddr; + let offset = size * (GuestAddr::from(idx) + 1); - let arg = val.to_le_bytes(); - unsafe { - self.write_mem(stack_ptr + offset, &arg); - } - Ok(()) - } - r => Err(QemuRWError::new_argument_error(QemuRWErrorKind::Write, r)), - } + let arg = val.to_le_bytes(); + self.write_mem(stack_ptr + offset, &arg)?; + Ok(()) } } diff --git a/utils/gdb_qemu/Justfile b/utils/gdb_qemu/Justfile index e9d722330a..f5a29659ca 100644 --- a/utils/gdb_qemu/Justfile +++ b/utils/gdb_qemu/Justfile @@ -3,8 +3,8 @@ import "../../just/libafl.just" DEMO_TARGET := "powerpc-unknown-linux-gnu" HOST_TARGET := "x86_64-unknown-linux-gnu" -DEMO_DIR := {{TARGET_DIR}}/{{DEMO_TARGET}}/"debug" -TARGET_DIR := {{TARGET_DIR}}/{{HOST_TARGET}}/"debug" +DEMO_DIR := TARGET_DIR/DEMO_TARGET/PROFILE_DIR +HOST_DIR := TARGET_DIR/HOST_TARGET/PROFILE_DIR FUZZER_NAME := "" clean: @@ -17,15 +17,15 @@ demo: format cargo build -p gdb_demo --profile {{PROFILE}} --target powerpc-unknown-linux-gnu run_demo: demo - cargo run -p gdb_demo --target powerpc-unknown-linux-gnu + cargo run -p gdb_demo --profile {{PROFILE}} --target powerpc-unknown-linux-gnu build: format cargo build -p gdb_qemu --profile {{PROFILE}} run: demo - cargo run -p gdb_qemu --profile {{PROFILE}} -- -p 1234 -L trace -- qemu-ppc -L /usr/powerpc-linux-gnu -g 1234 {{DEMO_DIR}}/gdb_demo + cargo run -p gdb_qemu --profile {{PROFILE}} -- -p 1234 -L trace qemu-ppc -- -L /usr/powerpc-linux-gnu -g 1234 {{DEMO_DIR}}/gdb_demo gdb: - gdb-multiarch -ex "set architecture powerpc:MPC8XX" -ex "set pagination off" -ex "set confirm off" -ex "file {{DEMO_DIR}}/gdb_demo" -ex "target remote | {{TARGET_DIR}}/gdb_qemu -p 1234 -L trace qemu-ppc -- -L /usr/powerpc-linux-gnu -g 1234 {{DEMO_DIR}}/gdb_demo" + gdb-multiarch -ex "set architecture powerpc:MPC8XX" -ex "set pagination off" -ex "set confirm off" -ex "file {{DEMO_DIR}}/gdb_demo" -ex "target remote | {{HOST_DIR}}/gdb_qemu -p 1234 -L trace qemu-ppc -- -L /usr/powerpc-linux-gnu -g 1234 {{DEMO_DIR}}/gdb_demo" all: demo build