Skip to content

Commit

Permalink
test: drop mocklibc
Browse files Browse the repository at this point in the history
Let's get rid of mocklibc and replace it with a simple combination of
mount & user namespaces + bind mount to replace the host's /etc with our
own version.

This means we don't $LD_PRELOAD the mocklibc DSO, but instead run each
unit test through a very simple python wrapper that sets up a temporary
user & mount namespace through the unshare() syscall,  gains "fake" root
using uid_map and gid_map, overmounts /etc in this new namespace (with
our own custom test files), and then executes the test binary itself.
Check user_namespaces(7) for more information about the namespace
shenanigans.
  • Loading branch information
mrc0mmand committed Jun 10, 2024
1 parent e89a5db commit 0f7943a
Show file tree
Hide file tree
Showing 13 changed files with 66 additions and 255 deletions.
3 changes: 1 addition & 2 deletions .github/workflows/ci.sh
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,7 @@ case "$PHASE" in
-Db_lundef=false \
"${COMMON_BUILD_OPTS[@]}"

# Note: we need to set verify_asan_link_order=0 as polkit LD_PRELOADs libmocklibc in unit tests
export ASAN_OPTIONS=strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1:verify_asan_link_order=0
export ASAN_OPTIONS=strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1
export UBSAN_OPTIONS=print_stacktrace=1:print_summary=1:halt_on_error=1

meson compile -C build -v
Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ permissions:
jobs:
build:
runs-on: ubuntu-latest
container: registry.fedoraproject.org/fedora:latest
container:
image: registry.fedoraproject.org/fedora:latest
options: "--privileged"
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-${{ matrix.phase }}
cancel-in-progress: true
Expand Down
12 changes: 0 additions & 12 deletions subprojects/mocklibc.wrap

This file was deleted.

Binary file removed subprojects/packagecache/mocklibc-1.0-2-wrap.zip
Binary file not shown.
Binary file removed subprojects/packagecache/mocklibc-1.0.tar.gz
Binary file not shown.
36 changes: 0 additions & 36 deletions subprojects/packagefiles/0001.patch

This file was deleted.

27 changes: 0 additions & 27 deletions subprojects/packagefiles/0002.patch

This file was deleted.

69 changes: 0 additions & 69 deletions subprojects/packagefiles/0003.patch

This file was deleted.

11 changes: 1 addition & 10 deletions test/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,8 @@ libpolkit_test_helper_dep = declare_dependency(
link_with: libpolkit_test_helper,
)

libmocklibc = subproject('mocklibc').get_variable('libmocklibc')

test_wrapper = find_program('wrapper.py')
test_data_dir = meson.current_source_dir() / 'data'
test_etc_dir = test_data_dir / 'etc'

test_env = environment()
test_env.set('LD_PRELOAD', libmocklibc.full_path())
test_env.set('MOCK_PASSWD', test_etc_dir / 'passwd')
test_env.set('MOCK_GROUP', test_etc_dir / 'group')
test_env.set('MOCK_NETGROUP', test_etc_dir / 'netgroup')
test_env.set('POLKIT_TEST_DATA', test_data_dir)

subdir('polkit')
if not get_option('libs-only')
Expand Down
6 changes: 3 additions & 3 deletions test/polkit/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ foreach test_unit: test_units

test(
test_unit,
exe,
env: test_env,
is_parallel: false,
test_wrapper,
args: ['--data-dir', test_data_dir, exe.full_path()],
timeout: 30,
)
endforeach
22 changes: 6 additions & 16 deletions test/polkitbackend/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,6 @@ c_flags = [
'-D_POLKIT_BACKEND_COMPILATION',
]

test_env.set('TOP_BUILD_DIR', build_root)
test_env.set('TOP_SRC_DIR', source_root)

exe = executable(
test_unit,
test_unit + '.c',
Expand All @@ -22,16 +19,9 @@ exe = executable(
link_with: libpolkit_backend,
)

prog = find_program('polkitbackendjsauthoritytest-wrapper.py')

if not get_option('b_sanitize').split(',').contains('address')
test(
test_unit,
prog,
env: test_env,
is_parallel: false,
timeout: 90,
)
else
warning('@0@ is not (yet) compatible with AddressSanitizer, skipping'.format(test_unit))
endif
test(
test_unit,
test_wrapper,
args: ['--data-dir', test_data_dir, '--mock-dbus', exe.full_path()],
timeout: 90,
)
79 changes: 0 additions & 79 deletions test/polkitbackend/polkitbackendjsauthoritytest-wrapper.py

This file was deleted.

52 changes: 52 additions & 0 deletions test/wrapper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#!/usr/bin/env python3

import argparse
import atexit
import os
import subprocess
import sys

import dbus
import dbus.mainloop.glib
import dbusmock


def setup_test_namespace(data_dir):
print(f"Test data dir: {data_dir}")

# Setup a new mount & user namespace, so we can use mount() unprivileged (see user_namespaces(7))
euid = os.geteuid()
egid = os.getegid()
os.unshare(os.CLONE_NEWNS|os.CLONE_NEWUSER)
# Map root to the original EUID and EGID, so we can actually call mount() inside our namespace
with open("/proc/self/uid_map", "w") as f:
f.write(f"0 {euid} 1")
with open("/proc/self/setgroups", "w") as f:
f.write("deny")
with open("/proc/self/gid_map", "w") as f:
f.write(f"0 {egid} 1")

# Overmount /etc with our own version
subprocess.check_call(["mount", "--bind", os.path.join(data_dir, "etc"), "/etc"])

if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("test_executable",
help="test executable to run in our own test namespace")
parser.add_argument("--data-dir", type=str, required=True,
help="path to test data directory (with our own /etc/{passwd,group,...} files)")
parser.add_argument("--mock-dbus", action="store_true",
help="set up a mock system D-Bus using dbusmock")
args = parser.parse_args()

setup_test_namespace(args.data_dir)

if args.mock_dbus:
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
dbusmock.DBusTestCase.start_system_bus()
atexit.register(dbusmock.DBusTestCase.stop_dbus, dbusmock.DBusTestCase.system_bus_pid)

print(f"Executing '{args.test_executable}'")
sys.stdout.flush()
os.environ["POLKIT_TEST_DATA"] = args.data_dir
subprocess.check_call(args.test_executable, shell=True)

0 comments on commit 0f7943a

Please sign in to comment.