Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .github/tests/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/bin/sh

set -x

PY_VER=$(gdb -q -nx -ex "pi print('.'.join(map(str, sys.version_info[:2])))" -ex quit 2>/dev/null || echo "3")
GEF_CI_NB_CPU=$(grep -c ^processor /proc/cpuinfo)

# Setup GEF
echo "source /gef/gef.py" > /root/.gdbinit

# Verify GEF setup
gdb -q -ex "gef missing" -ex "gef help" -ex "gef config" -ex start -ex continue -ex quit /bin/pwd

# Build test binaries
make -C tests/binaries -j ${GEF_CI_NB_CPU}

# Run pytest
python${PY_VER} -m pytest --forked -n ${GEF_CI_NB_CPU} -v -m "not benchmark" tests/
5 changes: 5 additions & 0 deletions .github/tests/run-tests.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/bin/sh

[ -z "${1}" ] && echo "Provide a container tag as an argument to this script" && exit 1

docker run --privileged --rm -e GITHUB_ACTIONS -v "$PWD:/gef" gef-test:${1}
51 changes: 51 additions & 0 deletions .github/tests/setup-dockerfile.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#!/bin/sh

[ -z "${1}" ] && echo "Provide a container image as an argument to this script" && exit 1

echo "FROM ${1}" > Dockerfile

cat >> Dockerfile << 'EOF'

# Install dependencies for Ubuntu-based images
RUN if [ -f /etc/debian_version ]; then \
export DEBIAN_FRONTEND=noninteractive NEEDRESTART_MODE=n && \
apt-get update && \
apt-get install -y gdb-multiarch python3-dev python3-pip python3-wheel python3-setuptools \
git cmake gcc g++ pkg-config libglib2.0-dev gdbserver qemu-user file; \
fi

# Install python3-full for Ubuntu 24.04
RUN if grep -q "24.04" /etc/os-release 2>/dev/null; then \
apt-get install -y python3-full; \
fi

# Install dependencies for Fedora-based images
RUN if [ -f /etc/fedora-release ]; then \
dnf install -y gdb gdb-gdbserver python3-devel python3-pip python3-wheel python3-setuptools python3-rpm \
git cmake gcc gcc-c++ pkg-config glib2-devel qemu-user qemu-user-static file procps-ng && \
dnf --enablerepo='*debug*' install -y glibc-debuginfo && \
dnf clean all; \
fi

# Copy only requirements.txt for caching
COPY tests/requirements.txt /tmp/requirements.txt

# Install Python requirements
RUN PY_VER=$(gdb -q -nx -ex "pi print('.'.join(map(str, sys.version_info[:2])))" -ex quit 2>/dev/null || echo "3") && \
if grep -q "24.04" /etc/os-release 2>/dev/null; then \
python${PY_VER} -m pip install --break-system-packages --upgrade -r /tmp/requirements.txt; \
else \
python${PY_VER} -m pip install --upgrade -r /tmp/requirements.txt; \
fi

RUN git config --global --add safe.directory /gef

WORKDIR /gef

# Copy entrypoint script
COPY .github/tests/entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh

ENTRYPOINT ["/entrypoint.sh"]

EOF
98 changes: 31 additions & 67 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
name: Tests

env:
GEF_CI_CACHE_DIR: /tmp
PY_VER: 0
GEF_CI_NB_CPU: 1

on:
Expand All @@ -19,78 +17,44 @@ jobs:
strategy:
fail-fast: false
matrix:
runner:
- ubuntu-24.04
- ubuntu-22.04
- ubuntu-24.04-arm
- ubuntu-22.04-arm

name: "Run Unit tests on ${{ matrix.runner }}"
runs-on: ${{ matrix.runner }}
arch: [amd64, arm64]
os: [ubuntu-24.04, ubuntu-22.04, fedora-42, fedora-43, fedora-rawhide]
include:
- os: ubuntu-24.04
base_image: ubuntu:24.04
- os: ubuntu-22.04
base_image: ubuntu:22.04
- os: fedora-42
base_image: fedora:42
- os: fedora-43
base_image: fedora:43
- os: fedora-rawhide
base_image: fedora:rawhide

name: "Run Unit tests on ${{ matrix.os }} (${{ matrix.arch }})"
runs-on: ${{ matrix.arch == 'arm64' && 'ubuntu-24.04-arm' || 'ubuntu-latest' }}
defaults:
run:
shell: bash

steps:
- uses: actions/checkout@v4

- name: Install python and toolchain
run: |
export NEEDRESTART_MODE=n
sudo apt-get update
sudo apt-get install -y gdb-multiarch python3-dev python3-pip python3-wheel python3-setuptools git cmake gcc g++ pkg-config libglib2.0-dev gdbserver qemu-user

