diff --git a/Makefile b/Makefile index f49d789b..5c5fe521 100644 --- a/Makefile +++ b/Makefile @@ -6,6 +6,8 @@ DOCKER_PREFIX := ${LIB_NAME} DOCKER_TAG := ${DOCKER_PREFIX}-yocto-builder DOCKER_SUSE_TAG := ${DOCKER_PREFIX}-open-suse-env DOCKER_PYTHON_TAG := ${DOCKER_PREFIX}-analyzer +DOCKER_YOCTO_VM_TAG := ${DOCKER_PREFIX}-yocto-vm +DOCKER_SUSE_VM_TAG := ${DOCKER_PREFIX}-suse-vm DOCKER_BUILD_VOLUME := ${DOCKER_PREFIX}-yocto-build DOCKER_DOWNLOADS_VOLUME := ${DOCKER_PREFIX}-yocto-downloads DOCKER_SSTATE_VOLUME := ${DOCKER_PREFIX}-yocto-sstate diff --git a/conf/packages.conf b/conf/packages.conf index 81914ef9..55935da5 100644 --- a/conf/packages.conf +++ b/conf/packages.conf @@ -59,6 +59,7 @@ IMAGE_INSTALL:append = " \ jq \ json-c \ kbd \ + kernel-devsrc \ kmod \ ldconfig \ less \ @@ -92,6 +93,7 @@ IMAGE_INSTALL:append = " \ libusb1 \ libxcrypt \ libxml2 \ + linux-libc-headers \ libyaml \ logrotate \ lshw \ @@ -140,6 +142,7 @@ IMAGE_INSTALL:append = " \ python3-setuptools \ readline \ rpm \ + ruby \ rsync \ rsyslog \ screen \ diff --git a/docker/compose.yml b/docker/compose.yml index a7f1a374..9f5e06f8 100644 --- a/docker/compose.yml +++ b/docker/compose.yml @@ -23,7 +23,34 @@ services: BUILD_DIR: ${BUILD_DIR} POKY_DIR: ${POKY_DIR} OS_IMAGE: ${OS_IMAGE} - VM_RAM: ${QEMU_VM_RAM} + USER: ${USER} + GROUP: ${GROUP} + + imgtests-yocto-vm: + build: + dockerfile: docker/vm-yocto.dockerfile + args: + USER: ${USER} + GROUP: ${GROUP} + PASSWORD: ${PASSWORD} + POKY_DIR: ${POKY_DIR} + image: ${DOCKER_YOCTO_VM_TAG} + privileged: true + devices: + - /dev/kvm + volumes: + - ${DOCKER_BUILD_VOLUME}:${BUILD_DIR} + - ${DOCKER_DOWNLOADS_VOLUME}:${POKY_DIR}/downloads + - ${DOCKER_SSTATE_VOLUME}:${POKY_DIR}/sstate-cache + - ${HOST_CONF_PATH}/local.conf:${BUILD_DIR}/conf/local.conf + - ${HOST_CONF_PATH}/packages.conf:${BUILD_DIR}/conf/packages.conf + - ${HOST_CONF_PATH}/added_packages.conf:${BUILD_DIR}/conf/added_packages.conf + - ${HOST_LAYERS_PATH}/meta-image-tests:${POKY_DIR}/meta-image-tests + - ${HOST_SCRIPTS_PATH}/add-layers.sh:${POKY_DIR}/add-layers.sh + environment: + BUILD_DIR: ${BUILD_DIR} + POKY_DIR: ${POKY_DIR} + OS_IMAGE: ${OS_IMAGE} CPUS: ${CPUS} USER: ${USER} GROUP: ${GROUP} @@ -37,6 +64,9 @@ services: networks: yocto-network: ipv4_address: ${YOCTO_ADDRESS} + depends_on: + imgtests-yocto: + condition: service_completed_successfully imgtests-suse-156: build: @@ -51,13 +81,28 @@ services: environment: S_USER: ${S_USER} PASSWORD: ${PASSWORD} - VM_RAM: ${QEMU_VM_RAM} - CPUS: ${CPUS} + networks: + - yocto-network + + imgtests-suse-vm: + build: + dockerfile: docker/vm-suse.dockerfile + args: + USER: ${USER} + image: ${DOCKER_SUSE_VM_TAG} + privileged: true + devices: + - /dev/kvm + volumes: + - ${DOCKER_OPENSUSE_VOLUME}:${SUSE_DIR} + environment: SSH_QEMU_PORT: ${SSH_TO_QEMU_PORT} SSH_SUSE_PORT: ${SSH_SUSE_PORT_156} SUSE_NE_PORT: ${SUSE_156_NE_PORT} DEFAULT_NE_PORT: ${DEFAULT_NE_PORT} IPERF3_PORT: ${IPERF3_PORT} + VM_RAM: ${QEMU_VM_RAM} + CPUS: ${CPUS} ports: - ${SSH_SUSE_PORT_156}:${SSH_SUSE_PORT_156} networks: diff --git a/docker/image_builder.dockerfile b/docker/image_builder.dockerfile index e4df2346..dee15e62 100644 --- a/docker/image_builder.dockerfile +++ b/docker/image_builder.dockerfile @@ -30,12 +30,14 @@ RUN groupadd -g 510 ${GROUP} && \ echo "${USER}:${PASSWORD}" | chpasswd USER ${USER} + RUN mkdir --parents ${POKY_DIR} && \ git clone --depth 1 -b walnascar --recurse-submodules https://git.yoctoproject.org/poky ${POKY_DIR} + WORKDIR ${POKY_DIR} COPY --chown=${USER}:${GROUP} layers ${POKY_DIR} -COPY --chown=${USER}:${GROUP} scripts/entrypoint.sh ${POKY_DIR}/ -COPY --chown=${USER}:${GROUP} scripts/cmd_yocto.sh ${POKY_DIR}/ +COPY --chown=${USER}:${GROUP} scripts/entrypoint.sh ${POKY_DIR} +COPY --chown=${USER}:${GROUP} scripts/cmd_yocto.sh ${POKY_DIR} ENTRYPOINT ["/home/user/poky/entrypoint.sh"] diff --git a/docker/open-suse.dockerfile b/docker/open-suse.dockerfile index 189d9519..ac93fdac 100644 --- a/docker/open-suse.dockerfile +++ b/docker/open-suse.dockerfile @@ -7,9 +7,6 @@ ENV DEBIAN_FRONTEND=noninteractive \ RUN apt-get update && \ apt-get install -y --no-install-recommends \ - qemu-system \ - qemu-utils \ - qemu-user-static \ cloud-init \ cloud-image-utils \ wget \ diff --git a/docker/vm-suse.dockerfile b/docker/vm-suse.dockerfile new file mode 100644 index 00000000..d6843278 --- /dev/null +++ b/docker/vm-suse.dockerfile @@ -0,0 +1,19 @@ +FROM ubuntu:24.04 + +ARG USER + +ENV DEBIAN_FRONTEND=noninteractive \ + SUSE_DIR=/home/${USER}/suse + +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + qemu-system \ + qemu-utils \ + qemu-user-static + +WORKDIR ${SUSE_DIR} + +COPY scripts/opensuse/start-vm.sh /start-vm.sh +RUN chmod +x /start-vm.sh + +ENTRYPOINT ["/start-vm.sh"] diff --git a/docker/vm-yocto.dockerfile b/docker/vm-yocto.dockerfile new file mode 100644 index 00000000..5fd0be70 --- /dev/null +++ b/docker/vm-yocto.dockerfile @@ -0,0 +1,38 @@ +FROM ubuntu:22.04 + +ARG USER +ARG GROUP +ARG PASSWORD +ARG POKY_DIR + +ENV DEBIAN_FRONTEND=noninteractive \ + LANG=en_US.UTF-8 + +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + sudo wget git locales chrpath cpio diffstat gawk \ + zstd liblz4-tool python3 python3-pip tar gcc make \ + bzip2 file g++ patch python3-pexpect python3-git \ + python3-jinja2 python3-subunit screen \ + libtirpc-dev libtirpc3 pkg-config \ + qemu qemu-system-x86 qemu-utils \ + openssh-server && \ + rm -rf /var/lib/apt/lists/* && \ + locale-gen en_US.UTF-8 + +RUN groupadd -g 510 ${GROUP} && \ + groupadd -g 993 kvm && \ + useradd -rm -d /home/${USER} -s /bin/bash -g ${GROUP} -u 1010 -G sudo,kvm ${USER} && \ + echo "${USER} ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers.d/${USER} && \ + echo "${USER}:${PASSWORD}" | chpasswd + +USER ${USER} + +RUN git clone --depth 1 -b walnascar --recurse-submodules https://git.yoctoproject.org/poky ${POKY_DIR} + +WORKDIR ${POKY_DIR} + +COPY --chown=${USER}:${GROUP} layers ${POKY_DIR} +COPY --chown=${USER}:${GROUP} scripts/start-vm-yocto.sh ${POKY_DIR} + +ENTRYPOINT ["/home/user/poky/start-vm-yocto.sh"] diff --git a/layers/meta-image-tests/recipes-kernel/linux/linux-yocto/fault-injection.cfg b/layers/meta-image-tests/recipes-kernel/linux/linux-yocto/fault-injection.cfg index 2c2602a3..722145fd 100644 --- a/layers/meta-image-tests/recipes-kernel/linux/linux-yocto/fault-injection.cfg +++ b/layers/meta-image-tests/recipes-kernel/linux/linux-yocto/fault-injection.cfg @@ -5,3 +5,4 @@ CONFIG_FAIL_PAGE_ALLOC=y CONFIG_FAIL_MAKE_REQUEST=y CONFIG_FAIL_IO_TIMEOUT=y CONFIG_FAIL_FUTEX=y +CONFIG_BPF_KPROBE_OVERRIDE=y diff --git a/scripts/cmd_yocto.sh b/scripts/cmd_yocto.sh index 970e2dff..767e454c 100755 --- a/scripts/cmd_yocto.sh +++ b/scripts/cmd_yocto.sh @@ -5,7 +5,3 @@ set -e cd "${POKY_DIR}" ./add-layers.sh bitbake "${OS_IMAGE}" -ROOTFS_IMAGE="${POKY_DIR}/build/tmp/deploy/images/qemux86-64/${OS_IMAGE}-qemux86-64.rootfs.ext4.qcow2" - -QB_SLIRP_OPT="-netdev user,id=net0,hostfwd=tcp::8080-:80,hostfwd=tcp::${SSH_QEMU_PORT}-:${SSH_TO_QEMU_PORT},hostfwd=tcp::${YOCTO_NE_PORT}-:${DEFAULT_NE_PORT},hostfwd=tcp:0.0.0.0:${IPERF3_PORT}-:${IPERF3_PORT},hostfwd=udp:0.0.0.0:${IPERF3_PORT}-:${IPERF3_PORT}" \ - runqemu qemux86-64 slirp nographic "${ROOTFS_IMAGE}" qemuparams="-monitor tcp:0.0.0.0:4444,server,nowait -smp ${CPUS}" diff --git a/scripts/fault-injection/chaosblade.sh b/scripts/fault-injection/chaosblade.sh new file mode 100644 index 00000000..02beca02 --- /dev/null +++ b/scripts/fault-injection/chaosblade.sh @@ -0,0 +1,85 @@ +#!/bin/bash + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +source "${SCRIPT_DIR}/common.sh" + +CHAOSBLADE_VERSION="${CHAOSBLADE_VERSION:-1.8.0}" +CPU_PERCENT="${CHAOSBLADE_CPU_PERCENT:-20}" +TIMEOUT="${CHAOSBLADE_TIMEOUT_SEC:-30}" +NET_DELAY_MS="${CHAOSBLADE_NETWORK_DELAY_MS:-}" +INTERFACE="${CHAOSBLADE_INTERFACE:-eth0}" + +IDS=() + +cleanup() { + local id + for id in "${IDS[@]}"; do + run "$id" <<<"sudo /opt/chaosblade/blade destroy $id" || warn "Failed to destroy $id" + done +} +trap cleanup EXIT + +install_chao() { + has blade && { info "chaosblade already installed on ${TARGET}"; return; } + [[ "$TARGET" == yocto ]] && die "not installed on ${TARGET}; add the chaosblade to the poky image" + info "Installing chaosblade v${CHAOSBLADE_VERSION} on ${TARGET}..." + run "$CHAOSBLADE_VERSION" <<'EOF' || die "Failed to install chaosblade" +set -euo pipefail +v=$1 +d=$(mktemp -d) +trap 'rm -rf "$d"' EXIT +cd "$d" +sudo zypper -n install --no-recommends wget tar +wget -q "https://github.com/chaosblade-io/chaosblade/releases/download/v${v}/chaosblade-${v}-linux_amd64.tar.gz" +tar -xzf "chaosblade-${v}-linux_amd64.tar.gz" +mkdir -p /opt/chaosblade +cp -r "chaosblade-${v}-linux_amd64"/. /opt/chaosblade/ +chmod 755 /opt/chaosblade/blade +ln -sf /opt/chaosblade/blade /usr/local/bin/blade +EOF +} + +create_exp() { + local out id + out=$(run "$@" <<'EOF' +set -euo pipefail +sudo /opt/chaosblade/blade check os "$1" "$2" > /dev/null 2>&1 || true +sudo /opt/chaosblade/blade create "$1" "$2" "${@:3}" +EOF +) || return 1 + id=$(printf '%s\n' "$out" | grep -o '"result":"[^"]*"' | head -1 | sed 's/"result":"\([^"]*\)"/\1/') || return 1 + [[ -n "$id" ]] || return 1 + printf '%s' "$id" +} + +show() { run "$1" <<<'sudo /opt/chaosblade/blade status "$1"'; } +destroy() { run "$1" <<<"sudo /opt/chaosblade/blade destroy $1" || warn "Failed to destroy $1"; } + +main() { + install_chao + + cpu=$(create_exp cpu fullload --cpu-percent "$CPU_PERCENT" --timeout "$TIMEOUT") || + die "Failed to create CPU experiment" + IDS+=("$cpu") + show "$cpu" + info "Running CPU experiment for 5s..." + sleep 5 + + if [[ -n "$NET_DELAY_MS" ]]; then + if net=$(create_exp network delay --time "$NET_DELAY_MS" --interface "$INTERFACE" --timeout "$TIMEOUT"); then + IDS+=("$net") + show "$net" + info "Running network experiment for 5s..." + sleep 5 + destroy "$net" + else + warn "Skipping network experiment" + fi + fi + + destroy "$cpu" + show "$cpu" + info "Done." +} +main "$@" diff --git a/scripts/fault-injection/chaosblade_vm.sh b/scripts/fault-injection/chaosblade_vm.sh new file mode 100755 index 00000000..ca056180 --- /dev/null +++ b/scripts/fault-injection/chaosblade_vm.sh @@ -0,0 +1,75 @@ +#!/bin/bash + +set -euo pipefail + +CHAOSBLADE_VERSION="${CHAOSBLADE_VERSION:-1.8.0}" +CPU_PERCENT="${CHAOSBLADE_CPU_PERCENT:-20}" +TIMEOUT="${CHAOSBLADE_TIMEOUT_SEC:-30}" +NET_DELAY_MS="${CHAOSBLADE_NETWORK_DELAY_MS:-}" +INTERFACE="${CHAOSBLADE_INTERFACE:-eth0}" + +IDS=() + +cleanup() { + local id + for id in "${IDS[@]}"; do + sudo /opt/chaosblade/blade destroy "$id" || true + done +} +trap cleanup EXIT + +install_chao() { + command -v blade && { echo "[INFO] chaosblade already installed"; return; } + echo "[INFO] Installing chaosblade v${CHAOSBLADE_VERSION}..." + d=$(mktemp -d) + trap 'rm -rf "$d"' EXIT + cd "$d" + sudo zypper -n install --no-recommends wget tar + wget -q "https://github.com/chaosblade-io/chaosblade/releases/download/v${CHAOSBLADE_VERSION}/chaosblade-${CHAOSBLADE_VERSION}-linux_amd64.tar.gz" + tar -xzf "chaosblade-${CHAOSBLADE_VERSION}-linux_amd64.tar.gz" + sudo mkdir -p /opt/chaosblade + sudo cp -r "chaosblade-${CHAOSBLADE_VERSION}-linux_amd64"/. /opt/chaosblade/ + sudo chmod 755 /opt/chaosblade/blade + sudo ln -sf /opt/chaosblade/blade /usr/local/bin/blade +} + +create_exp() { + sudo /opt/chaosblade/blade check os "$1" "$2" > /dev/null 2>&1 || true + local out id + out=$(sudo /opt/chaosblade/blade create "$1" "$2" "${@:3}") + id=$(printf '%s\n' "$out" | grep -o '"result":"[^"]*"' | head -1 | sed 's/"result":"\([^"]*\)"/\1/') + [[ -n "$id" ]] || return 1 + printf '%s' "$id" +} + +show() { sudo /opt/chaosblade/blade status "$1"; } +destroy() { sudo /opt/chaosblade/blade destroy "$1" || true; } + +main() { + install_chao + + local cpu net + cpu=$(create_exp cpu fullload --cpu-percent "$CPU_PERCENT" --timeout "$TIMEOUT") || + { echo "[ERROR] Failed to create CPU experiment"; exit 1; } + IDS+=("$cpu") + show "$cpu" + echo "[INFO] Running CPU experiment for 5s..." + sleep 5 + + if [[ -n "$NET_DELAY_MS" ]]; then + if net=$(create_exp network delay --time "$NET_DELAY_MS" --interface "$INTERFACE" --timeout "$TIMEOUT"); then + IDS+=("$net") + show "$net" + echo "[INFO] Running network experiment for 5s..." + sleep 5 + destroy "$net" + else + echo "[WARN] Skipping network experiment" + fi + fi + + destroy "$cpu" + show "$cpu" + echo "[INFO] Done." +} +main "$@" diff --git a/scripts/fault-injection/common.sh b/scripts/fault-injection/common.sh new file mode 100644 index 00000000..a82d3a16 --- /dev/null +++ b/scripts/fault-injection/common.sh @@ -0,0 +1,43 @@ +#!/bin/bash + +set -euo pipefail + +info() { printf '[INFO] %s\n' "$*"; } +warn() { printf '[WARN] %s\n' "$*" >&2; } +die() { printf '[ERROR] %s\n' "$*" >&2; exit 1; } + +TARGET="${TARGET:-${1:-}}" +case "$TARGET" in + yocto) + SSH_ADDR="${SSH_YOCTO_ADDR:?SSH_YOCTO_ADDR}" + SSH_USER="${SSH_YOCTO_USER:?SSH_YOCTO_USER}" + SSH_PASS="${SSH_YOCTO_PASS:-}" + SSH_PORT="${SSH_YOCTO_PORT:?SSH_YOCTO_PORT}" + ;; + suse) + SSH_ADDR="${SSH_SUSE_ADDR_156:?SSH_SUSE_ADDR_156}" + SSH_USER="${SSH_SUSE_USER:?SSH_SUSE_USER}" + SSH_PASS="${SSH_SUSE_PASS:-}" + SSH_PORT="${SSH_SUSE_PORT_156:?SSH_SUSE_PORT_156}" + ;; + *) die "TARGET must be 'yocto' or 'suse' (env TARGET or first arg).";; +esac + +ssh_cmd() { + local base=(ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR) + if command -v sshpass >/dev/null 2>&1; then + sshpass -p "$SSH_PASS" "${base[@]}" -p "$SSH_PORT" -l "$SSH_USER" "$SSH_ADDR" "$@" + else + "${base[@]}" -p "$SSH_PORT" -l "$SSH_USER" "$SSH_ADDR" "$@" + fi +} + +SUDO_SETUP='if [[ "$(id -u)" -eq 0 ]]; then sudo() { "$@"; }; fi' + +run() { + { printf '%s\n' "$SUDO_SETUP"; cat; } | ssh_cmd bash -s -- "$@" +} + +has() { + run "$1" <<<'command -v "$1" >/dev/null 2>&1' +} diff --git a/scripts/fault-injection/ebpfault.sh b/scripts/fault-injection/ebpfault.sh new file mode 100644 index 00000000..b5954f2d --- /dev/null +++ b/scripts/fault-injection/ebpfault.sh @@ -0,0 +1,75 @@ +#!/bin/bash + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "${SCRIPT_DIR}/common.sh" + +DIR="${EBPFAULT_DIR:-/opt/ebpfault}" +VER="${EBPFAULT_VER:-1.1.2}" +SYSCALL="${EBPFAULT_SYSCALL:-fchmodat}" +ERRNO="${EBPFAULT_ERRNO:-ENOENT}" +PROB="${EBPFAULT_PROB:-50}" +ARGS="${EBPFAULT_ARGS:-/bin/chmod 0755 /tmp/ebpfault_marker}" + +install_ebpf() { + has "$DIR/bin/ebpfault" && { info "ebpfault already installed in ${DIR}"; return; } + info "Installing ebpfault v${VER}..." + run "$DIR" "$VER" <<'EOF' || die "Failed to install ebpfault." +set -euo pipefail +d=$1; v=$2 +url="https://github.com/trailofbits/ebpfault/releases/download/v${v}/ebpfault-${v}-1.x86_64.tar.gz" +tmp=$(mktemp -d) +trap 'rm -rf "$tmp"' EXIT +wget -q -O "$tmp/ebpfault.tar.gz" "$url" || curl -fsSL -o "$tmp/ebpfault.tar.gz" "$url" +tar -xzf "$tmp/ebpfault.tar.gz" -C "$tmp" +sudo mkdir -p "$d" +sudo chown "$(id -u):$(id -g)" "$d" +cp -r "$tmp"/ebpfault-"${v}"-1.x86_64/. "$d"/ +chmod +x "$d/bin/ebpfault" +EOF +} + +kprobe_supported() { + run <<'EOF' +{ zcat /proc/config.gz 2>/dev/null || cat "/boot/config-$(uname -r)" 2>/dev/null; } | grep -q '^CONFIG_BPF_KPROBE_OVERRIDE=y' +EOF +} + +gen_config() { + info "Writing ebpfault config (${SYSCALL} -> -${ERRNO}, ${PROB}%)..." + run "$DIR" "$SYSCALL" "$ERRNO" "$PROB" <<'EOF' || die "Failed to write ebpfault config." +cat > "$1/config.json" </dev/null || cat "/boot/config-$(uname -r)" 2>/dev/null; } | grep -q '^CONFIG_BPF_KPROBE_OVERRIDE=y' +} + +gen_config() { + echo "[INFO] Writing ebpfault config (${SYSCALL} -> -${ERRNO}, ${PROB}%)..." + cat > "$DIR/config.json" </dev/null 2>&1 || true +sudo "$KRF_DIR/src/krfctl/krfctl" -C >/dev/null 2>&1 || true +EOF + if (( LOADED )); then + run <<'EOF' || true +sudo rmmod krfx >/dev/null 2>&1 || true +EOF + fi +} +trap cleanup EXIT + +missing() { + run <<'EOF' +for tool in gcc make ruby; do + if ! command -v "$tool" >/dev/null 2>&1; then + echo "$tool" + fi +done +if ! ls /lib/modules/*/build >/dev/null 2>&1; then + echo "kernel headers" +fi +EOF +} + +install_deps() { + if [[ "$TARGET" == "suse" ]]; then + info "Installing krf build deps on ${TARGET}." + run <<'EOF' || die "Failed to install deps on ${TARGET}" +set -e +sudo zypper -n install --no-recommends gcc make libelf-devel ruby git-core kernel-default-devel +EOF + else + die "Missing dependencies cannot be installed to Poky in runtime. Please, add them to conf/packages.conf and rebuild image" + fi +} + +build_krf() { + if has "$KRF_DIR/src/krfctl/krfctl"; then + info "krf already built in ${KRF_DIR}" + return + fi + info "Cloning and building krf in ${KRF_DIR}..." + run "$KRF_DIR" <<'EOF' || die "krf build failed." +set -e +krf_dir="$1" +if [[ ! -d "$krf_dir" ]]; then + sudo git clone --depth 1 https://github.com/trailofbits/krf "$krf_dir" + sudo chown -R "$(id -u):$(id -g)" "$krf_dir" +fi +headers="/lib/modules/$(uname -r)/build" +if [[ ! -d "$headers" ]]; then + headers=$(ls -d /lib/modules/*/build 2>/dev/null | head -1) +fi +if [[ -z "$headers" || ! -d "$headers" ]]; then + echo "no kernel headers found" + exit 1 +fi +kver=$(basename "$(dirname "$headers")") +mkdir -p /tmp/krf-fakebin +cat > /tmp/krf-fakebin/uname <<'INNER' +#!/bin/bash +if [[ "$1" == "-r" ]]; then echo "__KVER__"; else /bin/uname "$@"; fi +INNER +sed -i "s/__KVER__/${kver}/" /tmp/krf-fakebin/uname +chmod +x /tmp/krf-fakebin/uname +export PATH="/tmp/krf-fakebin:$PATH" +make -C "$krf_dir/src/module/linux" codegen +make -C "$headers" M="$krf_dir/src/module/linux" modules +make PLATFORM=linux -C "$krf_dir/src/krfexec" +make PLATFORM=linux -C "$krf_dir/src/krfctl" +make PLATFORM=linux -C "$krf_dir/src/krfmesg" +EOF +} + +load_module() { + if run <<'EOF' +lsmod | grep -q '^krfx' +EOF + then + info "krfx module already loaded" + return + fi + info "Loading krfx.ko..." + run "$KRF_DIR" <<'EOF' || die "Failed to load krfx.ko." +set -e +sudo insmod -f "$1/src/module/linux/krfx.ko" +EOF + LOADED=1 +} + +configure_krf() { + info "Configuring krf (syscalls='${SYSCALLS}', prob=${PROB})..." + run "$KRF_DIR" "$SYSCALLS" "$PROB" "$PROFILE" <<'EOF' || die "Failed to configure krf." +set -e +krfctl="$1/src/krfctl/krfctl" +sudo "$krfctl" -F "$2" +sudo "$krfctl" -T personality=28 +sudo "$krfctl" -p "$3" +if [[ -n "${4:-}" ]]; then + sudo "$krfctl" -P "${4:-}" +fi +echo 1 | sudo tee /proc/krf/log_faults >/dev/null +EOF +} + +run_target() { + info "Running '${TARGET_CMD}' through krfexec..." + run </dev/null 2>&1 || true + sudo "$KRF_DIR/src/krfctl/krfctl" -C >/dev/null 2>&1 || true + if (( LOADED )); then + sudo rmmod krfx >/dev/null 2>&1 || true + fi +} +trap cleanup EXIT + +missing() { + for tool in gcc make ruby; do + command -v "$tool" >/dev/null 2>&1 || echo "$tool" + done + ls /lib/modules/*/build >/dev/null 2>&1 || echo "kernel headers" +} + +install_deps() { + sudo zypper -n install --no-recommends gcc make libelf-devel ruby git-core kernel-default-devel +} + +build_krf() { + if [[ -x "$KRF_DIR/src/krfctl/krfctl" ]]; then + echo "[INFO] krf already built in ${KRF_DIR}" + return + fi + echo "[INFO] Cloning and building krf in ${KRF_DIR}..." + if [[ ! -d "$KRF_DIR" ]]; then + sudo git clone --depth 1 https://github.com/trailofbits/krf "$KRF_DIR" + sudo chown -R "$(id -u):$(id -g)" "$KRF_DIR" + fi + headers="/lib/modules/$(uname -r)/build" + if [[ ! -d "$headers" ]]; then + headers=$(ls -d /lib/modules/*/build 2>/dev/null | head -1) + fi + if [[ -z "$headers" || ! -d "$headers" ]]; then + echo "no kernel headers found" + exit 1 + fi + kver=$(basename "$(dirname "$headers")") + echo "building against kernel headers: ${kver}" + mkdir -p /tmp/krf-fakebin + cat > /tmp/krf-fakebin/uname <<'INNER' +#!/bin/bash +if [[ "$1" == "-r" ]]; then echo "__KVER__"; else /bin/uname "$@"; fi +INNER + sed -i "s/__KVER__/${kver}/" /tmp/krf-fakebin/uname + chmod +x /tmp/krf-fakebin/uname + export PATH="/tmp/krf-fakebin:$PATH" + make -C "$KRF_DIR/src/module/linux" codegen + make -C "$headers" M="$KRF_DIR/src/module/linux" modules + make PLATFORM=linux -C "$KRF_DIR/src/krfexec" + make PLATFORM=linux -C "$KRF_DIR/src/krfctl" + make PLATFORM=linux -C "$KRF_DIR/src/krfmesg" +} + +load_module() { + if lsmod | grep -q '^krfx'; then + echo "[INFO] krfx module already loaded" + return + fi + echo "[INFO] Loading krfx.ko (force; built against non-running kernel headers)..." + sudo insmod -f "$KRF_DIR/src/module/linux/krfx.ko" + LOADED=1 +} + +configure_krf() { + echo "[INFO] Configuring krf (syscalls='${SYSCALLS}', prob=${PROB})..." + krfctl="$KRF_DIR/src/krfctl/krfctl" + sudo "$krfctl" -F "$SYSCALLS" + sudo "$krfctl" -T personality=28 + sudo "$krfctl" -p "$PROB" + if [[ -n "$PROFILE" ]]; then + sudo "$krfctl" -P "$PROFILE" + fi + echo 1 | sudo tee /proc/krf/log_faults >/dev/null +} + +run_target() { + echo "[INFO] Running '${TARGET_CMD}' through krfexec..." + set +e + "$KRF_DIR/src/krfexec/krfexec" $TARGET_CMD + echo "exit_code=$?" + set -e +} + +show_logs() { + sudo dmesg | grep -i "faulting" | tail -n 20 +} + +main() { + local missing_list + missing_list=$(missing) + echo "[INFO] Missing prerequisites: ${missing_list:-none}" + if [[ -n "$missing_list" ]]; then + install_deps + missing_list=$(missing) + if [[ -n "$missing_list" ]]; then + echo "[ERROR] Still missing prerequisites: $missing_list" + exit 1 + fi + fi + + build_krf + load_module + configure_krf + run_target + show_logs + echo "[INFO] Done!" +} +main "$@" diff --git a/scripts/opensuse/start-vm.sh b/scripts/opensuse/start-vm.sh new file mode 100755 index 00000000..5e942a22 --- /dev/null +++ b/scripts/opensuse/start-vm.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +set -e + +cd "${SUSE_DIR}" + +echo "Waiting for OpenSUSE image to be ready..." +while true; do + for img in open-suse-*.ready.qcow2; do + [ -f "$img" ] && IMAGE="$img" && break 2 + done + sleep 10 +done + +exec qemu-system-x86_64 -enable-kvm -m "${VM_RAM}" -smp "${CPUS}" -nographic \ + -monitor tcp:0.0.0.0:4444,server,nowait \ + -drive file="$IMAGE",index=0,media=disk \ + -cdrom cloud-init.iso \ + -net user,hostfwd=tcp::"${SSH_SUSE_PORT}"-:"${SSH_QEMU_PORT}",hostfwd=tcp::"${SUSE_NE_PORT}"-:"${DEFAULT_NE_PORT}",hostfwd=tcp:0.0.0.0:"${IPERF3_PORT}"-:"${IPERF3_PORT}",hostfwd=udp:0.0.0.0:"${IPERF3_PORT}"-:"${IPERF3_PORT}" \ + -net nic diff --git a/scripts/opensuse/suse-compose.sh b/scripts/opensuse/suse-compose.sh index 60959d06..9e3d2877 100755 --- a/scripts/opensuse/suse-compose.sh +++ b/scripts/opensuse/suse-compose.sh @@ -4,6 +4,4 @@ set -e ./scripts/download_images.py "$1" ./scripts/cloud-setup.sh "${S_USER}" "${PASSWORD}" -CPUS=4 -qemu-system-x86_64 -m "${VM_RAM}" -smp "${CPUS}" -nographic -monitor tcp:0.0.0.0:4444,server,nowait -drive file=open-suse-"$1".ready.qcow2,index=0,media=disk \ - -cdrom cloud-init.iso -net user,hostfwd=tcp::"${SSH_SUSE_PORT}"-:"${SSH_QEMU_PORT}",hostfwd=tcp::"${SUSE_NE_PORT}"-:"${DEFAULT_NE_PORT}",hostfwd=tcp:0.0.0.0:"${IPERF3_PORT}"-:"${IPERF3_PORT}",hostfwd=udp:0.0.0.0:"${IPERF3_PORT}"-:"${IPERF3_PORT}" -net nic +echo "Image is ready. VM will be started by imgtests-suse-vm container." diff --git a/scripts/start-vm-yocto.sh b/scripts/start-vm-yocto.sh new file mode 100755 index 00000000..cd649b92 --- /dev/null +++ b/scripts/start-vm-yocto.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +rm -rf /home/user/poky/meta-openjdk-temurin/recipes-java/helloworld-java + +# shellcheck disable=SC1091 +source "/home/user/poky/oe-init-build-env" "/home/user/poky/build" + +ROOTFS_IMAGE="${POKY_DIR}/build/tmp/deploy/images/qemux86-64/${OS_IMAGE}-qemux86-64.rootfs.ext4.qcow2" + +QB_SLIRP_OPT="-netdev user,id=net0,hostfwd=tcp::8080-:80,hostfwd=tcp::${SSH_QEMU_PORT}-:${SSH_TO_QEMU_PORT},hostfwd=tcp::${YOCTO_NE_PORT}-:${DEFAULT_NE_PORT},hostfwd=tcp:0.0.0.0:${IPERF3_PORT}-:${IPERF3_PORT},hostfwd=udp:0.0.0.0:${IPERF3_PORT}-:${IPERF3_PORT}" \ + runqemu qemux86-64 kvm slirp nographic "${ROOTFS_IMAGE}" qemuparams="-monitor tcp:0.0.0.0:4444,server,nowait -smp ${CPUS}"