/* * Copyright (c) 2016 Richard Palethorpe * Copyright (c) 2017 SUSE LLC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* * Check that memory marked with MADV_DONTDUMP is not included in a core dump * and check that the same memory then marked with MADV_DODUMP is included in * a core dump. * * In order to reliably find the core dump this test temporarily changes the * system wide core_pattern setting. Meaning all core dumps will be sent to the * test's temporary dir until the setting is restored during cleanup. * * Test flow: map memory, * write generated character sequence to memory, * start child process, * mark memory with MADV_DONTDUMP in child, * abort child, * scan child's core dump for character sequence, * if the sequence is not found it is a pass otherwise a fail, */ #include #include #include #include #include #include #include #include #include "tst_test.h" #include "lapi/mmap.h" #define CORE_PATTERN "/proc/sys/kernel/core_pattern" #define CORE_FILTER "/proc/self/coredump_filter" #define YCOUNT 0x500L #define FMEMSIZE (YCOUNT + 0x2L) #define CORENAME_MAX_SIZE 512 static int dfd; static void *fmem; static char cpattern[CORENAME_MAX_SIZE]; static int restore_cpattern; static void setup(void) { char cwd[1024]; char tmpcpattern[1048]; char *fmemc; int i; unsigned int filter; struct rlimit limit; limit.rlim_max = RLIM_INFINITY; limit.rlim_cur = limit.rlim_max; SAFE_SETRLIMIT(RLIMIT_CORE, &limit); switch (prctl(PR_GET_DUMPABLE)) { case 0: tst_brk(TCONF, "Process is not dumpable."); case 1: break; default: tst_brk(TBROK | TERRNO, "prctl(PR_GET_DUMPABLE)"); } SAFE_FILE_SCANF(CORE_FILTER, "%x", &filter); if (!(0x1 & filter)) tst_brk(TCONF, "Anonymous private memory is not dumpable."); SAFE_FILE_SCANF(CORE_PATTERN, "%s[^\n]", cpattern); restore_cpattern = 1; tst_res(TINFO, "System core pattern is '%s'", cpattern); SAFE_GETCWD(cwd, sizeof(cwd)); snprintf(tmpcpattern, sizeof(tmpcpattern), "%s/dump-%%p", cwd); tst_res(TINFO, "Temporary core pattern is '%s'", tmpcpattern); SAFE_FILE_PRINTF(CORE_PATTERN, "%s", tmpcpattern); fmem = SAFE_MMAP(NULL, FMEMSIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); /* * Write a generated character sequence to the mapped memory, * which we later look for in the core dump. */ fmemc = (char *)fmem; *fmemc = 'x'; for (i = 0; i < YCOUNT; i++) fmemc[i + 1] = 'y'; fmemc[++i] = 'z'; } static void cleanup(void) { if (restore_cpattern) SAFE_FILE_PRINTF(CORE_PATTERN, "%s", cpattern); if (fmem) SAFE_MUNMAP(fmem, FMEMSIZE); if (dfd > 0) SAFE_CLOSE(dfd); } static int find_sequence(int pid) { char expectc = 'x'; ssize_t read, pos = 0; char rbuf[1024]; int ycount = 0; char dumpname[256]; snprintf(dumpname, 256, "dump-%d", pid); tst_res(TINFO, "Dump file should be %s", dumpname); if (access(dumpname, F_OK)) tst_brk(TBROK | TERRNO, "Dump file was not found."); dfd = SAFE_OPEN(dumpname, O_RDONLY); read = SAFE_READ(0, dfd, &rbuf, sizeof(rbuf)); while (read) { switch (rbuf[pos]) { case 'x': ycount = 0; expectc = 'y'; break; case 'y': if (expectc == 'y') { ycount++; } else { expectc = 'x'; break; } if (ycount == YCOUNT) expectc = 'z'; break; case 'z': if (expectc == 'z') { SAFE_CLOSE(dfd); return 1; } default: expectc = 'x'; } if (++pos >= read) { read = SAFE_READ(0, dfd, &rbuf, sizeof(rbuf)); pos = 0; } } SAFE_CLOSE(dfd); return 0; } static pid_t run_child(int advice) { int status; pid_t pid; char *advstr = advice == MADV_DONTDUMP ? "MADV_DONTDUMP" : "MADV_DODUMP"; pid = SAFE_FORK(); if (pid == 0) { if (madvise(fmem, FMEMSIZE, advice) == -1) { tst_res(TFAIL | TERRNO, "madvise(%p, %lu, %s) = -1", fmem, FMEMSIZE, advstr); exit(1); } abort(); } SAFE_WAITPID(pid, &status, 0); if (WIFSIGNALED(status) && WCOREDUMP(status)) return pid; if (WIFEXITED(status)) return 0; tst_res(TCONF, "No coredump produced after signal (%d)", WTERMSIG(status)); return 0; } static void run(unsigned int test_nr) { pid_t pid; if (!test_nr) { pid = run_child(MADV_DONTDUMP); if (pid && find_sequence(pid)) tst_res(TFAIL, "Found sequence in dump when MADV_DONTDUMP set"); else if (pid) tst_res(TPASS, "madvise(..., MADV_DONTDUMP)"); } else { pid = run_child(MADV_DODUMP); if (pid && find_sequence(pid)) tst_res(TPASS, "madvise(..., MADV_DODUMP)"); else if (pid) tst_res(TFAIL, "No sequence in dump after MADV_DODUMP."); } } static struct tst_test test = { .test = run, .tcnt = 2, .setup = setup, .cleanup = cleanup, .min_kver = "3.4.0", .needs_tmpdir = 1, .needs_root = 1, .forks_child = 1 };