- name: Install python and toolchain
if: startsWith(matrix.runner, 'ubuntu-24.04')
run: |
sudo apt-get install -y python3-full
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Install python and toolchain
if: startsWith(matrix.runner, 'ubuntu-24.04') == false
run: |
python3 -m pip install pip -U
- name: Create Dockerfile
run: .github/tests/setup-dockerfile.sh ${{ matrix.base_image }}

- name: Set runtime environment variables
run: |
echo PY_VER=`gdb -q -nx -ex "pi print('.'.join(map(str, sys.version_info[:2])))" -ex quit` >> $GITHUB_ENV
echo GEF_CI_NB_CPU="`grep -c ^processor /proc/cpuinfo`" >> $GITHUB_ENV
echo GEF_CI_ARCH="`uname --processor`" >> $GITHUB_ENV
echo GEF_CI_CACHE_DIR="`python3 -m pip cache dir`" >> $GITHUB_ENV

- name: Cache dependencies
uses: actions/cache@v4
id: cache-deps
env:
cache-name: cache-deps
- name: Build container image
uses: docker/build-push-action@v6
with:
key: ${{ matrix.runner }}-pip-${{ hashFiles('**/requirements.txt') }}
path: ${{ env.GEF_CI_CACHE_DIR }}
restore-keys:
${{ matrix.runner }}-pip-${{ env.cache-name }}-
${{ matrix.runner }}-pip-
${{ matrix.runner }}-${{ env.cache-name }}-
${{ matrix.runner }}-

- name: Install python and toolchain
if: startsWith(matrix.runner, 'ubuntu-24.04') == false
run: |
test "${{ env.PY_VER }}" == "0" && exit 1
mkdir -p ${{ env.GEF_CI_CACHE_DIR }}
python${{ env.PY_VER }} -m pip install --user --upgrade -r tests/requirements.txt

- name: Install Python Requirements
if: startsWith(matrix.runner, 'ubuntu-24.04')
run: |
test "${{ env.PY_VER }}" == "0" && exit 1
mkdir -p ${{ env.GEF_CI_CACHE_DIR }}
python${{ env.PY_VER }} -m pip install --break-system-packages --user --upgrade -r tests/requirements.txt

- name: Setup GEF
run: |
echo "source $(pwd)/gef.py" > ~/.gdbinit
gdb -q -ex 'gef missing' -ex 'gef help' -ex 'gef config' -ex start -ex continue -ex quit /bin/pwd

- name: Run Tests
run: |
make -C tests/binaries -j ${{ env.GEF_CI_NB_CPU }}
python${{ env.PY_VER }} -m pytest --forked -n ${{ env.GEF_CI_NB_CPU }} -v -m "not benchmark" tests/
context: .
push: false
load: true
tags: gef-test:${{ matrix.os }}-${{ matrix.arch }}
cache-from: type=gha,scope=${{ matrix.os }}-${{ matrix.arch }}
cache-to: type=gha,mode=max,scope=${{ matrix.os }}-${{ matrix.arch }}

- name: Run tests in container
run: .github/tests/run-tests.sh ${{ matrix.os }}-${{ matrix.arch }}
13 changes: 13 additions & 0 deletions tests/commands/heap.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
ERROR_INACTIVE_SESSION_MESSAGE,
debug_target,
findlines,
is_glibc_ge,
is_32b,
is_64b,
)
Expand All @@ -21,6 +22,7 @@ def setUp(self) -> None:
self._target = debug_target("heap")
return super().setUp()

@pytest.mark.skipif(is_glibc_ge(2, 43), reason="Skipped for glibc >= 2.43")
def test_cmd_heap_arenas(self):
gdb = self._gdb
cmd = "heap arenas"
Expand All @@ -32,6 +34,7 @@ def test_cmd_heap_arenas(self):
res = gdb.execute(cmd, to_string=True)
self.assertIn("Arena(base=", res)

@pytest.mark.skipif(is_glibc_ge(2, 43), reason="Skipped for glibc >= 2.43")
def test_cmd_heap_set_arena(self):
gdb = self._gdb
cmd = "heap set-arena &main_arena"
Expand Down Expand Up @@ -67,6 +70,7 @@ def test_cmd_heap_chunk_with_number(self):
chunklines = findlines("Chunk(addr=", res)
self.assertEqual(len(chunklines), 2)

@pytest.mark.skipif(is_glibc_ge(2, 43), reason="Skipped for glibc >= 2.43")
def test_cmd_heap_chunks(self):
gdb = self._gdb
cmd = "heap chunks"
Expand All @@ -91,6 +95,7 @@ def test_cmd_heap_chunks_summary(self):
self.assertIn("== Chunk distribution by size", res)
self.assertIn("== Chunk distribution by flag", res)

