Signed-off-by: fu.lin fulin10@huawei.com --- test/zdtm.py | 68 ++- test/zdtm/customization/Makefile | 23 +- test/zdtm/customization/get_smaps_bits.c | 127 +++++ test/zdtm/customization/get_smaps_bits.h | 6 + test/zdtm/customization/ipc.desc | 2 +- test/zdtm/customization/maps00.c | 271 +++++++++++ test/zdtm/customization/maps00.desc | 1 + test/zdtm/customization/maps007.c | 178 +++++++ test/zdtm/customization/maps007.desc | 1 + test/zdtm/customization/maps008.c | 514 ++++++++++++++++++++ test/zdtm/customization/maps008.desc | 1 + test/zdtm/customization/maps01.c | 183 +++++++ test/zdtm/customization/maps01.desc | 1 + test/zdtm/customization/maps02.c | 111 +++++ test/zdtm/customization/maps02.desc | 1 + test/zdtm/customization/maps04.c | 57 +++ test/zdtm/customization/maps04.desc | 1 + test/zdtm/customization/maps05.c | 91 ++++ test/zdtm/customization/maps05.desc | 1 + test/zdtm/customization/maps06.c | 70 +++ test/zdtm/customization/maps06.desc | 1 + test/zdtm/customization/maps_file_prot.c | 53 ++ test/zdtm/customization/maps_file_prot.desc | 1 + test/zdtm_ct.c | 13 +- 24 files changed, 1765 insertions(+), 11 deletions(-) create mode 100644 test/zdtm/customization/get_smaps_bits.c create mode 100644 test/zdtm/customization/get_smaps_bits.h create mode 100644 test/zdtm/customization/maps00.c create mode 100644 test/zdtm/customization/maps00.desc create mode 100644 test/zdtm/customization/maps007.c create mode 100644 test/zdtm/customization/maps007.desc create mode 100644 test/zdtm/customization/maps008.c create mode 100644 test/zdtm/customization/maps008.desc create mode 100644 test/zdtm/customization/maps01.c create mode 100644 test/zdtm/customization/maps01.desc create mode 100644 test/zdtm/customization/maps02.c create mode 100644 test/zdtm/customization/maps02.desc create mode 100644 test/zdtm/customization/maps04.c create mode 100644 test/zdtm/customization/maps04.desc create mode 100644 test/zdtm/customization/maps05.c create mode 100644 test/zdtm/customization/maps05.desc create mode 100644 test/zdtm/customization/maps06.c create mode 100644 test/zdtm/customization/maps06.desc create mode 100644 test/zdtm/customization/maps_file_prot.c create mode 100644 test/zdtm/customization/maps_file_prot.desc
diff --git a/test/zdtm.py b/test/zdtm.py index 73de5ac..111f9f1 100755 --- a/test/zdtm.py +++ b/test/zdtm.py @@ -366,6 +366,9 @@ def test_flag(tdesc, flag): return flag in tdesc.get('flags', '').split()
+def test_value(tdesc, opt, val): + return val in tdesc.get(opt, '').split() + # # Exception thrown when something inside the test goes wrong, # e.g. test doesn't start, criu returns with non zero code or @@ -947,7 +950,8 @@ class criu_rpc: inhfd.fd = int(fd[3:-1]) inhfd.key = key else: - raise test_fail_exc('RPC for %s(%s) required' % (arg, args.pop(0))) + raise test_fail_exc('RPC for %s(%s) required' % + (arg, args.pop(0)))
@staticmethod def run(action, @@ -1438,6 +1442,24 @@ class criu: "check", ["--no-default-config", "-v0", "--feature", feature], opts['criu_bin']) == 0
+ @staticmethod + def check_cmdline(cmdline): + with open("/proc/cmdline") as f: + bootparams = f.readline().strip().split() + + for arg in cmdline.split(): + words = [word.strip("'" ") for word in arg.split('=')] + matched = False + for param in bootparams: + prefix = param.startswith(words[0]) + if (len(words) == 1 and prefix) \ + or (len(words) == 2 and prefix and param[len(words[0])+1:] == words[1]): + matched = True + break + if not matched: + return True + return False + @staticmethod def available(): if not os.access(opts['criu_bin'], os.X_OK): @@ -1509,6 +1531,11 @@ def cr(cr_api, test, opts):
iters = iter_parm(opts['iters'], 1) for i in iters[0]: + if "--pin-memory" in test.getdopts(): + print("Clear pin memory space") + cmd = [opts["criu_bin"], "clear-pin-memory"] + subprocess.run(cmd, shell=False, check=True) + pres = iter_parm(opts['pre'], 0) for p in pres[0]: if opts['snaps']: @@ -1956,6 +1983,18 @@ class Launcher: testline = u"ok %d - %s # SKIP %s" % (self.__runtest, name, reason) print(testline, file=self.__file_report)
+ def modprobe_pin_memory(self, load): + if not load: + return + else: + found = False + with open("/proc/modules") as f: + for line in f.readlines(): + if "pin_memory" == line.split()[0]: + found = True + if not found: + subprocess.check_call(["modprobe", "pin_memory"]) + def run_test(self, name, desc, flavor):
if len(self.__subs) >= self.__max: @@ -1963,7 +2002,8 @@ class Launcher:
with open("/proc/sys/kernel/tainted") as taintfd: taint = taintfd.read() - if self.__taint != taint: + # 0x1000 means the out of tree module has been loaded + if self.__taint != taint and (int(self.__taint) | 0x1000) != int(taint): raise Exception("The kernel is tainted: %r (%r)" % (taint, self.__taint))
@@ -1988,8 +2028,15 @@ class Launcher: logf = None log = None
+ no_pid_ns = test_value(desc, 'opts', '--use-fork-pid') + zdtm_no_pid_ns = "1" if no_pid_ns else "0" + # load `pin_memory.ko`,`--pin-memory` option must be used with + # `--use-fork-pid`, so don't care `--pin-memory` option + self.modprobe_pin_memory(no_pid_ns) + sub = subprocess.Popen(["./zdtm_ct", "zdtm.py"], - env=dict(os.environ, CR_CT_TEST_INFO=arg), + env=dict(os.environ, CR_CT_TEST_INFO=arg, + ZDTM_NO_PID_NS=zdtm_no_pid_ns), stdout=log, stderr=subprocess.STDOUT, close_fds=True) @@ -2000,7 +2047,8 @@ class Launcher: "start": time.time() }
- if test_flag(desc, 'excl'): + # pin memory function don't support concurrency + if test_flag(desc, 'excl') or test_value(desc, "opts", "--pin-memory"): self.wait()
def __wait_one(self, flags): @@ -2336,6 +2384,12 @@ def run_tests(opts): launcher.skip(t, "remote lazy pages are not supported") continue
+ cmdline = tdesc.get('cmdline', '') + if cmdline and criu.check_cmdline(cmdline): + launcher.skip( + t, f"cmdline '{cmdline}' isn't support, or don't set") + continue + test_flavs = tdesc.get('flavor', 'h ns uns').split() opts_flavs = (opts['flavor'] or 'h,ns,uns').split(',') if opts_flavs != ['best']: @@ -2365,6 +2419,7 @@ def run_tests(opts): if fail: sys.exit(1)
+ sti_fmt = "%-40s%-10s%s"
@@ -2644,8 +2699,8 @@ rp.add_argument("--pre-dump-mode", choices=['splice', 'read'], default='splice') rp.add_argument("--kdat", - help="Path to criu.kdat, default '/run/criu.kdat'", - default="/run/criu.kdat") + help="Path to criu.kdat, default '/run/criu.kdat'", + default="/run/criu.kdat")
lp = sp.add_parser("list", help="List tests") lp.set_defaults(action=list_tests) @@ -2680,6 +2735,7 @@ if opts['action'] == 'run': kdat = pathlib.Path(opts['kdat']) if kdat.exists(): kdat.unlink() + for tst in test_classes.values(): tst.available()
diff --git a/test/zdtm/customization/Makefile b/test/zdtm/customization/Makefile index 563b7b1..82348f2 100644 --- a/test/zdtm/customization/Makefile +++ b/test/zdtm/customization/Makefile @@ -3,9 +3,21 @@ LIB := $(LIBDIR)/libzdtmtst.a LDLIBS += $(LIB) CPPFLAGS += -I$(LIBDIR)
-TST = \ - ipc +TST_NOFILE = \ + ipc \ + maps01 \ + maps02 \ + maps04 \ + maps05 \ + maps007 \ + maps008
+TST_FILE = \ + maps00 \ + maps06 \ + maps_file_prot + +TST = $(TST_NOFILE) $(TST_FILE) SRC = $(TST:%=%.c) OBJ = $(SRC:%.c=%.o) DEP = $(SRC:%.c=%.d) @@ -18,9 +30,12 @@ all: $(TST) install: all .PHONY: all install
-$(TST:%=%.pid): %.pid: % +$(TST_NOFILE:%=%.pid): %.pid: % $(<D)/$(<F) --pidfile=$@ --outfile=$<.out
+$(TST_FILE:%=%.pid): %.pid: % + $(<D)/$(<F) --pidfile=$@ --outfile=$<.out --filename=$<.test + %.out: %.pid % -kill -TERM `cat $<`
@@ -43,6 +58,8 @@ wait_stop:
$(TST): | $(LIB)
+maps02: get_smaps_bits.o + %: %.sh cp $< $@ chmod +x $@ diff --git a/test/zdtm/customization/get_smaps_bits.c b/test/zdtm/customization/get_smaps_bits.c new file mode 100644 index 0000000..9253f4d --- /dev/null +++ b/test/zdtm/customization/get_smaps_bits.c @@ -0,0 +1,127 @@ +#include <string.h> +#include <sys/mman.h> +#include "zdtmtst.h" + +#ifndef MAP_HUGETLB +# define MAP_HUGETLB 0x40000 +#endif + +#ifndef MADV_HUGEPAGE +# define MADV_HUGEPAGE 14 +#endif + +#ifndef MADV_NOHUGEPAGE +# define MADV_NOHUGEPAGE 15 +#endif + +#ifndef MADV_DONTDUMP +# define MADV_DONTDUMP 16 +#endif + +static void parse_vmflags(char *buf, unsigned long *flags, unsigned long *madv) +{ + char *tok; + + if (!buf[0]) + return; + + tok = strtok(buf, " \n"); + if (!tok) + return; + +#define _vmflag_match(_t, _s) (_t[0] == _s[0] && _t[1] == _s[1]) + + do { + /* mmap() block */ + if (_vmflag_match(tok, "gd")) + *flags |= MAP_GROWSDOWN; + else if (_vmflag_match(tok, "lo")) + *flags |= MAP_LOCKED; + else if (_vmflag_match(tok, "nr")) + *flags |= MAP_NORESERVE; + else if (_vmflag_match(tok, "ht")) + *flags |= MAP_HUGETLB; + + /* madvise() block */ + if (_vmflag_match(tok, "sr")) + *madv |= (1ul << MADV_SEQUENTIAL); + else if (_vmflag_match(tok, "rr")) + *madv |= (1ul << MADV_RANDOM); + else if (_vmflag_match(tok, "dc")) + *madv |= (1ul << MADV_DONTFORK); + else if (_vmflag_match(tok, "dd")) + *madv |= (1ul << MADV_DONTDUMP); + else if (_vmflag_match(tok, "mg")) + *madv |= (1ul << MADV_MERGEABLE); + else if (_vmflag_match(tok, "hg")) + *madv |= (1ul << MADV_HUGEPAGE); + else if (_vmflag_match(tok, "nh")) + *madv |= (1ul << MADV_NOHUGEPAGE); + + /* + * Anything else is just ignored. + */ + } while ((tok = strtok(NULL, " \n"))); + +#undef _vmflag_match +} + +#define is_hex_digit(c) \ + (((c) >= '0' && (c) <= '9') || \ + ((c) >= 'a' && (c) <= 'f') || \ + ((c) >= 'A' && (c) <= 'F')) + +static int is_vma_range_fmt(char *line, unsigned long *start, unsigned long *end) +{ + char *p = line; + while (*line && is_hex_digit(*line)) + line++; + + if (*line++ != '-') + return 0; + + while (*line && is_hex_digit(*line)) + line++; + + if (*line++ != ' ') + return 0; + + sscanf(p, "%lx-%lx", start, end); + return 1; +} + +int get_smaps_bits(unsigned long where, unsigned long *flags, unsigned long *madv) +{ + unsigned long start = 0, end = 0; + FILE *smaps = NULL; + char buf[1024]; + int found = 0; + + if (!where) + return 0; + + smaps = fopen("/proc/self/smaps", "r"); + if (!smaps) { + pr_perror("Can't open smaps"); + return -1; + } + + while (fgets(buf, sizeof(buf), smaps)) { + is_vma_range_fmt(buf, &start, &end); + + if (!strncmp(buf, "VmFlags: ", 9) && start == where) { + found = 1; + parse_vmflags(buf, flags, madv); + break; + } + } + + fclose(smaps); + + if (!found) { + pr_perror("VmFlags not found for %lx", where); + return -1; + } + + return 0; +} diff --git a/test/zdtm/customization/get_smaps_bits.h b/test/zdtm/customization/get_smaps_bits.h new file mode 100644 index 0000000..ce1070d --- /dev/null +++ b/test/zdtm/customization/get_smaps_bits.h @@ -0,0 +1,6 @@ +#ifndef ZDTM_GET_SMAPS_BITS_H_ +#define ZDTM_GET_SMAPS_BITS_H_ + +extern int get_smaps_bits(unsigned long where, unsigned long *flags, unsigned long *madv); + +#endif /* ZDTM_GET_SMAPS_BITS_H_ */ diff --git a/test/zdtm/customization/ipc.desc b/test/zdtm/customization/ipc.desc index 63df42a..4c127a0 100644 --- a/test/zdtm/customization/ipc.desc +++ b/test/zdtm/customization/ipc.desc @@ -1 +1 @@ -{'flavor': 'h'} +{'arch': 'aarch64', 'flavor': 'h'} diff --git a/test/zdtm/customization/maps00.c b/test/zdtm/customization/maps00.c new file mode 100644 index 0000000..83533f8 --- /dev/null +++ b/test/zdtm/customization/maps00.c @@ -0,0 +1,271 @@ +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <signal.h> +#include <string.h> +#include <sys/mman.h> +#include <setjmp.h> +#include <sys/types.h> +#include <sys/stat.h> +#include "zdtmtst.h" + +const char *test_doc = "Create all sorts of maps and compare /proc/pid/maps\n" + "before and after migration\n"; +const char *test_author = "Pavel Emelianov xemul@parallels.com"; + +char *filename; +TEST_OPTION(filename, string, "file name", 1); + +const static int map_prots[] = { + PROT_NONE, + PROT_READ, + PROT_READ | PROT_WRITE, + PROT_READ | PROT_WRITE | PROT_EXEC, +}; +#define NUM_MPROTS sizeof(map_prots) / sizeof(int) +#define RW_PROT(x) ((x) & (PROT_READ | PROT_WRITE)) +#define X_PROT(x) ((x) & PROT_EXEC) + +int check_prot(int src_prot, int dst_prot) +{ + if (RW_PROT(src_prot) != RW_PROT(dst_prot)) + return 0; + /* If exec bit will be enabled may depend on NX capability of CPUs of + * source and destination nodes. In any case, migrated mapping should + * not have less permissions than newly created one + ** + * A is a subset of B iff (A & B) == A + */ + return (X_PROT(dst_prot) & X_PROT(src_prot)) == X_PROT(dst_prot); +} + +const static int map_flags[] = { + MAP_PRIVATE, + MAP_SHARED, + MAP_PRIVATE | MAP_ANONYMOUS, + MAP_SHARED | MAP_ANONYMOUS +}; +#define NUM_MFLAGS sizeof(map_flags) / sizeof(int) +#define NUM_MAPS NUM_MPROTS * NUM_MFLAGS +#define ONE_MAP_SIZE 0x2000 + +struct map +{ + int prot; + int prot_real; + int flag; + char filename[256]; + int fd; + void *ptr; +}; + +static void init_map(struct map *map, int prot_no, int flag_no) +{ + map->fd = -1; + map->prot = map_prots[prot_no]; + map->flag = map_flags[flag_no]; +} + +static int make_map(struct map *map) +{ + uint32_t crc; + uint8_t buf[ONE_MAP_SIZE]; + static int i = 0; + + if (!(map->flag & MAP_ANONYMOUS)) { + /* need file */ + if (snprintf(map->filename, sizeof(map->filename), + "%s-%02d", filename, i++) >= sizeof(map->filename)) { + pr_perror("filename %s is too long", filename); + return -1; + } + + map->fd = open(map->filename, O_RDWR | O_CREAT, 0600); + if (map->fd < 0) { + pr_perror("can't open %s", map->filename); + return -1; + } + + crc = ~0; + datagen(buf, sizeof(buf), &crc); + if (write(map->fd, buf, sizeof(buf)) != sizeof(buf)) { + pr_perror("failed to write %s", map->filename); + return -1; + } + } + + map->ptr = mmap(NULL, ONE_MAP_SIZE, map->prot, map->flag, map->fd, 0); + if (map->ptr == MAP_FAILED) { + pr_perror("can't create mapping"); + return -1; + } + + if ((map->flag & MAP_ANONYMOUS) && (map->prot & PROT_WRITE)) { + /* can't fill it with data otherwise */ + crc = ~0; + datagen(map->ptr, ONE_MAP_SIZE, &crc); + } + + test_msg("map: ptr %p flag %8x prot %8x\n", + map->ptr, map->flag, map->prot); + + return 0; +} + +static sigjmp_buf segv_ret; /* we need sig*jmp stuff, otherwise SIGSEGV will reset our handler */ +static void segfault(int signo) +{ + siglongjmp(segv_ret, 1); +} + +/* + * after test func should be placed check map, because size of test_func + * is calculated as (check_map-test_func) + */ +int test_func(void) +{ + return 1; +} +static int check_map(struct map *map) +{ + int prot = PROT_WRITE | PROT_READ | PROT_EXEC; + + if (signal(SIGSEGV, segfault) == SIG_ERR) + { + fail("setting SIGSEGV handler failed: %m\n"); + return -1; + } + if (!sigsetjmp(segv_ret, 1)) + { + uint32_t crc = ~0; + if (datachk(map->ptr, ONE_MAP_SIZE, &crc)) /* perform read access */ + if (!(map->flag & MAP_ANONYMOUS) || + (map->prot & PROT_WRITE)) { /* anon maps could only be filled when r/w */ + fail("CRC mismatch: ptr %p flag %8x prot %8x\n", + map->ptr, map->flag, map->prot); + return -1; + } + /* prot |= PROT_READ// need barrier before this line, + because compiler change order commands. + I finded one method: look at next lines*/ + } else + prot &= PROT_WRITE | !PROT_READ | PROT_EXEC; + + if (signal(SIGSEGV, segfault) == SIG_ERR) + { + fail("setting SIGSEGV handler failed: %m\n"); + return -1; + } + + if (!sigsetjmp(segv_ret, 1)) + { + * (int *) (map->ptr) = 1234; /* perform write access */ + } else + prot &= !PROT_WRITE | PROT_READ | PROT_EXEC; + + if (signal(SIGSEGV, segfault) == SIG_ERR) + { + fail("restoring SIGSEGV handler failed: %m\n"); + return -1; + } + + if (!sigsetjmp(segv_ret, 1)) + { + if (map->prot & PROT_WRITE) { + memcpy(map->ptr,test_func, ONE_MAP_SIZE); + __builtin___clear_cache(map->ptr, map->ptr+ONE_MAP_SIZE); + } else { + if (!(map->flag & MAP_ANONYMOUS)) { + uint8_t funlen = (uint8_t *)check_map - (uint8_t *)test_func; + lseek(map->fd,0,SEEK_SET); + if (write(map->fd,test_func,funlen)<funlen) { + pr_perror("failed to write %s", map->filename); + return -1; + } + } + } + if (!(map->flag & MAP_ANONYMOUS) || (map->prot & PROT_WRITE)) { + /* Function body has been copied into the mapping */ + ((int (*)(void))map->ptr)(); /* perform exec access */ + } else { + /* No way to copy function body into mapping, + * clear exec bit from effective protection + */ + prot &= PROT_WRITE | PROT_READ | !PROT_EXEC; + } + } else + prot &= PROT_WRITE | PROT_READ | !PROT_EXEC; + + if (signal(SIGSEGV, SIG_DFL) == SIG_ERR) + { + fail("restoring SIGSEGV handler failed: %m\n"); + return -1; + } + + return prot; +} + +static void destroy_map(struct map *map) +{ + munmap(map->ptr, ONE_MAP_SIZE); + + if (map->fd >= 0) + { + close(map->fd); + unlink(map->filename); + } +} + + +#define MAPS_LEN 0x10000 + +int main(int argc, char ** argv) +{ + struct map maps[NUM_MAPS] = {}, maps_compare[NUM_MAPS] = {}; + int i, j, k; + test_init(argc, argv); + + k = 0; + for (i = 0; i < NUM_MPROTS; i++) + for (j = 0; j < NUM_MFLAGS; j++) + init_map(maps + k++, i, j); + + for (i = 0; i < NUM_MAPS; i++) + if (make_map(maps + i)) + goto err; + + test_daemon(); + test_waitsig(); + + for (i = 0; i < NUM_MAPS; i++) + if ((maps[i].prot_real=check_map(maps + i))<0) + goto err; + k=0; + for (i = 0; i < NUM_MPROTS; i++) + for (j = 0; j < NUM_MFLAGS; j++) + init_map(maps_compare + k++, i, j); + for (i = 0; i < NUM_MAPS; i++) + if (make_map(maps_compare+ i)) + goto err; + for (i = 0; i < NUM_MAPS; i++) + if ((maps_compare[i].prot_real=check_map(maps_compare + i))<0) + goto err; + for (i = 0; i< NUM_MAPS; i++) + if (!check_prot(maps[i].prot_real, maps_compare[i].prot_real)){ + fail("protection on %i (flag=%d prot=%d) maps has changed (prot=%d(expected %d))", + i, maps[i].flag, maps[i].prot, maps[i].prot_real, maps_compare[i].prot_real); + goto err; + } + + pass(); + + for (i = 0; i < NUM_MAPS; i++) { + destroy_map(maps + i); + destroy_map(maps_compare + i); + } + return 0; + +err: + return 1; +} diff --git a/test/zdtm/customization/maps00.desc b/test/zdtm/customization/maps00.desc new file mode 100644 index 0000000..dad462e --- /dev/null +++ b/test/zdtm/customization/maps00.desc @@ -0,0 +1 @@ +{'arch': 'aarch64', 'flavor': 'h', 'opts': '--pin-memory --use-fork-pid', 'flags': 'suid', 'cmdline': 'pinmemory max_pin_pid_num'} diff --git a/test/zdtm/customization/maps007.c b/test/zdtm/customization/maps007.c new file mode 100644 index 0000000..ee5e7c7 --- /dev/null +++ b/test/zdtm/customization/maps007.c @@ -0,0 +1,178 @@ + +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <sys/mman.h> +#include <signal.h> +#include <sys/wait.h> +#include <sys/uio.h> +#include <asm/unistd.h> + +#include "zdtmtst.h" +#include "lock.h" + +#define MAP_SIZE (1UL << 20) +#define MEM_SIZE (1UL << 29) + +const char *test_doc = "create random mappings and touch memory"; + +int sys_process_vm_readv(pid_t pid, void *addr, void *buf, int size) +{ + struct iovec lvec = {.iov_base = buf, .iov_len = size }; + struct iovec rvec = {.iov_base = addr, .iov_len = size }; + /* workaround bug in glibc with sixth argument of syscall */ + char nop[PAGE_SIZE]; + + memset(nop, 0, sizeof(nop)); + + return syscall(__NR_process_vm_readv, pid, &lvec, 1, &rvec, 1, 0); +} + +/* The child follows the parents two steps behind. */ +#define MAX_DELTA 1000 +int main(int argc, char **argv) +{ + void *start, *end, *p; + pid_t child; + struct { + futex_t delta; + futex_t stop; + } *shm; + uint32_t v; + unsigned long long count = 0; + int i; + + test_init(argc, argv); + + /* shared memory for synchronization */ + shm = mmap(NULL, PAGE_SIZE, PROT_WRITE | PROT_READ, MAP_ANONYMOUS | MAP_SHARED, -1, 0); + if (shm == MAP_FAILED) + return -1; + + /* allocate workspace */ + start = mmap(NULL, MEM_SIZE, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (start == MAP_FAILED) + return -1; + + test_msg("%p-%p\n", start, start + MEM_SIZE); + + end = start + MEM_SIZE; + + v = 0; + futex_set(&shm->delta, v); + futex_set(&shm->stop, 0); + + child = fork(); + if (child < 0) { + pr_perror("fork"); + return 1; + } + + while (1) { + void *ret; + unsigned long size; + int prot = PROT_NONE; + + if (child) { + if (!test_go()) + break; + futex_wait_while_gt(&shm->delta, 2 * MAX_DELTA); + futex_inc_and_wake(&shm->delta); + } else { + if (!futex_get(&shm->stop)) + /* shm->delta must be always bigger than MAX_DELTA */ + futex_wait_while_lt(&shm->delta, MAX_DELTA + 2); + else if (count % 100 == 0) + test_msg("count %llu delta %d\n", + count, futex_get(&shm->delta)); /* heartbeat */ + + if (futex_get(&shm->stop) && atomic_get(&shm->delta.raw) == MAX_DELTA) + break; + futex_dec_and_wake(&shm->delta); + } + + count++; + if (child && count == MAX_DELTA + 1) + test_daemon(); + + p = start + ((lrand48() * PAGE_SIZE) % MEM_SIZE); + size = lrand48() * PAGE_SIZE; + size %= (end - p); + size %= MAP_SIZE; + if (size == 0) + size = PAGE_SIZE; + + if (lrand48() % 2) + prot |= PROT_READ; + if (lrand48() % 2) + prot |= PROT_EXEC; + if (lrand48() % 2) + prot |= PROT_WRITE; + + ret = mmap(p, size, prot, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0); + if (ret == MAP_FAILED) { + pr_perror("%p-%p", p, p + size); + goto err; + } + + if (!(prot & PROT_WRITE)) + continue; + + for (i = 0; i < lrand48() % 50; i++) { + char *t = p + (lrand48() * PAGE_SIZE) % (size); + t[0] = lrand48(); + } + } + test_msg("count %llu\n", count); + + if (child == 0) { + if (!test_go()) + pr_perror("unexpected state"); + futex_set_and_wake(&shm->stop, 2); + test_waitsig(); + return 0; + } else { + int readable = 0, status = -1; + + /* stop the child */ + futex_set(&shm->stop, 1); + futex_add_and_wake(&shm->delta, MAX_DELTA); + /* wait until the child will be in the same point */ + futex_wait_until(&shm->stop, 2); + + /* check that child and parent have the identical content of memory */ + for (p = start; p < end; p += PAGE_SIZE) { + char rbuf[PAGE_SIZE], lbuf[PAGE_SIZE]; + int rret, lret; + + lret = sys_process_vm_readv(getpid(), p, lbuf, PAGE_SIZE); + rret = sys_process_vm_readv(child, p, rbuf, PAGE_SIZE); + if (rret != lret) { + pr_perror("%p %d %d", p, lret, rret); + goto err; + } + if (lret < 0) + continue; + readable++; + if (memcmp(rbuf, lbuf, PAGE_SIZE)) { + pr_perror("%p", p); + goto err; + } + } + test_msg("readable %d\n", readable); + kill(child, SIGTERM); + wait(&status); + if (status != 0) { + pr_perror("Non-zero exit code: %d", status); + goto err; + } + pass(); + } + + return 0; +err: + kill(child, SIGSEGV); + *((volatile int *) 0) = 0; + return 1; +} diff --git a/test/zdtm/customization/maps007.desc b/test/zdtm/customization/maps007.desc new file mode 100644 index 0000000..9ed7e46 --- /dev/null +++ b/test/zdtm/customization/maps007.desc @@ -0,0 +1 @@ +{'arch': 'aarch64', 'opts': '--pin-memory --use-fork-pid', 'flags': 'suid', 'flavor': 'h', 'cmdline': 'pinmemory max_pin_pid_num'} diff --git a/test/zdtm/customization/maps008.c b/test/zdtm/customization/maps008.c new file mode 100644 index 0000000..7ed7c10 --- /dev/null +++ b/test/zdtm/customization/maps008.c @@ -0,0 +1,514 @@ +#include <stdlib.h> +#include <stdbool.h> +#include <string.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <signal.h> +#include <sys/mman.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <linux/limits.h> +#include "zdtmtst.h" +#include "lock.h" + +const char *test_doc = "ps tree with anon shared vmas for dedup"; + +/* + * 1. ps tree with non triavial anon shmem vmas is created first. + * 2. Each process gets its portion of shmem vmas. + * 3. Each process continuously datagens its portion until + * criu dump is finished. + * 4. Each process datachecks all its shmem portions after restore. + * 5. Contents of anon shmem vmas are checked for equality in + * different processes. + */ + +typedef int (*proc_func_t)(task_waiter_t *setup_waiter); + +static pid_t fork_and_setup(proc_func_t pfunc) +{ + task_waiter_t setup_waiter; + pid_t pid; + + task_waiter_init(&setup_waiter); + pid = test_fork(); + if (pid < 0) { + pr_perror("fork failed"); + exit(1); + } + + if (pid == 0) + exit(pfunc(&setup_waiter)); + + task_waiter_wait4(&setup_waiter, pid); + task_waiter_fini(&setup_waiter); + return pid; +} + +static void cont_and_wait_child(pid_t pid) +{ + int status; + + kill(pid, SIGTERM); + waitpid(pid, &status, 0); + if (WIFEXITED(status)) { + if (WEXITSTATUS(status)) + exit(WEXITSTATUS(status)); + } else + exit(1); +} + +static void *mmap_ashmem(size_t size) +{ + void *mem = mmap(NULL, size, PROT_WRITE | PROT_READ, + MAP_SHARED | MAP_ANONYMOUS, -1, 0); + if (mem == MAP_FAILED) { + pr_perror("Can't map shmem %zx", size); + exit(1); + } + return mem; +} + +static void *mmap_proc_mem(pid_t pid, unsigned long addr, + unsigned long size) +{ + int fd; + void *mem; + char path[PATH_MAX]; + + snprintf(path, PATH_MAX, "/proc/%d/map_files/%lx-%lx", + (int)pid, addr, addr + size); + fd = open(path, O_RDWR); + if (fd == -1) { + pr_perror("Can't open file %s", path); + exit(1); + } + + mem = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); + close(fd); + if (mem == MAP_FAILED) { + pr_perror("Can't map file %s", path); + exit(1); + } + return mem; +} + +static void check_mem_eq(void *addr1, size_t size1, void *addr2, size_t size2) +{ + unsigned long min_size = size1 < size2 ? size1 : size2; + + if (memcmp(addr1, addr2, min_size)) { + pr_err("Mem differs %lx %lx %lx", (unsigned long)addr1, + (unsigned long)addr2, min_size); + exit(1); + } +} + +static void xmunmap(void *map, size_t size) +{ + if (munmap(map, size)) { + pr_err("xmunmap"); + exit(1); + } +} + +static void chk_proc_mem_eq(pid_t pid1, void *addr1, unsigned long size1, + pid_t pid2, void *addr2, unsigned long size2) +{ + void *map1, *map2; + + map1 = mmap_proc_mem(pid1, (unsigned long)addr1, size1); + map2 = mmap_proc_mem(pid2, (unsigned long)addr2, size2); + check_mem_eq(map1, size1, map2, size2); + xmunmap(map1, size1); + xmunmap(map2, size2); +} + +/* + * ps tree: + * proc1_______________ + * | | | + * proc11___ proc12 proc13 + * | | | + * proc111 proc112 proc131 + */ +#define PROC_CNT 7 + +#define PROC1_PGIX 0 +#define PROC11_PGIX 1 +#define PROC12_PGIX 2 +#define PROC13_PGIX 3 +#define PROC111_PGIX 4 +#define PROC112_PGIX 5 +#define PROC131_PGIX 6 +#define ZERO_PGIX 7 +/* unused pgix: 8 */ +#define MEM_PERIOD (9 * PAGE_SIZE) + +struct pstree { + pid_t proc1; + pid_t proc11; + pid_t proc12; + pid_t proc13; + pid_t proc111; + pid_t proc112; + pid_t proc131; +}; +struct pstree *pstree; + +struct test_sync { + futex_t datagen; + futex_t datagen_exit_cnt; +}; +struct test_sync *test_sync; + +size_t mem1_size, mem2_size, mem3_size; +uint8_t *mem1, *mem2, *mem3; + +#define CRC_EPOCH_OFFSET (PAGE_SIZE - sizeof(uint32_t)) + +static void read_each_pg(volatile uint8_t *mem, size_t size, size_t off) +{ + if (!mem) + return; + + while (off < size) { + (mem + off)[0]; + off += MEM_PERIOD; + } +} + +void datagen_each_pg(uint8_t *mem, size_t size, size_t off, uint32_t crc_epoch) +{ + if (!mem) + return; + + while (futex_get(&test_sync->datagen) && (off < size)) { + uint32_t crc = crc_epoch; + + datagen(mem + off, CRC_EPOCH_OFFSET, &crc); + *(uint32_t *)(mem + off + CRC_EPOCH_OFFSET) = crc_epoch; + off += MEM_PERIOD; + } +} + +void datachck_each_pg(uint8_t *mem, size_t size, size_t off) +{ + if (!mem) + return; + + while (off < size) { + uint32_t crc = *(uint32_t *)(mem + off + CRC_EPOCH_OFFSET); + + if (datachk(mem + off, CRC_EPOCH_OFFSET, &crc)) + exit(1); + off += MEM_PERIOD; + } +} + +static void mems_read_each_pgix(size_t pgix) +{ + const size_t off = pgix * PAGE_SIZE; + + read_each_pg(mem1, mem1_size, off); + read_each_pg(mem2, mem2_size, off); + read_each_pg(mem3, mem3_size, off); +} + +static void mems_datagen_each_pgix(size_t pgix, uint32_t *crc_epoch) +{ + const size_t off = pgix * PAGE_SIZE; + + ++(*crc_epoch); + datagen_each_pg(mem1, mem1_size, off, *crc_epoch); + datagen_each_pg(mem2, mem2_size, off, *crc_epoch); + datagen_each_pg(mem3, mem3_size, off, *crc_epoch); +} + +static void mems_datachck_each_pgix(size_t pgix) +{ + const size_t off = pgix * PAGE_SIZE; + + datachck_each_pg(mem1, mem1_size, off); + datachck_each_pg(mem2, mem2_size, off); + datachck_each_pg(mem3, mem3_size, off); +} + +static int proc131_func(task_waiter_t *setup_waiter) +{ + uint32_t crc_epoch = 0; + + pstree->proc131 = getpid(); + mems_datagen_each_pgix(PROC131_PGIX, &crc_epoch); + task_waiter_complete_current(setup_waiter); + + while (futex_get(&test_sync->datagen)) + mems_datagen_each_pgix(PROC131_PGIX, &crc_epoch); + futex_inc_and_wake(&test_sync->datagen_exit_cnt); + test_waitsig(); + + mems_datachck_each_pgix(PROC131_PGIX); + return 0; +} + +static int proc13_func(task_waiter_t *setup_waiter) +{ + size_t MEM1_HOLE_START = 2 * MEM_PERIOD; + size_t MEM1_HOLE_SIZE = 1 * MEM_PERIOD; + uint32_t crc_epoch = 0; + + pstree->proc13 = getpid(); + xmunmap(mem1 + MEM1_HOLE_START, MEM1_HOLE_SIZE); + xmunmap(mem2, mem2_size); + xmunmap(mem3, mem3_size); + mem2 = mem1 + MEM1_HOLE_START + MEM1_HOLE_SIZE; + mem2_size = mem1_size - (mem2 - mem1); + mem1_size = MEM1_HOLE_START; + mem3 = mmap_ashmem(mem3_size); + mems_datagen_each_pgix(PROC13_PGIX, &crc_epoch); + fork_and_setup(proc131_func); + task_waiter_complete_current(setup_waiter); + + while (futex_get(&test_sync->datagen)) + mems_datagen_each_pgix(PROC13_PGIX, &crc_epoch); + futex_inc_and_wake(&test_sync->datagen_exit_cnt); + test_waitsig(); + + mems_datachck_each_pgix(PROC13_PGIX); + + chk_proc_mem_eq(pstree->proc13, mem1, mem1_size, + pstree->proc131, mem1, mem1_size); + chk_proc_mem_eq(pstree->proc13, mem2, mem2_size, + pstree->proc131, mem2, mem2_size); + chk_proc_mem_eq(pstree->proc13, mem3, mem3_size, + pstree->proc131, mem3, mem3_size); + + cont_and_wait_child(pstree->proc131); + return 0; +} + +static int proc12_func(task_waiter_t *setup_waiter) +{ + uint32_t crc_epoch = 0; + + pstree->proc12 = getpid(); + mems_datagen_each_pgix(PROC12_PGIX, &crc_epoch); + task_waiter_complete_current(setup_waiter); + + while (futex_get(&test_sync->datagen)) + mems_datagen_each_pgix(PROC12_PGIX, &crc_epoch); + futex_inc_and_wake(&test_sync->datagen_exit_cnt); + test_waitsig(); + + mems_datachck_each_pgix(PROC12_PGIX); + + return 0; +} + +static int proc111_func(task_waiter_t *setup_waiter) +{ + uint32_t crc_epoch = 0; + + pstree->proc111 = getpid(); + mems_datagen_each_pgix(PROC111_PGIX, &crc_epoch); + task_waiter_complete_current(setup_waiter); + + while (futex_get(&test_sync->datagen)) + mems_datagen_each_pgix(PROC111_PGIX, &crc_epoch); + futex_inc_and_wake(&test_sync->datagen_exit_cnt); + test_waitsig(); + + mems_datachck_each_pgix(PROC111_PGIX); + return 0; +} + +static int proc112_func(task_waiter_t *setup_waiter) +{ + uint32_t crc_epoch = 0; + + pstree->proc112 = getpid(); + mems_datagen_each_pgix(PROC112_PGIX, &crc_epoch); + task_waiter_complete_current(setup_waiter); + + while (futex_get(&test_sync->datagen)) + mems_datagen_each_pgix(PROC112_PGIX, &crc_epoch); + futex_inc_and_wake(&test_sync->datagen_exit_cnt); + test_waitsig(); + + mems_datachck_each_pgix(PROC112_PGIX); + return 0; +} + +static int proc11_func(task_waiter_t *setup_waiter) +{ + const size_t MEM3_START_CUT = 1 * MEM_PERIOD; + const size_t MEM3_END_CUT = 2 * MEM_PERIOD; + void *mem3_old = mem3; + size_t mem3_size_old = mem3_size; + uint32_t crc_epoch = 0; + uint8_t *proc1_mem3; + + pstree->proc11 = getpid(); + xmunmap(mem3, MEM3_START_CUT); + mem3 += MEM3_START_CUT; + mem3_size -= MEM3_START_CUT; + fork_and_setup(proc111_func); + fork_and_setup(proc112_func); + xmunmap(mem3 + mem3_size - MEM3_END_CUT, MEM3_END_CUT); + mem3_size -= MEM3_END_CUT; + mems_datagen_each_pgix(PROC11_PGIX, &crc_epoch); + task_waiter_complete_current(setup_waiter); + + while (futex_get(&test_sync->datagen)) + mems_datagen_each_pgix(PROC11_PGIX, &crc_epoch); + futex_inc_and_wake(&test_sync->datagen_exit_cnt); + test_waitsig(); + + mems_datachck_each_pgix(PROC11_PGIX); + + chk_proc_mem_eq(pstree->proc11, mem1, mem1_size, + pstree->proc111, mem1, mem1_size); + chk_proc_mem_eq(pstree->proc11, mem1, mem1_size, + pstree->proc112, mem1, mem1_size); + + chk_proc_mem_eq(pstree->proc11, mem2, mem2_size, + pstree->proc111, mem2, mem2_size); + chk_proc_mem_eq(pstree->proc11, mem2, mem2_size, + pstree->proc112, mem2, mem2_size); + + chk_proc_mem_eq(pstree->proc11, mem3, mem3_size, + pstree->proc111, mem3, mem3_size + MEM3_END_CUT); + chk_proc_mem_eq(pstree->proc11, mem3, mem3_size, + pstree->proc112, mem3, mem3_size + MEM3_END_CUT); + + proc1_mem3 = mmap_proc_mem(pstree->proc1, + (unsigned long)mem3_old, mem3_size_old); + check_mem_eq(mem3, mem3_size, proc1_mem3 + MEM3_START_CUT, mem3_size); + xmunmap(proc1_mem3, mem3_size_old); + + cont_and_wait_child(pstree->proc111); + cont_and_wait_child(pstree->proc112); + return 0; +} + +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#define MB(n) ((n) * (1UL << 20)) + +static int proc1_func(void) +{ + uint32_t crc_epoch = 0; + uint8_t *mem2_old = NULL; + + /* + * Min mem size: + * At least 5 mem periods for mem pages and vma holes. + * At least 1 MB mem size not to test on tiny working set. + */ + mem1_size = MEM_PERIOD * MAX(5, MB(1) / MEM_PERIOD + 1); + mem2_size = mem1_size * 2; + mem3_size = mem2_size * 3; + + futex_set(&test_sync->datagen, 1); + pstree->proc1 = getpid(); + mem1 = mmap_ashmem(mem1_size); + mem2 = mmap_ashmem(mem2_size); + mem3 = mmap_ashmem(mem3_size); + mems_datagen_each_pgix(PROC1_PGIX, &crc_epoch); + mems_read_each_pgix(ZERO_PGIX); + + fork_and_setup(proc11_func); + fork_and_setup(proc12_func); + fork_and_setup(proc13_func); + + xmunmap(mem1, mem1_size); + if (mremap(mem2, mem2_size, mem1_size, MREMAP_MAYMOVE | MREMAP_FIXED, + mem1) != mem1) { + pr_perror("proc1 mem2 remap"); + exit(1); + } + mem2_old = mem2; + mem2 = NULL; + + test_daemon(); + while (test_go()) + mems_datagen_each_pgix(PROC1_PGIX, &crc_epoch); + test_waitsig(); + futex_set(&test_sync->datagen_exit_cnt, 0); + futex_set(&test_sync->datagen, 0); + futex_wait_while(&test_sync->datagen_exit_cnt, PROC_CNT); + + mems_datachck_each_pgix(PROC1_PGIX); + + chk_proc_mem_eq(pstree->proc1, mem1, mem1_size, + pstree->proc11, mem2_old, mem2_size); + chk_proc_mem_eq(pstree->proc1, mem1, mem1_size, + pstree->proc12, mem2_old, mem2_size); + + chk_proc_mem_eq(pstree->proc1, mem3, mem3_size, + pstree->proc12, mem3, mem3_size); + + cont_and_wait_child(pstree->proc11); + cont_and_wait_child(pstree->proc12); + cont_and_wait_child(pstree->proc13); + + pass(); + return 0; +} + +static void kill_pstree_from_root(void) +{ + if (getpid() != pstree->proc1) + return; + + kill(pstree->proc11, SIGKILL); + kill(pstree->proc12, SIGKILL); + kill(pstree->proc13, SIGKILL); + kill(pstree->proc111, SIGKILL); + kill(pstree->proc112, SIGKILL); + kill(pstree->proc131, SIGKILL); +} + +static void sigchld_hand(int signo, siginfo_t *info, void *ucontext) +{ + if (info->si_code != CLD_EXITED) + return; + if (!info->si_status) + return; + + /* + * If we are not ps tree root then propagate child error to parent. + * If we are ps tree root then also call all + * atexit handlers set up by zdtm test framework and this test. + * exit() is not async signal safe but it's ok for testing purposes. + * exit() usage allows us to use very simple error handling + * and pstree killing logic. + */ + exit(info->si_status); +} + +int main(int argc, char **argv) +{ + struct sigaction sa = { + .sa_sigaction = sigchld_hand, + .sa_flags = SA_RESTART | SA_SIGINFO | SA_NOCLDSTOP + }; + sigemptyset(&sa.sa_mask); + + test_init(argc, argv); + + pstree = (struct pstree *)mmap_ashmem(PAGE_SIZE); + test_sync = (struct test_sync *)mmap_ashmem(sizeof(*test_sync)); + + if (sigaction(SIGCHLD, &sa, NULL)) { + pr_perror("SIGCHLD handler setup"); + exit(1); + }; + + if (atexit(kill_pstree_from_root)) { + pr_err("Can't setup atexit cleanup func"); + exit(1); + } + return proc1_func(); +} diff --git a/test/zdtm/customization/maps008.desc b/test/zdtm/customization/maps008.desc new file mode 100644 index 0000000..154ef8c --- /dev/null +++ b/test/zdtm/customization/maps008.desc @@ -0,0 +1 @@ +{'arch': 'aarch64', 'opts': '--pin-memory --use-fork-pid', 'flavor': 'h', 'flags': 'suid', 'cmdline': 'pinmemory max_pin_pid_num'} diff --git a/test/zdtm/customization/maps01.c b/test/zdtm/customization/maps01.c new file mode 100644 index 0000000..119d7a6 --- /dev/null +++ b/test/zdtm/customization/maps01.c @@ -0,0 +1,183 @@ +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <signal.h> +#include <string.h> +#include <sys/mman.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <linux/limits.h> +#include "zdtmtst.h" + +#define MEM_SIZE (1LU << 30) +#define MEM_OFFSET (1LU << 29) +#define MEM_OFFSET2 (MEM_SIZE - PAGE_SIZE) +#define MEM_OFFSET3 (20LU * PAGE_SIZE) + +const char *test_doc = "Test shared memory"; +const char *test_author = "Andrew Vagin <avagin@openvz.org"; + +int main(int argc, char ** argv) +{ + void *m, *m2, *p, *p2; + char path[PATH_MAX]; + uint32_t crc; + pid_t pid = -1; + int status, fd; + task_waiter_t t; + + test_init(argc, argv); + + task_waiter_init(&t); + + m = mmap(NULL, MEM_SIZE, PROT_WRITE | PROT_READ, + MAP_SHARED | MAP_ANONYMOUS, -1, 0); + + if (m == MAP_FAILED) { + pr_err("Failed to mmap %lu Mb shared anonymous R/W memory\n", + MEM_SIZE >> 20); + goto err; + } + + p = mmap(NULL, MEM_SIZE, PROT_WRITE | PROT_READ, + MAP_SHARED | MAP_ANONYMOUS, -1, 0); + + if (p == MAP_FAILED) { + pr_err("Failed to mmap %ld Mb shared anonymous R/W memory\n", + MEM_SIZE >> 20); + goto err; + } + + p2 = mmap(NULL, MEM_OFFSET, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (p2 == MAP_FAILED) { + pr_err("Failed to mmap %lu Mb anonymous memory\n", + MEM_OFFSET >> 20); + goto err; + } + + pid = test_fork(); + if (pid < 0) { + pr_err("Fork failed with %d\n", pid); + goto err; + } else if (pid == 0) { + void *p3; + + p3 = mmap(NULL, MEM_OFFSET3, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (p3 == MAP_FAILED) { + pr_err("Failed to mmap %lu Mb anonymous R/W memory\n", + MEM_OFFSET3 >> 20); + goto err; + } + + crc = ~0; + datagen(m + MEM_OFFSET, PAGE_SIZE, &crc); + crc = ~0; + datagen(m + MEM_OFFSET2, PAGE_SIZE, &crc); + crc = ~0; + datagen(p + MEM_OFFSET + MEM_OFFSET3, PAGE_SIZE, &crc); + crc = ~0; + datagen(p + MEM_OFFSET + 2 * MEM_OFFSET3, PAGE_SIZE, &crc); + crc = ~0; + datagen(p + MEM_OFFSET3, PAGE_SIZE, &crc); + crc = ~0; + datagen(p3, PAGE_SIZE, &crc); + + task_waiter_complete(&t, 1); + + test_waitsig(); + + crc = ~0; + status = datachk(m + MEM_OFFSET, PAGE_SIZE, &crc); + if (status) + return 1; + crc = ~0; + status = datachk(m + MEM_OFFSET2, PAGE_SIZE, &crc); + if (status) + return 1; + crc = ~0; + status = datachk(m + PAGE_SIZE, PAGE_SIZE, &crc); + if (status) + return 1; + crc = ~0; + status = datachk(p + MEM_OFFSET + 2 * MEM_OFFSET3, PAGE_SIZE, &crc); + if (status) + return 1; + crc = ~0; + status = datachk(p + MEM_OFFSET3, PAGE_SIZE, &crc); + if (status) + return 1; + crc = ~0; + status = datachk(p3, PAGE_SIZE, &crc); + if (status) + return 1; + return 0; + } + task_waiter_wait4(&t, 1); + + munmap(p, MEM_OFFSET); + p2 = mremap(p + MEM_OFFSET, MEM_OFFSET, MEM_OFFSET, MREMAP_FIXED | MREMAP_MAYMOVE, p2); + if (p2 == MAP_FAILED) + goto err; + + snprintf(path, PATH_MAX, "/proc/self/map_files/%lx-%lx", + (unsigned long) m, + (unsigned long) m + MEM_SIZE); + fd = open(path, O_RDWR); + if (fd == -1) { + pr_perror("Can't open file %s", path); + goto err; + } + + m2 = mmap(NULL, PAGE_SIZE, PROT_WRITE | PROT_READ, MAP_SHARED, fd, MEM_OFFSET3); + if (m2 == MAP_FAILED) { + pr_perror("Can't map file %s", path); + goto err; + } + close(fd); + + munmap(m, PAGE_SIZE); + munmap(m + PAGE_SIZE * 10, PAGE_SIZE); + munmap(m + MEM_OFFSET2, PAGE_SIZE); + + crc = ~0; + datagen(m + PAGE_SIZE, PAGE_SIZE, &crc); + + crc = ~0; + datagen(m2, PAGE_SIZE, &crc); + + test_daemon(); + test_waitsig(); + + kill(pid, SIGTERM); + wait(&status); + if (WIFEXITED(status)) { + if (WEXITSTATUS(status)) + goto err; + } else + goto err; + + crc = ~0; + if (datachk(m + MEM_OFFSET, PAGE_SIZE, &crc)) + goto err; + + crc = ~0; + if (datachk(m2, PAGE_SIZE, &crc)) + goto err; + + crc = ~0; + if (datachk(p2 + MEM_OFFSET3, PAGE_SIZE, &crc)) + goto err; + + pass(); + + return 0; +err: + if (waitpid(-1, NULL, WNOHANG) == 0) { + kill(pid, SIGTERM); + wait(NULL); + } + return 1; +} diff --git a/test/zdtm/customization/maps01.desc b/test/zdtm/customization/maps01.desc new file mode 100644 index 0000000..dad462e --- /dev/null +++ b/test/zdtm/customization/maps01.desc @@ -0,0 +1 @@ +{'arch': 'aarch64', 'flavor': 'h', 'opts': '--pin-memory --use-fork-pid', 'flags': 'suid', 'cmdline': 'pinmemory max_pin_pid_num'} diff --git a/test/zdtm/customization/maps02.c b/test/zdtm/customization/maps02.c new file mode 100644 index 0000000..eb7c09b --- /dev/null +++ b/test/zdtm/customization/maps02.c @@ -0,0 +1,111 @@ +#include <sys/mman.h> +#include "zdtmtst.h" +#include "get_smaps_bits.h" + +#ifndef MADV_DONTDUMP +#define MADV_DONTDUMP 16 +#endif + +const char *test_doc = "Test shared memory with advises"; +const char *test_author = "Cyrill Gorcunov gorcunov@openvz.org"; + +struct mmap_data { + void *start; + unsigned long orig_flags; + unsigned long orig_madv; + unsigned long new_flags; + unsigned long new_madv; +}; + +#define MEM_SIZE (8192) + +static int alloc_anon_mmap(struct mmap_data *m, int flags, int adv) +{ + m->start = mmap(NULL, MEM_SIZE, PROT_READ | PROT_WRITE, + flags, -1, 0); + if (m->start == MAP_FAILED) { + pr_perror("mmap failed"); + return -1; + } + + if (madvise(m->start, MEM_SIZE, adv)) { + if (errno == EINVAL) { + test_msg("madvise failed, no kernel support\n"); + munmap(m->start, MEM_SIZE); + *m = (struct mmap_data){ }; + } else { + pr_perror("madvise failed"); + return -1; + } + } + + return 0; +} + +int main(int argc, char **argv) +{ + struct mmap_data m[5] = { }; + size_t i; + + test_init(argc, argv); + + test_msg("Alloc growsdown\n"); + if (alloc_anon_mmap(&m[0], MAP_PRIVATE | MAP_ANONYMOUS, MADV_DONTFORK)) + return -1; + + test_msg("Alloc locked/sequential\n"); + if (alloc_anon_mmap(&m[1], MAP_PRIVATE | MAP_ANONYMOUS | MAP_LOCKED, MADV_SEQUENTIAL)) + return -1; + + test_msg("Alloc noreserve/dontdump\n"); + if (alloc_anon_mmap(&m[2], MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, MADV_DONTDUMP)) + return -1; + + test_msg("Alloc hugetlb/hugepage\n"); + if (alloc_anon_mmap(&m[3], MAP_PRIVATE | MAP_ANONYMOUS, MADV_HUGEPAGE)) + return -1; + + test_msg("Alloc dontfork/random|mergeable\n"); + if (alloc_anon_mmap(&m[4], MAP_PRIVATE | MAP_ANONYMOUS, MADV_MERGEABLE)) + return -1; + + test_msg("Fetch existing flags/adv\n"); + for (i = 0; i < sizeof(m)/sizeof(m[0]); i++) { + if (get_smaps_bits((unsigned long)m[i].start, + &m[i].orig_flags, + &m[i].orig_madv)) + return -1; + } + + test_daemon(); + test_waitsig(); + + test_msg("Fetch restored flags/adv\n"); + for (i = 0; i < sizeof(m)/sizeof(m[0]); i++) { + if (get_smaps_bits((unsigned long)m[i].start, + &m[i].new_flags, + &m[i].new_madv)) + return -1; + + if (m[i].orig_flags != m[i].new_flags) { + pr_perror("Flags are changed %lx %lx -> %lx (%zu)", + (unsigned long)m[i].start, + m[i].orig_flags, m[i].new_flags, i); + fail(); + return -1; + } + + if (m[i].orig_madv != m[i].new_madv) { + pr_perror("Madvs are changed %lx %lx -> %lx (%zu)", + (unsigned long)m[i].start, + m[i].orig_madv, m[i].new_madv, i); + fail(); + return -1; + } + + } + + pass(); + + return 0; +} diff --git a/test/zdtm/customization/maps02.desc b/test/zdtm/customization/maps02.desc new file mode 100644 index 0000000..f14d661 --- /dev/null +++ b/test/zdtm/customization/maps02.desc @@ -0,0 +1 @@ +{'arch': 'aarch64', 'opts': '--pin-memory --use-fork-pid', 'flavor': 'h', 'cmdline': 'pinmemory max_pin_pid_num'} diff --git a/test/zdtm/customization/maps04.c b/test/zdtm/customization/maps04.c new file mode 100644 index 0000000..780c566 --- /dev/null +++ b/test/zdtm/customization/maps04.c @@ -0,0 +1,57 @@ +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <signal.h> +#include <string.h> +#include <sys/mman.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <linux/limits.h> +#include "zdtmtst.h" + +#define MEM_SIZE (1L << 29) + +const char *test_doc = "Test big mappings"; +const char *test_author = "Andrew Vagin <avagin@openvz.org"; + +int main(int argc, char ** argv) +{ + void *m; + uint32_t crc; + int i; + + test_init(argc, argv); + + m = mmap(NULL, MEM_SIZE, PROT_WRITE | PROT_READ, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + + if (m == MAP_FAILED) { + fail(); + return 1; + } + + crc = ~0; + datagen(m, MEM_SIZE, &crc); + + for (i = 0; i < MEM_SIZE / (1<<20); i++) + if (mprotect(m + (lrand48() * PAGE_SIZE % MEM_SIZE), PAGE_SIZE, PROT_NONE)) { + pr_perror("mprotect"); + return 1; + } + + test_daemon(); + test_waitsig(); + + if (mprotect(m, MEM_SIZE, PROT_READ)) + pr_perror("mprotect"); + + crc = ~0; + if (datachk(m, MEM_SIZE, &crc)) + fail("Mem corrupted"); + else + pass(); + + return 0; +} diff --git a/test/zdtm/customization/maps04.desc b/test/zdtm/customization/maps04.desc new file mode 100644 index 0000000..2db7603 --- /dev/null +++ b/test/zdtm/customization/maps04.desc @@ -0,0 +1 @@ +{'arch': 'aarch64', 'opts': '--pin-memory --use-fork-pid', 'flavor': 'h', 'timeout': '60', 'cmdline': 'pinmemory max_pin_pid_num'} diff --git a/test/zdtm/customization/maps05.c b/test/zdtm/customization/maps05.c new file mode 100644 index 0000000..faa09ee --- /dev/null +++ b/test/zdtm/customization/maps05.c @@ -0,0 +1,91 @@ +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <signal.h> +#include <string.h> +#include <sys/mman.h> +#include <setjmp.h> +#include <sys/types.h> +#include <sys/stat.h> +#include "zdtmtst.h" + +const char *test_doc = "Create a bunch of small VMAs and test they survive transferring\n"; +const char *test_author = "Cyrill Gorcunov gorcunov@openvz.org"; + +#define NR_MAPS 4096 + +#define NR_MAPS_1 (NR_MAPS + 0) +#define NR_MAPS_2 (NR_MAPS + 1) + +#define MAPS_SIZE_1 (140 << 10) +#define MAPS_SIZE_2 (8192) + +int main(int argc, char *argv[]) +{ + void *map[NR_MAPS + 2] = { }, *addr; + size_t i, summary; + + test_init(argc, argv); + + summary = NR_MAPS * 2 * 4096 + MAPS_SIZE_1 + MAPS_SIZE_2 + (1 << 20); + + addr = mmap(NULL, summary, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + if (addr == MAP_FAILED) { + pr_perror("Can't mmap"); + return 1; + } + munmap(addr, summary); + + for (i = 0; i < NR_MAPS; i++) { + map[i] = mmap(i > 0 ? map[i - 1] + 8192 : addr, 4096, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + if (map[i] == MAP_FAILED) { + pr_perror("Can't mmap"); + return 1; + } else { + /* Dirtify it */ + int *v = (void *)map[i]; + *v = i; + } + } + + map[NR_MAPS_1] = mmap(map[NR_MAPS_1 - 1] + 8192, MAPS_SIZE_1, PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_ANONYMOUS | MAP_PRIVATE | MAP_GROWSDOWN, -1, 0); + if (map[NR_MAPS_1] == MAP_FAILED) { + pr_perror("Can't mmap"); + return 1; + } else { + /* Dirtify it */ + int *v = (void *)map[NR_MAPS_1]; + *v = i; + test_msg("map-1: %p %p\n", map[NR_MAPS_1], map[NR_MAPS_1] + MAPS_SIZE_1); + } + + map[NR_MAPS_2] = mmap(map[NR_MAPS_1] + MAPS_SIZE_1, MAPS_SIZE_2, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE | MAP_GROWSDOWN, -1, 0); + if (map[NR_MAPS_2] == MAP_FAILED) { + pr_perror("Can't mmap"); + return 1; + } else { + /* Dirtify it */ + int *v = (void *)map[NR_MAPS_2]; + *v = i; + test_msg("map-2: %p %p\n", map[NR_MAPS_2], map[NR_MAPS_2] + MAPS_SIZE_2); + } + + test_daemon(); + test_waitsig(); + + for (i = 0; i < NR_MAPS; i++) { + int *v = (void *)map[i]; + + if (*v != i) { + fail("Data corrupted at page %lu", (unsigned long)i); + return 1; + } + } + + pass(); + return 0; +} diff --git a/test/zdtm/customization/maps05.desc b/test/zdtm/customization/maps05.desc new file mode 100644 index 0000000..f14d661 --- /dev/null +++ b/test/zdtm/customization/maps05.desc @@ -0,0 +1 @@ +{'arch': 'aarch64', 'opts': '--pin-memory --use-fork-pid', 'flavor': 'h', 'cmdline': 'pinmemory max_pin_pid_num'} diff --git a/test/zdtm/customization/maps06.c b/test/zdtm/customization/maps06.c new file mode 100644 index 0000000..7480d6b --- /dev/null +++ b/test/zdtm/customization/maps06.c @@ -0,0 +1,70 @@ +#include "zdtmtst.h" +#include <sys/mman.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +const char *test_doc = "Create a lot of file vma-s"; +const char *test_author = "Andrei Vagin avagin@openvz.org"; + +char *filename; +TEST_OPTION(filename, string, "file name", 1); + +int main(int argc, char ** argv) +{ + void *start; + int fd, i; + int ps = sysconf(_SC_PAGESIZE); + int test_size; + + test_init(argc, argv); + + fd = open(filename, O_RDWR | O_CREAT, 0666); + if (fd < 0) + return 1; + + ftruncate(fd, ps); + + if (ps == 0x1000) + test_size = 10240; + else + test_size = 512; + + start = mmap(0, ps * test_size * 4, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); + if (start == MAP_FAILED) + return 1; + + for (i = 0; i < test_size; i++) { + int *addr; + addr = mmap(start + i * 3 * ps, ps, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_FILE | MAP_FIXED, fd, 0); + if (addr == MAP_FAILED) + return 1; + addr[0] = i * 2; + addr = mmap(start + (i * 3 + 1) * ps, ps, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0); + if (addr == MAP_FAILED) + return 1; + addr[0] = i; + } + + test_daemon(); + + test_waitsig(); + + for (i = 0; i < test_size; i++) { + int *addr; + addr = start + i * 3 * ps; + if (addr[0] != i * 2) + fail(); + addr = start + (i * 3 + 1) * ps; + if (addr[0] != i) + fail(); + } + + pass(); + + return 0; +} diff --git a/test/zdtm/customization/maps06.desc b/test/zdtm/customization/maps06.desc new file mode 100644 index 0000000..f14d661 --- /dev/null +++ b/test/zdtm/customization/maps06.desc @@ -0,0 +1 @@ +{'arch': 'aarch64', 'opts': '--pin-memory --use-fork-pid', 'flavor': 'h', 'cmdline': 'pinmemory max_pin_pid_num'} diff --git a/test/zdtm/customization/maps_file_prot.c b/test/zdtm/customization/maps_file_prot.c new file mode 100644 index 0000000..3b28c1f --- /dev/null +++ b/test/zdtm/customization/maps_file_prot.c @@ -0,0 +1,53 @@ +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <linux/limits.h> +#include "zdtmtst.h" + +const char *test_doc = "Test mappings of same file with different prot"; +const char *test_author = "Jamie Liu jamieliu@google.com"; + +char *filename; +TEST_OPTION(filename, string, "file name", 1); + +#define die(fmt, arg...) do { pr_perror(fmt, ## arg); return 1; } while (0) + +int main(int argc, char ** argv) +{ + void *ro_map, *rw_map; + int fd; + + test_init(argc, argv); + + fd = open(filename, O_RDWR | O_CREAT, 0644); + if (fd < 0) + die("open failed"); + if (ftruncate(fd, 2 * PAGE_SIZE)) + die("ftruncate failed"); + + ro_map = mmap(NULL, 2 * PAGE_SIZE, PROT_READ, MAP_SHARED, fd, 0); + if (ro_map == MAP_FAILED) + die("mmap failed"); + rw_map = ro_map + PAGE_SIZE; + if (mprotect(rw_map, PAGE_SIZE, PROT_READ | PROT_WRITE)) + die("mprotect failed"); + + close(fd); + + test_daemon(); + test_waitsig(); + + /* Check that rw_map is still writeable */ + *(volatile char *)rw_map = 1; + + if (mprotect(ro_map, PAGE_SIZE, PROT_READ | PROT_WRITE)) { + fail("mprotect after restore failed"); + return 1; + } + + pass(); + return 0; +} diff --git a/test/zdtm/customization/maps_file_prot.desc b/test/zdtm/customization/maps_file_prot.desc new file mode 100644 index 0000000..0ec4023 --- /dev/null +++ b/test/zdtm/customization/maps_file_prot.desc @@ -0,0 +1 @@ +{'arch': 'aarch64', 'opts': '--pin-memory --use-fork-pid', 'flavor': 'h'} diff --git a/test/zdtm_ct.c b/test/zdtm_ct.c index 5495d61..186b360 100644 --- a/test/zdtm_ct.c +++ b/test/zdtm_ct.c @@ -9,6 +9,7 @@ #include <sys/stat.h> #include <fcntl.h> #include <errno.h> +#include <string.h>
#ifndef CLONE_NEWTIME #define CLONE_NEWTIME 0x00000080 /* New time namespace */ @@ -73,13 +74,23 @@ int main(int argc, char **argv) { pid_t pid; int status; + char *val = getenv("ZDTM_NO_PID_NS"); + int flags = CLONE_NEWNS | CLONE_NEWNET | CLONE_NEWIPC; + + /* + * Some customizing mechanism don't support pid namespace, + * so every customizing feature testcase will set + * 'ZDTM_NO_PID_NS' environment value. + */ + if (val == NULL || strcmp(val, "1") != 0) + flags |= CLONE_NEWPID;
/* * pidns is used to avoid conflicts * mntns is used to mount /proc * net is used to avoid conflicts of parasite sockets */ - if (unshare(CLONE_NEWNS | CLONE_NEWPID | CLONE_NEWNET | CLONE_NEWIPC)) + if (unshare(flags)) return 1; pid = fork(); if (pid == 0) {