static void do_test(void) { int i; if (!SAFE_FORK()) { SAFE_FILE_PRINTF(PATH_AUTOGROUP, "%d", 1); SAFE_SETSID(); if (SAFE_FORK()) pause(); SAFE_KILL(getppid(), SIGKILL); usleep(1000); // The child has gone, the grandchild runs with kref == 1 SAFE_FILE_PRINTF(PATH_AUTOGROUP, "%d", 0); SAFE_SETSID(); // runs with the freed ag/tg for (i = 0; i < LOOPS; i++) usleep(10); TST_CHECKPOINT_WAKE(0); exit(0); } SAFE_WAIT(NULL); // destroy the child's ag/tg TST_CHECKPOINT_WAIT(0); tst_res(TPASS, "Bug not reproduced"); }
static void do_child_1(void) { pid_t pid; int i; for (i = 0; i < MAXKIDS; i++) { pid = SAFE_FORK(); if (pid == 0) { if (i == 0 || i == 1) do_exit(0); if (i == 2 || i == 3) do_compute(); if (i == 4 || i == 5) do_fork(); if (i == 6 || i == 7) do_sleep(); } fork_kid_pid[i] = pid; } TST_CHECKPOINT_WAKE2(0, MAXKIDS); if (TST_TRACE(reap_children(0, 0, fork_kid_pid, MAXKIDS))) return; tst_res(TPASS, "Test PASSED"); }
static void do_child_1(void) { pid_t pid, group; int i; group = SAFE_GETPGID(0); for (i = 0; i < MAXKIDS; i++) { if (i == (MAXKIDS / 2)) SAFE_SETPGID(0, 0); pid = SAFE_FORK(); if (pid == 0) do_exit(0); fork_kid_pid[i] = pid; } TST_CHECKPOINT_WAKE2(0, MAXKIDS); if (reap_children(0, 0, fork_kid_pid + (MAXKIDS / 2), MAXKIDS / 2)) return; /* Make sure can pickup children in a diff. process group */ if (reap_children(-group, 0, fork_kid_pid, MAXKIDS / 2)) return; tst_res(TPASS, "Test PASSED"); }
static void run(void) { pid_t pid; int status; retry: pid = SAFE_FORK(); if (!pid) { setup_cgroup_paths(getpid()); child(); } setup_cgroup_paths(pid); SAFE_WAIT(&status); cleanup(); /* * Rarely cgroup OOM kills both children not only the one that allocates * memory in loop, hence we retry here if that happens. */ if (WIFSIGNALED(status)) { tst_res(TINFO, "Both children killed, retrying..."); goto retry; } if (WIFEXITED(status) && WEXITSTATUS(status) == TCONF) tst_brk(TCONF, "MADV_FREE is not supported"); if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) tst_brk(TBROK, "Child %s", tst_strstatus(status)); }
static void verify_creat(void) { pid_t pid; pid = SAFE_FORK(); if (pid == 0) { char *av[] = {TEST_APP, NULL}; (void)execve(TEST_APP, av, tst_ipc_envp); perror("execve failed"); exit(1); } TST_CHECKPOINT_WAIT(0); TEST(creat(TEST_APP, O_WRONLY)); if (TEST_RETURN != -1) { tst_res(TFAIL, "creat() succeeded unexpectedly"); return; } if (TEST_ERRNO == ETXTBSY) tst_res(TPASS, "creat() received EXTBSY"); else tst_res(TFAIL | TTERRNO, "creat() failed unexpectedly"); SAFE_KILL(pid, SIGKILL); SAFE_WAITPID(pid, NULL, 0); }
static void thp_test(void) { long prev_left; int pid; while (bst->right - bst->left > 1) { pid_t pid = SAFE_FORK(); if (!pid) { /* We set mid to left assuming exec will succeed. If * exec fails with E2BIG (and thus returns) then we * restore left and set right to mid instead. */ prev_left = bst->left; bst->mid = (bst->left + bst->right) / 2; bst->left = bst->mid; args[bst->mid] = NULL; TEST(execvp("true", args)); if (TST_ERR != E2BIG) tst_brk(TBROK | TTERRNO, "execvp(\"true\", ...)"); bst->left = prev_left; bst->right = bst->mid; exit(0); } tst_reap_children(); tst_res(TINFO, "left: %ld, right: %ld, mid: %ld", bst->left, bst->right, bst->mid); } /* We end with mid == right or mid == left where right - left = * 1. Regardless we must use left because right is only set to values * which are too large. */ pid = SAFE_FORK(); if (pid == 0) { args[bst->left] = NULL; TEST(execvp("true", args)); if (TST_ERR != E2BIG) tst_brk(TBROK | TTERRNO, "execvp(\"true\", ...)"); exit(0); } tst_reap_children(); tst_res(TPASS, "system didn't crash."); }
static void verify_setuid(void) { pid_t pid; pid = SAFE_FORK(); if (!pid) dosetuid(); else dosetuid(); }
static void verify_execve(void) { int i; for (i = 0; i < nchild; i++) { if (SAFE_FORK() == 0) do_child(); } TST_CHECKPOINT_WAKE2(0, nchild); }
static void mem_test(void) { pid_t pid; int i = 0, pid_cntr = 0; unsigned long long alloc_bytes = alloc_maxbytes; const char *write_msg = ""; if (dowrite) write_msg = "(and written to) "; /* to make mtest01 support -i N */ children_done = 0; do { pid = SAFE_FORK(); if (pid == 0) { alloc_bytes = MIN(ALLOC_THRESHOLD, alloc_bytes); child_loop_alloc(alloc_bytes); } pid_list[pid_cntr++] = pid; if (alloc_bytes <= ALLOC_THRESHOLD) break; alloc_bytes -= ALLOC_THRESHOLD; } while (pid_cntr < max_pids); /* wait in the loop for all children finish allocating */ while (children_done < pid_cntr) { if (tst_timeout_remaining() < STOP_THRESHOLD) { tst_res(TWARN, "the remaininig time is not enough for testing"); break; } usleep(100000); } if (children_done < pid_cntr) { tst_res(TFAIL, "kbytes allocated %sless than expected %llu", write_msg, alloc_maxbytes / 1024); } else { tst_res(TPASS, "%llu kbytes allocated %s", alloc_maxbytes / 1024, write_msg); } for (i = 0; i < pid_cntr; i++) { TST_PROCESS_STATE_WAIT(pid_list[i], 'T'); kill(pid_list[i], SIGCONT); } }
static void verify_execveat(unsigned int i) { struct tcase *tc = &tcases[i]; char *argv[2] = {TEST_APP, NULL}; pid_t pid; pid = SAFE_FORK(); if (pid == 0) { TEST(execveat(*tc->fd, tc->pathname, argv, environ, tc->flag)); tst_res(TFAIL | TERRNO, "execveat() returns unexpected errno"); } }
static void do_test(void) { pid_t pid = SAFE_FORK(); switch (pid) { case 0: tst_res(TPASS, "Child (%i) reports", getpid()); break; default: tst_res(TPASS, "Parent (%i) reports", getpid()); break; } }
static void verify_mempolicy(unsigned int node, int mode) { struct bitmask *bm = numa_allocate_nodemask(); unsigned int i; numa_bitmask_setbit(bm, node); TEST(set_mempolicy(mode, bm->maskp, bm->size+1)); if (TST_RET) { tst_res(TFAIL | TTERRNO, "set_mempolicy(%s) node %u", tst_numa_mode_name(mode), node); return; } tst_res(TPASS, "set_mempolicy(%s) node %u", tst_numa_mode_name(mode), node); numa_free_nodemask(bm); const char *prefix = "child: "; if (SAFE_FORK()) { prefix = "parent: "; tst_reap_children(); } tst_nodemap_reset_counters(nodes); alloc_fault_count(nodes, NULL, PAGES_ALLOCATED * page_size); tst_nodemap_print_counters(nodes); for (i = 0; i < nodes->cnt; i++) { if (nodes->map[i] == node) { if (nodes->counters[i] == PAGES_ALLOCATED) { tst_res(TPASS, "%sNode %u allocated %u", prefix, node, PAGES_ALLOCATED); } else { tst_res(TFAIL, "%sNode %u allocated %u, expected %u", prefix, node, nodes->counters[i], PAGES_ALLOCATED); } continue; } if (nodes->counters[i]) { tst_res(TFAIL, "%sNode %u allocated %u, expected 0", prefix, i, nodes->counters[i]); } } }
static void verify_access(unsigned int n) { struct tcase *tc = tcases + n; pid_t pid; access_test(tc, "root"); pid = SAFE_FORK(); if (pid) { SAFE_WAITPID(pid, NULL, 0); } else { SAFE_SETUID(uid); access_test(tc, "nobody"); } }
static void test_hugeshmctl(void) { pid_t pid; int status; switch (pid = SAFE_FORK()) { case 0: /* set the user ID of the child to the non root user */ SAFE_SETUID(ltp_uid); do_child(); exit(0); default: SAFE_WAITPID(pid, &status, 0); } }
static void verify_shmat(unsigned int n) { int *addr; pid_t pid; int status; struct shmid_ds buf; struct test_case_t *tc = &tcases[n]; addr = shmat(shm_id, *tc->shmaddr, tc->flag); if (addr == (void *)-1) { tst_res(TFAIL | TERRNO, "shmat() failed"); return; } SAFE_SHMCTL(shm_id, IPC_STAT, &buf); if (buf.shm_nattch != 1) { tst_res(TFAIL, "number of attaches was incorrect"); goto end; } if (buf.shm_segsz != INT_SIZE) { tst_res(TFAIL, "segment size was incorrect"); goto end; } if (expected_addr(*tc->shmaddr, addr) != addr) { tst_res(TFAIL, "shared memory address %p is not correct, expected %p", addr, expected_addr(*tc->shmaddr, addr)); goto end; } pid = SAFE_FORK(); if (!pid) do_child(addr, tc->exp_status == SIGSEGV); else SAFE_WAITPID(pid, &status, 0); if (expected_status(status, tc->exp_status)) tst_res(TFAIL, "shmat() failed to attach %s", tc->desp); else tst_res(TPASS, "shmat() succeeded to attach %s", tc->desp); end: SAFE_SHMDT(addr); }
static void test_swapping(void) { #if __WORDSIZE == 32 tst_brk(TCONF, "test is not designed for 32-bit system."); #endif init_meminfo(); switch (pid = SAFE_FORK()) { case 0: do_alloc(); exit(0); default: check_swapping(); } }
pid_t create_sig_proc(int sig, int count, unsigned int usec) { pid_t pid, cpid; pid = getpid(); cpid = SAFE_FORK(); if (cpid == 0) { while (count-- > 0) { usleep(usec); if (kill(pid, sig) == -1) break; } exit(0); } return cpid; }
static void do_fork(void) { pid_t fork_pid; int i; TST_CHECKPOINT_WAIT(0); for (i = 0; i < 50; i++) { fork_pid = SAFE_FORK(); if (fork_pid == 0) exit(3); if (TST_TRACE(reap_children(fork_pid, 0, &fork_pid, 1))) break; } exit(3); }
static void do_shmat(unsigned int n) { pid_t pid; struct test_case_t *tc = &tcases[n]; if (!tc->exp_user) { verify_shmat(tc); } else { pid = SAFE_FORK(); if (pid) { tst_reap_children(); } else { SAFE_SETUID(pw->pw_uid); verify_shmat(tc); exit(0); } } }
static void verify_execve(void) { pid_t pid; char *argv[2] = {TEST_APP, NULL}; pid = SAFE_FORK(); if (pid == 0) do_child(); TST_CHECKPOINT_WAIT(0); TEST(execve(TEST_APP, argv, environ)); if (TST_ERR != ETXTBSY) tst_res(TFAIL | TTERRNO, "execve succeeded, expected failure"); else tst_res(TPASS | TTERRNO, "execve failed as expected"); TST_CHECKPOINT_WAKE(0); }
static void verify_inotify(void) { int inotify_fd, fd; pid_t pid; int i, tests; pid = SAFE_FORK(); if (pid == 0) { while (1) { for (i = 0; i < FILES; i++) { fd = SAFE_OPEN(names[i], O_CREAT | O_RDWR, 0600); SAFE_CLOSE(fd); } for (i = 0; i < FILES; i++) SAFE_UNLINK(names[i]); } } for (tests = 0; tests < TEARDOWNS; tests++) { inotify_fd = myinotify_init1(O_NONBLOCK); if (inotify_fd < 0) tst_brk(TBROK | TERRNO, "inotify_init failed"); for (i = 0; i < FILES; i++) { /* * Both failure and success are fine since * files are being deleted in parallel - this * is what provokes the race we want to test * for... */ myinotify_add_watch(inotify_fd, names[i], IN_MODIFY); } SAFE_CLOSE(inotify_fd); } /* We survived for given time - test succeeded */ tst_res(TPASS, "kernel survived inotify beating"); /* Kill the child creating / deleting files and wait for it */ SAFE_KILL(pid, SIGKILL); SAFE_WAIT(NULL); }
static void setup(void) { struct passwd *pw; pid_t pid; pw = SAFE_GETPWNAM("nobody"); nobody_uid = pw->pw_uid; pw = SAFE_GETPWNAM("bin"); bin_uid = pw->pw_uid; pid = SAFE_FORK(); if (pid == 0) { SAFE_SETREUID(nobody_uid, nobody_uid); SAFE_MKDIR(TESTDIR, 0700); exit(0); } tst_reap_children(); SAFE_SETREUID(bin_uid, bin_uid); }
/* * Try to update a key, racing with removing write permission. * This may crash buggy kernels. */ static void test_update_setperm_race(void) { static const char payload[] = "payload"; key_serial_t keyid; int i; new_session_keyring(); TEST(add_key("user", "desc", payload, sizeof(payload), KEY_SPEC_SESSION_KEYRING)); if (TEST_RETURN < 0) { tst_res(TBROK | TTERRNO, "failed to add 'user' key"); return; } keyid = TEST_RETURN; if (SAFE_FORK() == 0) { uint32_t perm = KEY_POS_ALL; for (i = 0; i < 10000; i++) { perm ^= KEY_POS_WRITE; TEST(keyctl(KEYCTL_SETPERM, keyid, perm)); if (TEST_RETURN != 0) tst_brk(TBROK | TTERRNO, "setperm failed"); } exit(0); } tst_res(TINFO, "Try to update the 'user' key..."); for (i = 0; i < 10000; i++) { TEST(keyctl(KEYCTL_UPDATE, keyid, payload, sizeof(payload))); if (TEST_RETURN != 0 && TEST_ERRNO != EACCES) { tst_res(TBROK | TTERRNO, "failed to update 'user' key"); return; } } tst_reap_children(); tst_res(TPASS, "didn't crash while racing to update 'user' key"); }
/* * oom - allocates memory according to specified testcase and checks * desired outcome (e.g. child killed, operation failed with ENOMEM) * @testcase: selects how child allocates memory * valid choices are: NORMAL, MLOCK and KSM * @lite: if non-zero, child makes only single TESTMEM+MB allocation * if zero, child keeps allocating memory until it gets killed * or some operation fails * @retcode: expected return code of child process * if matches child ret code, this function reports PASS, * otherwise it reports FAIL * @allow_sigkill: if zero and child is killed, this function reports FAIL * if non-zero, then if child is killed by SIGKILL * it is considered as PASS */ void oom(int testcase, int lite, int retcode, int allow_sigkill) { pid_t pid; int status, threads; switch (pid = SAFE_FORK()) { case 0: threads = MAX(1, tst_ncpus() - 1); child_alloc(testcase, lite, threads); default: break; } tst_res(TINFO, "expected victim is %d.", pid); SAFE_WAITPID(-1, &status, 0); if (WIFSIGNALED(status)) { if (allow_sigkill && WTERMSIG(status) == SIGKILL) { tst_res(TPASS, "victim signalled: (%d) %s", SIGKILL, tst_strsig(SIGKILL)); } else { tst_res(TFAIL, "victim signalled: (%d) %s", WTERMSIG(status), tst_strsig(WTERMSIG(status))); } } else if (WIFEXITED(status)) { if (WEXITSTATUS(status) == retcode) { tst_res(TPASS, "victim retcode: (%d) %s", retcode, strerror(retcode)); } else { tst_res(TFAIL, "victim unexpectedly ended with " "retcode: %d, expected: %d", WEXITSTATUS(status), retcode); } } else { tst_res(TFAIL, "victim unexpectedly ended"); } }
void dirtyc0w_test(void) { int i, fd, pid, fail = 0; char c; /* Create file */ fd = SAFE_OPEN(FNAME, O_WRONLY|O_CREAT|O_EXCL, 0444); SAFE_WRITE(1, fd, STR, sizeof(STR)-1); SAFE_CLOSE(fd); pid = SAFE_FORK(); if (!pid) { SAFE_SETGID(nobody_gid); SAFE_SETUID(nobody_uid); SAFE_EXECLP("dirtyc0w_child", "dirtyc0w_child", NULL); } TST_CHECKPOINT_WAIT(0); for (i = 0; i < 100; i++) { usleep(10000); SAFE_FILE_SCANF(FNAME, "%c", &c); if (c != 't') { fail = 1; break; } } SAFE_KILL(pid, SIGUSR1); tst_reap_children(); SAFE_UNLINK(FNAME); if (fail) tst_res(TFAIL, "Bug reproduced!"); else tst_res(TPASS, "Bug not reproduced"); }
static void run(void) { pid_t pid; int status = 1; struct rusage rusage; pid = SAFE_FORK(); if (!pid) { TST_PROCESS_STATE_WAIT(getppid(), 'S'); exit(0); } TEST(wait4(pid, &status, 0, &rusage)); if (TST_RET == -1) { tst_res(TFAIL | TERRNO, "wait4() failed"); return; } if (TST_RET != pid) { tst_res(TFAIL, "waitpid() returned wrong pid %li, expected %i", TST_RET, pid); } else { tst_res(TPASS, "waitpid() returned correct pid %i", pid); } if (!WIFEXITED(status)) { tst_res(TFAIL, "WIFEXITED() not set in status (%s)", tst_strstatus(status)); return; } tst_res(TPASS, "WIFEXITED() is set in status"); if (WEXITSTATUS(status)) tst_res(TFAIL, "WEXITSTATUS() != 0 but %i", WEXITSTATUS(status)); else tst_res(TPASS, "WEXITSTATUS() == 0"); }
static int worker_run(struct worker *self) { char buf[BUFFER_SIZE]; struct sigaction term_sa = { .sa_handler = SIG_IGN, .sa_flags = 0, }; struct queue *q = self->q; sigaction(SIGTTIN, &term_sa, NULL); while (1) { if (!queue_pop(q, buf)) break; read_test(buf); } queue_destroy(q, 1); tst_flush(); return 0; } static void spawn_workers(void) { int i; struct worker *wa = workers; bzero(workers, worker_count * sizeof(*workers)); for (i = 0; i < worker_count; i++) { wa[i].q = queue_init(); wa[i].pid = SAFE_FORK(); if (!wa[i].pid) exit(worker_run(wa + i)); } }
static void do_child_1(void) { pid_t pid, group; int i; int status; group = SAFE_GETPGID(0); for (i = 0; i < MAXKIDS; i++) { if (i == (MAXKIDS / 2)) SAFE_SETPGID(0, 0); pid = SAFE_FORK(); if (pid == 0) do_exit(0); fork_kid_pid[i] = pid; } if (TST_TRACE(waitpid_ret_test(0, &status, WNOHANG, 0, 0))) return; if (TST_TRACE(waitpid_ret_test(-group, &status, WNOHANG, 0, 0))) return; TST_CHECKPOINT_WAKE2(0, MAXKIDS); if (TST_TRACE(reap_children(0, WNOHANG, fork_kid_pid + (MAXKIDS / 2), MAXKIDS / 2))) return; if (TST_TRACE(reap_children(-group, WNOHANG, fork_kid_pid, MAXKIDS / 2))) return; tst_res(TPASS, "Test PASSED"); }
static void run(unsigned int test_case) { /* Work in child process - needed to undo unshare and chroot */ if (SAFE_FORK()) { tst_reap_children(); return; } /* pivot_root requires no shared mounts exist in process namespace */ TEST(unshare(CLONE_NEWNS | CLONE_FS)); if (TST_RET == -1) tst_brk(TFAIL | TERRNO, "unshare failed"); /* * Create an initial root dir. pivot_root doesn't work if the initial root * dir is a initramfs, so use chroot to create a safe environment */ SAFE_MOUNT("none", "/", NULL, MS_REC|MS_PRIVATE, NULL); SAFE_MOUNT("none", CHROOT_DIR, "tmpfs", 0, 0); SAFE_CHROOT(CHROOT_DIR); SAFE_MKDIR(NEW_ROOT, 0777); /* * pivot_root only works if new_root is a mount point, so mount a tmpfs * unless testing for that fail mode */ if (test_cases[test_case].test_case != NEW_ROOT_ON_CURRENT_ROOT) SAFE_MOUNT("none", NEW_ROOT, "tmpfs", 0, 0); /* * Create put_old under new_root, unless testing for that specific fail * mode */ const char* actual_put_old = NULL; if (test_cases[test_case].test_case == PUT_OLD_NOT_UNDERNEATH_NEW_ROOT) { actual_put_old = PUT_OLD_BAD; SAFE_MKDIR(PUT_OLD_FS, 0777); SAFE_MOUNT("none", PUT_OLD_FS, "tmpfs", 0, 0); SAFE_MKDIR(PUT_OLD_BAD, 0777); } else { actual_put_old = PUT_OLD; if (test_cases[test_case].test_case == PUT_OLD_NOT_DIR) SAFE_CREAT(PUT_OLD, 0777); else SAFE_MKDIR(PUT_OLD, 0777); } if (test_cases[test_case].test_case == NO_CAP_SYS_ADMIN) { #ifdef HAVE_LIBCAP drop_cap_sys_admin(); #else tst_res(TCONF, "System doesn't have POSIX capabilities support"); return; #endif } TEST(syscall(__NR_pivot_root, NEW_ROOT, actual_put_old)); if (test_cases[test_case].test_case == NORMAL) { if (TST_RET) tst_res(TFAIL | TERRNO, "pivot_root failed"); else tst_res(TPASS, "pivot_root succeeded"); return; } if (TST_RET == 0) { tst_res(TFAIL, "pivot_root succeeded unexpectedly"); return; } if (errno != test_cases[test_case].expected_error) { tst_res(TFAIL | TERRNO, "pivot_root failed with wrong errno"); return; } tst_res(TPASS | TERRNO, "pivot_root failed as expectedly"); }
static void child(void) { size_t i; char *ptr; unsigned int usage, old_limit, old_memsw_limit; int status, pid, retries = 0; SAFE_MKDIR(cgroup_path, 0777); SAFE_FILE_PRINTF(tasks_path, "%i", getpid()); ptr = SAFE_MMAP(NULL, PAGES * page_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); for (i = 0; i < PAGES * page_size; i++) ptr[i] = 'a'; if (madvise(ptr, PAGES * page_size, MADV_FREE)) { if (errno == EINVAL) tst_brk(TCONF | TERRNO, "MADV_FREE is not supported"); tst_brk(TBROK | TERRNO, "MADV_FREE failed"); } if (ptr[page_size] != 'a') tst_res(TFAIL, "MADV_FREE pages were freed immediatelly"); else tst_res(TPASS, "MADV_FREE pages were not freed immediatelly"); ptr[TOUCHED_PAGE1 * page_size] = 'b'; ptr[TOUCHED_PAGE2 * page_size] = 'b'; usage = 8 * 1024 * 1024; tst_res(TINFO, "Setting memory limits to %u %u", usage, 2 * usage); SAFE_FILE_SCANF(limit_in_bytes_path, "%u", &old_limit); if (swap_accounting_enabled) SAFE_FILE_SCANF(memsw_limit_in_bytes_path, "%u", &old_memsw_limit); SAFE_FILE_PRINTF(limit_in_bytes_path, "%u", usage); if (swap_accounting_enabled) SAFE_FILE_PRINTF(memsw_limit_in_bytes_path, "%u", 2 * usage); do { sleep_between_faults++; pid = SAFE_FORK(); if (!pid) memory_pressure_child(); tst_res(TINFO, "Memory hungry child %i started, try %i", pid, retries); SAFE_WAIT(&status); } while (retries++ < 10 && count_freed(ptr) == 0); char map[PAGES+1]; unsigned int freed = 0; unsigned int corrupted = 0; for (i = 0; i < PAGES; i++) { char exp_val; if (ptr[i * page_size]) { exp_val = 'a'; map[i] = 'p'; } else { exp_val = 0; map[i] = '_'; freed++; } if (i != TOUCHED_PAGE1 && i != TOUCHED_PAGE2) { if (check_page(ptr + i * page_size, exp_val)) { map[i] = '?'; corrupted++; } } else { if (check_page_baaa(ptr + i * page_size)) { map[i] = '?'; corrupted++; } } } map[PAGES] = '\0'; tst_res(TINFO, "Memory map: %s", map); if (freed) tst_res(TPASS, "Pages MADV_FREE were freed on low memory"); else tst_res(TFAIL, "No MADV_FREE page was freed on low memory"); if (corrupted) tst_res(TFAIL, "Found corrupted page"); else tst_res(TPASS, "All pages have expected content"); if (swap_accounting_enabled) SAFE_FILE_PRINTF(memsw_limit_in_bytes_path, "%u", old_memsw_limit); SAFE_FILE_PRINTF(limit_in_bytes_path, "%u", old_limit); SAFE_MUNMAP(ptr, PAGES); exit(0); }