@pytest.mark.skipif(is_glibc_ge(2, 43), reason="Skipped for glibc >= 2.43")
def test_cmd_heap_chunks_min_size_filter(self):
gdb = self._gdb
self.assertEqual(
Expand Down Expand Up @@ -148,6 +153,7 @@ def setUp(self) -> None:
self._target = debug_target("heap-non-main")
return super().setUp()

@pytest.mark.skipif(is_glibc_ge(2, 43), reason="Skipped for glibc >= 2.43")
def test_cmd_heap_chunks(self):
gdb = self._gdb
cmd = "heap chunks"
Expand All @@ -169,6 +175,7 @@ def test_cmd_heap_chunks(self):
# make sure that the chunks of each arena are distinct
self.assertNotEqual(chunks, non_main_chunks)

@pytest.mark.skipif(is_glibc_ge(2, 43), reason="Skipped for glibc >= 2.43")
def test_cmd_heap_bins_non_main(self):
gdb = self._gdb
gef = self._gef
Expand All @@ -181,6 +188,7 @@ def test_cmd_heap_bins_non_main(self):
self.assertIn("size=0x20", res)

@pytest.mark.skipif(ARCH not in ("i686", "x86_64",), reason=f"Skipped for {ARCH}")
@pytest.mark.skipif(is_glibc_ge(2, 42), reason="Skipped for glibc >= 2.42")
def test_cmd_heap_bins_tcache(self):
gdb = self._gdb
gdb.execute("run")
Expand All @@ -202,6 +210,7 @@ def setUp(self) -> None:
self._target = debug_target("heap-multiple-heaps")
return super().setUp()

@pytest.mark.skipif(is_glibc_ge(2, 43), reason="Skipped for glibc >= 2.43")
def test_cmd_heap_chunks_mult_heaps(self):
gdb = self._gdb

Expand Down Expand Up @@ -259,6 +268,7 @@ def setUp(self) -> None:
self.expected_unsorted_bin_size = 0x430 if ARCH == "i686" or is_64b() else 0x428
return super().setUp()

@pytest.mark.skipif(is_glibc_ge(2, 43), reason="Skipped for glibc >= 2.43")
def test_cmd_heap_bins_large(self):
gdb = self._gdb
gdb.execute("run")
Expand All @@ -268,6 +278,7 @@ def test_cmd_heap_bins_large(self):
self.assertIn("Chunk(addr=", res)
self.assertIn(f"size={self.expected_large_bin_size:#x}", res)

@pytest.mark.skipif(is_glibc_ge(2, 43), reason="Skipped for glibc >= 2.43")
def test_cmd_heap_bins_small(self):
gdb = self._gdb
cmd = "heap bins small"
Expand All @@ -278,6 +289,7 @@ def test_cmd_heap_bins_small(self):
self.assertIn("Chunk(addr=", res)
self.assertIn(f"size={self.expected_small_bin_size:#x}", res)

@pytest.mark.skipif(is_glibc_ge(2, 43), reason="Skipped for glibc >= 2.43")
def test_cmd_heap_bins_unsorted(self):
gdb = self._gdb
gdb.execute("run")
Expand All @@ -295,6 +307,7 @@ def setUp(self) -> None:
return super().setUp()

@pytest.mark.skipif(ARCH not in ("i686", "x86_64"), reason=f"Skipped for {ARCH}")
@pytest.mark.skipif(is_glibc_ge(2, 42), reason="Skipped for glibc >= 2.42")
def test_cmd_heap_bins_tcache_all(self):
gdb = self._gdb
gdb.execute("run")
Expand Down
4 changes: 3 additions & 1 deletion tests/commands/scan.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
scan command test module
"""

import pytest

from tests.base import RemoteGefUnitTestGeneric
from tests.utils import ERROR_INACTIVE_SESSION_MESSAGE, debug_target
from tests.utils import ARCH, ERROR_INACTIVE_SESSION_MESSAGE, IN_GITHUB_ACTIONS, debug_target, is_glibc_ge


class ScanCommand(RemoteGefUnitTestGeneric):
Expand All @@ -14,6 +15,7 @@ def setUp(self) -> None:
self._target = debug_target("default")
return super().setUp()

@pytest.mark.skipif(ARCH == "aarch64" and IN_GITHUB_ACTIONS and is_glibc_ge(2, 41), reason=f"Skipped for {ARCH} on CI with glibc >= 2.41")
def test_cmd_scan(self):
gdb = self._gdb
cmd = "scan libc stack"
Expand Down
8 changes: 8 additions & 0 deletions tests/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,14 @@ class Color(enum.Enum):
BLINK_OFF = "\001\033[25m\002"


def is_glibc_ge(major, minor):
ver = platform.libc_ver()
if ver[0] == 'glibc':
(glibc_major, glibc_minor, *glibc_patch) = list(map(int, ver[1].split('.')))
return (glibc_major, glibc_minor) >= (major, minor)
return False


def is_64b() -> bool:
return ARCH in CI_VALID_ARCHITECTURES_64B

Expand Down
Loading