Skip to content

Commit

Permalink
zdtm: add support for LD_PRELOAD tests
Browse files Browse the repository at this point in the history
This commit adds a `--preload` option to ZDTM's run command.
This option runs CRIU with LD_PRELOAD to intercept libc functions
such as pread(). This method allows to simulate special cases,
for example, when a successful call to pread() transfers fewer
bytes than requested.

Signed-off-by: Radostin Stoyanov <rstoyanov@fedoraproject.org>
  • Loading branch information
rst0git committed May 23, 2024
1 parent c0e9ced commit f5789e3
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 2 deletions.
3 changes: 3 additions & 0 deletions scripts/ci/run-ci-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,9 @@ make -C test/others/rpc/ run

./test/zdtm.py run -t zdtm/static/env00 --sibling

./test/zdtm.py run -t zdtm/static/maps00 --preload
./test/zdtm.py run -t zdtm/static/maps02 --preload

./test/zdtm.py run -t zdtm/transition/maps007 --pre 2 --dedup
./test/zdtm.py run -t zdtm/transition/maps007 --pre 2 --noauto-dedup
./test/zdtm.py run -t zdtm/transition/maps007 --pre 2 --page-server
Expand Down
21 changes: 21 additions & 0 deletions test/ld-preload/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
CC = gcc
CFLAGS = -c -fPIC -ldl

SRC = libinterceptor.c
OBJ = $(SRC:.c=.o)

LIB = libinterceptor.so

.PHONY: all clean run

all: $(LIB)

$(LIB): $(OBJ)
$(CC) -shared -o $(LIB) $(OBJ)

$(OBJ): $(SRC)
$(CC) $(CFLAGS) $<

clean:
rm -f $(OBJ) $(LIB)

25 changes: 25 additions & 0 deletions test/ld-preload/libinterceptor.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#define _GNU_SOURCE
#include <unistd.h>
#include <dlfcn.h>
#include <errno.h>

ssize_t (*original_pread)(int fd, void *buf, size_t count, off_t offset) = NULL;

ssize_t pread64(int fd, void *buf, size_t count, off_t offset)
{
if (!original_pread) {
original_pread = dlsym(RTLD_NEXT, "pread");
if (!original_pread) {
errno = EIO;
return -1;
}
}

/* The following aims to simulate the case when pread() returns less
* data than requested. We need to ensure that CRIU handles such cases. */
if (count > 2048) {
count -= 1024;
}

return original_pread(fd, buf, count, offset);
}
21 changes: 19 additions & 2 deletions test/zdtm.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,13 @@
# File to store content of streamed images
STREAMED_IMG_FILE_NAME = "img.criu"

# Interceptor library used to preload C functions
LIBINTERCEPTOR_PATH = os.path.join(
os.path.dirname(os.path.abspath(__file__)),
"ld-preload",
"libinterceptor.so"
)

prev_line = None
uuid = uuid.uuid4()

Expand Down Expand Up @@ -628,6 +635,8 @@ def available():
["make", "zdtm_ct"], env=dict(os.environ, MAKEFLAGS=""))
if not os.access("zdtm/lib/libzdtmtst.a", os.F_OK):
subprocess.check_call(["make", "-C", "zdtm/"])
if 'preload' in opts and opts['preload']:
subprocess.check_call(["make", "-C", "ld-preload/"])
if 'rootless' in opts and opts['rootless']:
return
subprocess.check_call(
Expand Down Expand Up @@ -880,6 +889,7 @@ def run(action,
fault=None,
strace=[],
preexec=None,
preload=False,
nowait=False,
timeout=60):
env = dict(
Expand All @@ -890,6 +900,9 @@ def run(action,
print("Forcing %s fault" % fault)
env['CRIU_FAULT'] = fault

if preload:
env['LD_PRELOAD'] = LIBINTERCEPTOR_PATH

cr = subprocess.Popen(strace +
[criu_bin, action, "--no-default-config"] + args,
env=env,
Expand Down Expand Up @@ -1065,6 +1078,7 @@ def __init__(self, opts):
self.__criu_bin = opts['criu_bin']
self.__crit_bin = opts['crit_bin']
self.__pre_dump_mode = opts['pre_dump_mode']
self.__preload = bool(opts['preload'])
self.__mntns_compat_mode = bool(opts['mntns_compat_mode'])

if opts['rpc']:
Expand Down Expand Up @@ -1192,8 +1206,10 @@ def __criu_act(self, action, opts=[], log=None, nowait=False):
with open("/proc/sys/kernel/ns_last_pid") as ns_last_pid_fd:
ns_last_pid = ns_last_pid_fd.read()

preload = self.__preload and action in ['dump', 'pre-dump', 'restore']

ret = self.__criu.run(action, s_args, self.__criu_bin, self.__fault,
strace, preexec, nowait)
strace, preexec, preload, nowait)

if nowait:
os.close(status_fds[1])
Expand Down Expand Up @@ -2083,7 +2099,7 @@ def run_test(self, name, desc, flavor):
'dedup', 'sbs', 'freezecg', 'user', 'dry_run', 'noauto_dedup',
'remote_lazy_pages', 'show_stats', 'lazy_migrate', 'stream',
'tls', 'criu_bin', 'crit_bin', 'pre_dump_mode', 'mntns_compat_mode',
'rootless')
'rootless', 'preload')
arg = repr((name, desc, flavor, {d: self.__opts[d] for d in nd}))

if self.__use_log:
Expand Down Expand Up @@ -2788,6 +2804,7 @@ def get_cli_args():
help="Select tests for a shard <index> (0-based)")
rp.add_argument("--test-shard-count", type=int, default=0,
help="Specify how many shards are being run (0=sharding disabled; must be the same for all shards)")
rp.add_argument("--preload", action="store_true", help="Run criu with interceptor library using LD_PRELOAD")

lp = sp.add_parser("list", help="List tests")
lp.set_defaults(action=list_tests)
Expand Down

0 comments on commit f5789e3

Please sign in to comment.