Fix jemalloc and qemu tests on nixos (#2515)

* docs: Add missing testing toc entries and other minor formatting

* fix: Adjust nix dev shell packages to properly support jemalloc and qemu tests

* fix(jemalloc): Add more robust error handling to jemalloc commands and fix test

* fix: point JEMALLOC_PATH to correct jemalloc package path

* fix: Use correct aglib-compatible symbol resolution function

* fix: re-enable test_jemalloc_heap test and make slightly more forgiving
pull/2529/head
Aaron Adams 1 year ago committed by GitHub
parent 019ff1a625
commit f26453884f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -103,8 +103,8 @@ We use `qemu-system` for full system level emulation for our Linux kernel tests.
### Testing Under Nix
You will need to build a nix-compatible gdbinit.py file, which you can do with `nix build .#pwndbg-dev`. Then simply run
the test by adding the `--nix` flag:
You will need to build a nix-compatible `gdbinit.py` file, which you can do with `nix build .#pwndbg-dev`. Then simply
run the test by adding the `--nix` flag:
```bash
./tests.sh --nix [filter]

@ -1,4 +1,4 @@
# This should be kept in sync with setup-dev.sh and lint.sh requirements
# Packages installed by this file should be kept in sync with setup-dev.sh and lint.sh requirements
{
pkgs ? # If pkgs is not defined, instantiate nixpkgs from locked commit
let
@ -15,45 +15,81 @@
...
}:
let
lib = pkgs.lib;
pyEnv = import ./pyenv.nix {
inherit
pkgs
lib
python3
inputs
isLLDB
;
lib = pkgs.lib;
isDev = true;
};
jemalloc-static = pkgs.jemalloc.overrideAttrs (oldAttrs: {
version = "5.3.0"; # version match setup-dev.sh
configureFlags = (oldAttrs.configureFlags or [ ]) ++ [
"--enable-static"
"--disable-shared"
];
# debug symbols currently required for jemalloc.py type resolution
preBuild = ''
makeFlagsArray+=(CFLAGS="-O0 -g")
'';
postInstall = ''
${oldAttrs.postInstall or ""}
cp -v lib/libjemalloc.a $out/lib/
'';
dontStrip = true; # don't strip the debug symbols we added
});
in
{
default = pkgs.mkShell {
NIX_CONFIG = "extra-experimental-features = nix-command flakes repl-flake";
# Anything not handled by the poetry env
nativeBuildInputs =
(with pkgs; [
# from setup-dev.sh
nasm
gcc
curl
gdb
parallel
qemu
netcat-openbsd
zig_0_10 # matches setup-dev.sh
go
builtins.attrValues {
inherit (pkgs)
# from setup-dev.sh
nasm
gcc
curl
gdb
parallel
qemu
netcat-openbsd
zig_0_10 # version match setup-dev.sh
go
# from qemu-tests.sh
binutils
;
}
++ [
jemalloc-static
# from qemu-tests.sh
(pkgs.writeShellScriptBin "gdb-multiarch" ''
exec ${lib.getBin pkgs.gdb}/bin/gdb "$@"
'')
pkgs.pkgsCross.aarch64-multiplatform.buildPackages.binutils
pkgs.pkgsCross.riscv64.buildPackages.binutils
pkgs.pkgsCross.mipsel-linux-gnu.buildPackages.binutils
(pkgs.writeShellScriptBin "aarch64-linux-gnu-gcc" ''
exec ${lib.getBin pkgs.pkgsCross.aarch64-multiplatform.buildPackages.gcc}/bin/aarch64-unknown-linux-gnu-gcc "$@"
'')
(pkgs.writeShellScriptBin "riscv64-linux-gnu-gcc" ''
exec ${lib.getBin pkgs.pkgsCross.riscv64.buildPackages.gcc}/bin/riscv64-unknown-linux-gnu-gcc "$@"
'')
pyEnv
])
++ pkgs.lib.optionals isLLDB (
with pkgs;
[
lldb_19
]
);
]
++ pkgs.lib.optionals isLLDB [
pkgs.lldb_19
];
shellHook = ''
export PWNDBG_VENV_PATH="PWNDBG_PLEASE_SKIP_VENV"
export ZIGPATH="${pkgs.lib.getBin pkgs.zig_0_10}/bin/"
export JEMALLOC_PATH="${jemalloc-static}/lib/libjemalloc.a"
'';
};
}

@ -192,6 +192,10 @@ class RTree:
def __init__(self, addr: int) -> None:
self._addr = addr
rtree_s = pwndbg.aglib.typeinfo.load("struct rtree_s")
if rtree_s is None:
raise pwndbg.dbg_mod.Error("rtree_s type not found")
# self._Value = pwndbg.aglib.memory.poi(emap_s, self._addr)
# self._Value = pwndbg.aglib.memory.fetch_struct_as_dictionary(
@ -203,14 +207,10 @@ class RTree:
self._extents = None
@staticmethod
def get_rtree() -> RTree | None:
try:
addr = pwndbg.dbg.selected_inferior().symbol_address_from_name("je_arena_emap_global")
if addr is None:
return None
except pwndbg.dbg_mod.Error:
return None
def get_rtree() -> RTree:
addr = pwndbg.dbg.selected_inferior().symbol_address_from_name("je_arena_emap_global")
if addr is None:
raise pwndbg.dbg_mod.Error("Required je_arena_emap_global symbol not found")
return RTree(addr)
@property
@ -261,9 +261,14 @@ class RTree:
- Jemalloc stores the extent address in the rtree as a node and to find a specific node we need a address key.
"""
rtree_node_elm_s = pwndbg.aglib.typeinfo.load("struct rtree_node_elm_s")
if rtree_node_elm_s is None:
raise pwndbg.dbg_mod.Error("rtree_node_elm_s type not found")
rtree_leaf_elm_s = pwndbg.aglib.typeinfo.load("struct rtree_leaf_elm_s")
if rtree_leaf_elm_s is None:
raise pwndbg.dbg_mod.Error("rtree_leaf_elm_s type not found")
# Credits: 盏一's jegdb
# https://web.archive.org/web/20221114090949/https://github.com/hidva/hidva.github.io/blob/dev/_drafts/jegdb.py
# For subkey 0
subkey = self.__subkey(key, 1)
@ -327,7 +332,11 @@ class RTree:
extent_addresses = []
rtree_node_elm_s = pwndbg.aglib.typeinfo.load("struct rtree_node_elm_s")
if rtree_node_elm_s is None:
raise pwndbg.dbg_mod.Error("rtree_node_elm_s type not found")
rtree_leaf_elm_s = pwndbg.aglib.typeinfo.load("struct rtree_leaf_elm_s")
if rtree_leaf_elm_s is None:
raise pwndbg.dbg_mod.Error("rtree_leaf_elm_s type not found")
max_subkeys = 1 << rtree_levels[RTREE_HEIGHT - 1][0]["bits"]
# print("max_subkeys: ", max_subkeys)

@ -1590,7 +1590,9 @@ def jemalloc_find_extent(addr) -> None:
rtree = jemalloc.RTree.get_rtree()
extent = rtree.lookup_hard(addr)
if extent is None:
print(message.error("ERROR: Extent not found"))
return
# print pointer address first, then extent address then extent information
print(f"Pointer Address: {hex(addr)}")
print(f"Extent Address: {hex(extent.extent_address)}")
@ -1639,6 +1641,9 @@ def jemalloc_heap() -> None:
rtree = jemalloc.RTree.get_rtree()
extents = rtree.extents
if len(extents) == 0:
print(message.warn("No extents found"))
return
for extent in extents:
# TODO: refactor so not create copies
jemalloc_extent_info(extent.extent_address, header=False)

@ -21,6 +21,10 @@ GO = go
SOURCES_GO = $(wildcard *.go)
COMPILED_GO = $(SOURCES_GO:.go=.x86) $(SOURCES_GO:.go=.x64)
ifndef JEMALLOC_PATH
JEMALLOC_PATH = /usr/local/lib/libjemalloc.a
endif
ifeq ($(TARGET), x86)
CFLAGS += -m32
endif
@ -95,11 +99,15 @@ heap_malloc_chunk.out: heap_malloc_chunk.c
heap_jemalloc_extent_info.out: heap_jemalloc_extent_info.c
@echo "[+] Building heap_jemalloc_extent_info.out"
${CC} -g -O0 -Wno-nonnull -Wno-unused-result -o heap_jemalloc_extent_info.out heap_jemalloc_extent_info.c -lpthread /usr/local/lib/libjemalloc.a -lm -lstdc++ -pthread -ldl
${CC} -g -O0 -Wno-nonnull -Wno-unused-result \
-o heap_jemalloc_extent_info.out heap_jemalloc_extent_info.c \
-lpthread ${JEMALLOC_PATH} -lm -lstdc++ -pthread -ldl
heap_jemalloc_heap.out: heap_jemalloc_heap.c
@echo "[+] Building heap_jemalloc_heap.out"
${CC} -g -O0 -Wno-nonnull -Wno-unused-result -o heap_jemalloc_heap.out heap_jemalloc_heap.c -lpthread /usr/local/lib/libjemalloc.a -lm -lstdc++ -pthread -ldl
${CC} -g -O0 -Wno-nonnull -Wno-unused-result \
-o heap_jemalloc_heap.out heap_jemalloc_heap.c \
-lpthread ${JEMALLOC_PATH} \-lm -lstdc++ -pthread -ldl
multiple_threads.out: multiple_threads.c

@ -547,12 +547,15 @@ def test_jemalloc_extent_info(start_binary):
gdb.execute("break break_here")
gdb.execute("continue")
EXPECTED_EXTENT_ADDRESS = 0x7FFFF7A16580
find_extent_results = gdb.execute("jemalloc_find_extent ptr", to_string=True).splitlines()
extent_address = None
for line in find_extent_results:
if "Extent Address:" in line:
extent_address = int(line.split(" ")[-1], 16)
if extent_address is None:
raise ValueError("Could not find extent address")
# run jemalloc extent_info command
result = gdb.execute(
f"jemalloc_extent_info {EXPECTED_EXTENT_ADDRESS}", to_string=True
).splitlines()
result = gdb.execute(f"jemalloc_extent_info {extent_address}", to_string=True).splitlines()
expected_output = [
"Jemalloc extent info",
@ -568,7 +571,6 @@ def test_jemalloc_extent_info(start_binary):
assert re.match(expected_output[i], result[i])
@pytest.mark.skip(reason="Output is resulting in duplicate extents")
def test_jemalloc_heap(start_binary):
start_binary(HEAP_JEMALLOC_HEAP)
gdb.execute("break break_here")
@ -582,14 +584,8 @@ def test_jemalloc_heap(start_binary):
"This command was tested only for jemalloc 5.3.0 and does not support lower versions",
]
expected_output += [
"",
"Allocated Address: " + re_match_valid_address,
r"Extent Address: " + re_match_valid_address,
"Size: 0x401000",
"Small class: False",
]
# Extent sizes different depending on the system built (it would seem), so only check for the 0x8000 size,
# since it seems consistent. The output of an extent implies the rest of the command is working
expected_output += [
"",
"Allocated Address: " + re_match_valid_address,
@ -598,21 +594,5 @@ def test_jemalloc_heap(start_binary):
"Small class: False",
]
expected_output += [
"",
"Allocated Address: " + re_match_valid_address,
r"Extent Address: " + re_match_valid_address,
"Size: 0x8000",
"Small class: False",
]
expected_output += [
"",
"Allocated Address: " + re_match_valid_address,
r"Extent Address: " + re_match_valid_address,
"Size: 0x1f7000",
"Small class: False",
]
for i in range(len(expected_output)):
assert re.match(expected_output[i], result[i])

Loading…
Cancel
Save