Test more kernels (#1684)

This commit is contained in:
theguy147 2023-05-01 21:39:16 +02:00 committed by GitHub
parent a9ec60c727
commit 6ff05bbcc8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 125 additions and 81 deletions

View File

@ -67,6 +67,13 @@ jobs:
./setup-dev.sh --user
mkdir .cov
- name: Set up cache for QEMU images
id: qemu-cache
uses: actions/cache@v3
with:
path: ./tests/qemu-tests/images
key: ${{ matrix.os }}-cache-qemu-images
- name: Download images
run: |
./tests/qemu-tests/download_images.sh

View File

@ -8,21 +8,23 @@ URL="https://github.com/gsingh93/linux-exploit-dev-env/releases/latest/download"
mkdir -p "${OUT_DIR}"
for arch in x86_64 arm64; do
file="rootfs-${arch}.img"
wget "${URL}/${file}" -O "${OUT_DIR}/${file}"
download() {
local file="$1"
hash_old=$(grep "${file}" "${OUT_DIR}/hashsums.txt.old" || true)
hash_new=$(grep "${file}" "${OUT_DIR}/hashsums.txt")
# only download file if it doesn't exist or its hashsum has changed
if [ ! -f "${OUT_DIR}/${file}" ] || [ "${hash_new}" != "${hash_old}" ]; then
wget "${URL}/${file}" -O "${OUT_DIR}/${file}"
fi
}
file="vmlinux-linux-${arch}"
wget "${URL}/${file}" -O "${OUT_DIR}/${file}"
if [ -f "${OUT_DIR}/hashsums.txt" ]; then
mv -f "${OUT_DIR}/hashsums.txt" "${OUT_DIR}/hashsums.txt.old"
fi
file="vmlinux-ack-${arch}"
wget "${URL}/${file}" -O "${OUT_DIR}/${file}"
done
wget "${URL}/hashsums.txt" -O "${OUT_DIR}/hashsums.txt"
for kernel_type in ack linux; do
file="bzImage-${kernel_type}-x86_64"
wget "${URL}/${file}" -O "${OUT_DIR}/${file}"
file="Image-${kernel_type}-arm64"
wget "${URL}/${file}" -O "${OUT_DIR}/${file}"
done
while read -r hash file; do
echo "Downloading ${file}..."
download "${file}"
done < "${OUT_DIR}/hashsums.txt"

View File

@ -4,28 +4,27 @@ ARCH=""
KERNEL_TYPE=""
CMDLINE=""
VALID_ARCHS=("x86_64" "arm64" "aarch64")
VALID_TYPES=("linux" "ack")
CWD=$(dirname -- "$0")
IMAGE_DIR="${CWD}/images"
KERNEL_LIST=($(basename -a "${IMAGE_DIR}"/vmlinux* | sed "s/vmlinux-//"))
help_and_exit() {
echo "Usage: $0 [options] [-- other qemu options]"
echo ""
echo " --arch=<ARCH> select the architecture to run"
echo " possible values: [${VALID_ARCHS[*]}]"
echo ""
echo " --type=<TYPE> select the kernel type to run"
echo " possible values: [${VALID_TYPES[*]}]"
echo ""
echo " --append=<CMDLINE> append something to the kernel's cmdline."
echo " --kernel=<KERNEL> select kernel to run"
echo " --append=<CMDLINE> append something to the kernel's cmdline."
echo ""
echo "Options after '--' will be passed to QEMU."
echo ""
echo "Available kernels:"
printf "\t%s\n" "${KERNEL_LIST[@]}"
exit 1
}
while [[ $# -gt 0 ]]; do
case "$1" in
--arch=*) ARCH="${1#--arch=}" ;;
--type=*) KERNEL_TYPE="${1#--type=}" ;;
--kernel=*) KERNEL_NAME="${1#--kernel=}" ;;
--append=*) CMDLINE="${CMDLINE} ${1#--append=}" ;;
-h | --help) help_and_exit ;;
--)
@ -37,44 +36,39 @@ while [[ $# -gt 0 ]]; do
shift
done
CWD=$(dirname -- "$0")
IMAGE_DIR="${CWD}/images"
if [ -z "$ARCH" ]; then
if [ -z "${KERNEL_NAME}" ]; then
help_and_exit
fi
if [[ ! " ${VALID_ARCHS[*]} " =~ " ${ARCH} " ]]; then
echo "Invalid arch '${ARCH}'"
if [[ ! " ${KERNEL_LIST[*]} " =~ " ${KERNEL_NAME} " ]]; then
echo "Invalid kernel '${KERNEL_NAME}'"
help_and_exit
fi
if [[ ! " ${VALID_TYPES[*]} " =~ " ${KERNEL_TYPE} " ]]; then
echo "Invalid kernel type '${KERNEL_TYPE}'"
help_and_exit
fi
# KERNEL_NAME = <KERNEL_TYPE>-<KERNEL_VERSION>-<ARCH>
# e.g. "linux-5.10.178-arm64" or "ack-android13-5.10-lts-x86_64"
ARCH="${KERNEL_NAME##*-}"
KERNEL_VERSION=$(echo ${KERNEL_NAME} | grep -oP "\d+\.\d+(\.\d+)?(-lts)?")
KERNEL_TYPE=$(echo ${KERNEL_NAME} | sed "s/-${KERNEL_VERSION}-${ARCH}//")
if [[ "${ARCH}" == @(arm64|aarch64) ]]; then
ARCH=arm64
QEMU_BIN=qemu-system-aarch64
KERNEL="${IMAGE_DIR}/Image-${KERNEL_TYPE}-arm64"
ROOTFS="${IMAGE_DIR}/rootfs-arm64.img"
CMDLINE="console=ttyAMA0 root=/dev/vda nokaslr ${CMDLINE}"
QEMU_ARGS=(
-cpu max
-machine virt
-append "console=ttyAMA0 root=/dev/vda nokaslr ${CMDLINE}"
)
elif [ "$ARCH" == "x86_64" ]; then
QEMU_BIN=qemu-system-x86_64
KERNEL="${IMAGE_DIR}/bzImage-${KERNEL_TYPE}-x86_64"
ROOTFS="${IMAGE_DIR}/rootfs-x86_64.img"
QEMU_ARGS=(
-append "8250.nr_uarts=1 console=ttyS0 root=/dev/vda nokaslr ${CMDLINE}"
)
CMDLINE="8250.nr_uarts=1 console=ttyS0 root=/dev/vda nokaslr ${CMDLINE}"
QEMU_ARGS=()
fi
KERNEL=$(echo ${IMAGE_DIR}/*Image-${KERNEL_NAME})
ROOTFS=$(echo ${IMAGE_DIR}/*-${ARCH}.img)
QEMU_ARGS+=(
-kernel $KERNEL
-nographic
@ -85,4 +79,4 @@ QEMU_ARGS+=(
echo "Waiting for GDB to attach (use 'ctrl-a x' to quit)"
$QEMU_BIN "${QEMU_ARGS[@]}"
$QEMU_BIN ${QEMU_ARGS[@]} -append "${CMDLINE}"

View File

@ -7,20 +7,9 @@ ROOT_DIR="$(readlink -f ../../)"
GDB_INIT_PATH="$ROOT_DIR/gdbinit.py"
COVERAGERC_PATH="$ROOT_DIR/pyproject.toml"
ARCH=""
KERNEL_TYPE=""
VMLINUX=""
PLATFORMS=(
# ARCH KERNEL_TYPE [QEMU_ARGS]
"x86_64 linux"
"x86_64 ack"
"arm64 linux"
"arm64 ack"
)
CWD=$(dirname -- "$0")
IMAGE_DIR="${CWD}/images"
VMLINUX_LIST=($(basename -a "${IMAGE_DIR}"/vmlinux*))
ptrace_scope=$(cat /proc/sys/kernel/yama/ptrace_scope)
if [[ $ptrace_scope -ne 0 && $(id -u) -ne 0 ]]; then
@ -96,7 +85,10 @@ done
gdb_load_pwndbg=(--command "$GDB_INIT_PATH" -ex "set exception-verbose on")
run_gdb() {
if [[ "$ARCH" == x86_64 ]]; then
local arch="$1"
shift
if [[ "${arch}" == x86_64 ]]; then
GDB=gdb
else
GDB=gdb-multiarch
@ -109,7 +101,7 @@ run_gdb() {
# NOTE: We run tests under GDB sessions and because of some cleanup/tests dependencies problems
# we decided to run each test in a separate GDB session
gdb_args=(--command pytests_collect.py)
TESTS_COLLECT_OUTPUT=$(run_gdb "${gdb_args[@]}")
TESTS_COLLECT_OUTPUT=$(run_gdb "x86_64" "${gdb_args[@]}")
if [ $? -eq 1 ]; then
echo -E "$TESTS_COLLECT_OUTPUT"
@ -122,15 +114,22 @@ fi
TESTS_LIST=($(echo -E "$TESTS_COLLECT_OUTPUT" | grep -o "tests/.*::.*" | grep "${TEST_NAME_FILTER}"))
init_gdb() {
gdb_connect_qemu=(-ex "file ${VMLINUX}" -ex "target remote :1234")
gdb_args=("${gdb_connect_qemu[@]}" -ex 'break start_kernel' -ex 'continue')
run_gdb "${gdb_args[@]}" > /dev/null 2>&1
local kernel_type="$1"
local kernel_version="$2"
local arch="$3"
gdb_connect_qemu=(-ex "file ${IMAGE_DIR}/vmlinux-${kernel_type}-${kernel_version}-${arch}" -ex "target remote :1234")
gdb_args=("${gdb_connect_qemu[@]}" -ex 'break *start_kernel' -ex 'continue')
run_gdb "${arch}" "${gdb_args[@]}" > /dev/null 2>&1
}
run_test() {
test_case="$1"
local kernel_type="$2"
local kernel_version="$3"
local arch="$4"
gdb_connect_qemu=(-ex "file ${VMLINUX}" -ex "target remote :1234")
gdb_connect_qemu=(-ex "file ${IMAGE_DIR}/vmlinux-${kernel_type}-${kernel_version}-${arch}" -ex "target remote :1234")
gdb_args=("${gdb_connect_qemu[@]}" --command pytests_launcher.py)
if [ ${RUN_CODECOV} -ne 0 ]; then
gdb_args=(-ex 'py import coverage;coverage.process_startup()' "${gdb_args[@]}")
@ -141,9 +140,10 @@ run_test() {
USE_PDB="${USE_PDB}" \
PWNDBG_LAUNCH_TEST="${test_case}" \
PWNDBG_DISABLE_COLORS=1 \
PWNDBG_ARCH="$ARCH" \
PWNDBG_KERNEL_TYPE="$KERNEL_TYPE" \
run_gdb "${gdb_args[@]}"
PWNDBG_ARCH="${arch}" \
PWNDBG_KERNEL_TYPE="${kernel_type}" \
PWNDBG_KERNEL_VERSION="${kernel_version}" \
run_gdb "${arch}" "${gdb_args[@]}"
return $?
}
@ -173,22 +173,27 @@ process_output() {
}
test_system() {
local kernel_type="$1"
local kernel_version="$2"
local arch="$3"
shift 3
local qemu_args=("$@")
FAILED_TESTS=()
echo "============================ Testing $KERNEL_TYPE-$ARCH ============================"
printf "============================ Testing %-20s ============================\n" "${kernel_type}-${kernel_version}-${arch}"
if [[ ! -z ${QEMU_ARGS} ]]; then
echo "Additional QEMU parameters used: '${QEMU_ARGS[*]}'"
if [[ ! -z ${qemu_args} ]]; then
echo "Additional QEMU parameters used: '${qemu_args[*]}'"
fi
echo ""
"${CWD}/run_qemu_system.sh" --arch="$ARCH" --type="$KERNEL_TYPE" -- "${QEMU_ARGS[@]}" > /dev/null 2>&1 &
"${CWD}/run_qemu_system.sh" --kernel="${kernel_type}-${kernel_version}-${arch}" -- "${qemu_args[@]}" > /dev/null 2>&1 &
init_gdb
init_gdb "${kernel_type}" "${kernel_version}" "${arch}"
start=$(date +%s)
for t in "${TESTS_LIST[@]}"; do
output=$(run_test "$t")
output=$(run_test "$t" "${kernel_type}" "${kernel_version}" "${arch}")
process_output "$output"
done
@ -216,13 +221,19 @@ test_system() {
pkill qemu-system
}
for platform in "${PLATFORMS[@]}"; do
read -r arch kernel_type qemu_args <<< "$platform"
for vmlinux in "${VMLINUX_LIST[@]}"; do
KERNEL=$(echo "${vmlinux}" | sed "s/vmlinux-//")
# extract architecture as last dash-separated group of the kernels name
ARCH="${KERNEL##*-}"
KERNEL_VERSION=$(echo ${KERNEL} | grep -oP "\d+\.\d+(\.\d+)?(-lts)?")
KERNEL_TYPE=$(echo ${KERNEL} | sed "s/-${KERNEL_VERSION}-${ARCH}//")
QEMU_ARGS=()
ARCH="$arch"
KERNEL_TYPE="$kernel_type"
QEMU_ARGS=($qemu_args)
VMLINUX="${IMAGE_DIR}/vmlinux-${KERNEL_TYPE}-${ARCH}"
test_system "${KERNEL_TYPE}" "${KERNEL_VERSION}" "${ARCH}" ${QEMU_ARGS}
test_system
if [[ "${ARCH}" == @("x86_64") ]]; then
# additional test with extra QEMU flags
QEMU_ARGS=(-cpu qemu64,+la57)
test_system "${KERNEL_TYPE}" "${KERNEL_VERSION}" "${ARCH}" "${QEMU_ARGS[@]}"
fi
done

View File

@ -1,5 +1,7 @@
import gdb
import pwndbg
def test_command_kbase():
pass # TODO
@ -16,6 +18,11 @@ def test_command_kcmdline():
def test_command_kconfig():
if not pwndbg.gdblib.kernel.has_debug_syms():
res = gdb.execute("kconfig", to_string=True)
assert "may only be run when debugging a Linux kernel with debug" in res
return
res = gdb.execute("kconfig", to_string=True)
assert "CONFIG_IKCONFIG = y" in res
@ -24,11 +31,21 @@ def test_command_kconfig():
def test_command_kversion():
if not pwndbg.gdblib.kernel.has_debug_syms():
res = gdb.execute("kversion", to_string=True)
assert "may only be run when debugging a Linux kernel with debug" in res
return
res = gdb.execute("kversion", to_string=True)
assert "Linux version" in res
def test_command_slab_list():
if not pwndbg.gdblib.kernel.has_debug_syms():
res = gdb.execute("slab list", to_string=True)
assert "may only be run when debugging a Linux kernel with debug" in res
return
res = gdb.execute("slab list", to_string=True)
assert "kmalloc" in res

View File

@ -1,9 +1,20 @@
import os
import gdb
import pytest
import pwndbg
from pwndbg.gdblib import kernel
ARCH = os.getenv("PWNDBG_ARCH")
KERNEL_TYPE = os.getenv("PWNDBG_KERNEL_TYPE")
KERNEL_VERSION = os.getenv("PWNDBG_KERNEL_VERSION")
@pytest.mark.skipif(
ARCH in ["x86_64"] and KERNEL_TYPE in ["linux"] and KERNEL_VERSION.startswith("5"),
reason="global pfn symbols missing. further investigation required", # TODO: fix
)
def test_gdblib_kernel_archops_address_translation():
# test address translation functions for LowMem
min_low_pfn = int(gdb.lookup_global_symbol("min_low_pfn").value())
@ -21,6 +32,7 @@ def test_gdblib_kernel_archops_address_translation():
assert kernel.page_to_phys(kernel.phys_to_page(phys)) == phys
@pytest.mark.skipif(not pwndbg.gdblib.kernel.has_debug_syms(), reason="test requires debug symbols")
def test_gdblib_kernel_krelease():
release_ver = pwndbg.gdblib.kernel.krelease()
# release should be int tuple of form (major, minor, patch) or (major, minor)
@ -29,5 +41,6 @@ def test_gdblib_kernel_krelease():
assert release_str in pwndbg.gdblib.kernel.kversion()
@pytest.mark.skipif(not pwndbg.gdblib.kernel.has_debug_syms(), reason="test requires debug symbols")
def test_gdblib_kernel_is_kaslr_enabled():
pwndbg.gdblib.kernel.is_kaslr_enabled()