1 /* 2 * Copyright (c) 2016 Richard Palethorpe <richiejp (at) f-m.fm> 3 * Copyright (c) 2017 SUSE LLC 4 * 5 * This program is free software: you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation, either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 */ 18 /* 19 * Check that memory marked with MADV_DONTDUMP is not included in a core dump 20 * and check that the same memory then marked with MADV_DODUMP is included in 21 * a core dump. 22 * 23 * In order to reliably find the core dump this test temporarily changes the 24 * system wide core_pattern setting. Meaning all core dumps will be sent to the 25 * test's temporary dir until the setting is restored during cleanup. 26 * 27 * Test flow: map memory, 28 * write generated character sequence to memory, 29 * start child process, 30 * mark memory with MADV_DONTDUMP in child, 31 * abort child, 32 * scan child's core dump for character sequence, 33 * if the sequence is not found it is a pass otherwise a fail, 34 */ 35 36 #include <sys/types.h> 37 #include <sys/wait.h> 38 #include <sys/prctl.h> 39 #include <fcntl.h> 40 #include <unistd.h> 41 #include <signal.h> 42 #include <stdlib.h> 43 #include <stdio.h> 44 45 #include "tst_test.h" 46 #include "lapi/mmap.h" 47 48 #define CORE_PATTERN "/proc/sys/kernel/core_pattern" 49 #define CORE_FILTER "/proc/self/coredump_filter" 50 #define YCOUNT 0x500L 51 #define FMEMSIZE (YCOUNT + 0x2L) 52 53 static int dfd; 54 static void *fmem; 55 static char *cpattern; 56 57 static void setup(void) 58 { 59 char cwd[1024]; 60 char tmpcpattern[1048]; 61 char *fmemc; 62 int i; 63 unsigned int filter; 64 struct rlimit limit; 65 66 limit.rlim_max = RLIM_INFINITY; 67 limit.rlim_cur = limit.rlim_max; 68 SAFE_SETRLIMIT(RLIMIT_CORE, &limit); 69 70 switch (prctl(PR_GET_DUMPABLE)) { 71 case 0: 72 tst_brk(TCONF, "Process is not dumpable."); 73 case 1: 74 break; 75 default: 76 tst_brk(TBROK | TERRNO, "prctl(PR_GET_DUMPABLE)"); 77 } 78 79 SAFE_FILE_SCANF(CORE_FILTER, "%x", &filter); 80 if (!(0x1 & filter)) 81 tst_brk(TCONF, "Anonymous private memory is not dumpable."); 82 83 SAFE_FILE_SCANF(CORE_PATTERN, "%m[^\n]", &cpattern); 84 tst_res(TINFO, "System core pattern is '%s'", cpattern); 85 86 SAFE_GETCWD(cwd, sizeof(cwd)); 87 snprintf(tmpcpattern, sizeof(tmpcpattern), "%s/dump-%%p", cwd); 88 tst_res(TINFO, "Temporary core pattern is '%s'", tmpcpattern); 89 SAFE_FILE_PRINTF(CORE_PATTERN, "%s", tmpcpattern); 90 91 fmem = SAFE_MMAP(NULL, 92 FMEMSIZE, 93 PROT_READ | PROT_WRITE, 94 MAP_ANONYMOUS | MAP_PRIVATE, 95 -1, 96 0); 97 98 /* 99 * Write a generated character sequence to the mapped memory, 100 * which we later look for in the core dump. 101 */ 102 fmemc = (char *)fmem; 103 *fmemc = 'x'; 104 for (i = 0; i < YCOUNT; i++) 105 fmemc[i + 1] = 'y'; 106 fmemc[++i] = 'z'; 107 } 108 109 static void cleanup(void) 110 { 111 if (cpattern) 112 SAFE_FILE_PRINTF(CORE_PATTERN, "%s", cpattern); 113 114 free(cpattern); 115 116 if (fmem) 117 SAFE_MUNMAP(fmem, FMEMSIZE); 118 119 if (dfd > 0) 120 SAFE_CLOSE(dfd); 121 } 122 123 static int find_sequence(int pid) 124 { 125 char expectc = 'x'; 126 ssize_t read, pos = 0; 127 char rbuf[1024]; 128 int ycount = 0; 129 char dumpname[256]; 130 131 snprintf(dumpname, 256, "dump-%d", pid); 132 tst_res(TINFO, "Dump file should be %s", dumpname); 133 if (access(dumpname, F_OK)) 134 tst_brk(TBROK | TERRNO, "Dump file was not found."); 135 136 dfd = SAFE_OPEN(dumpname, O_RDONLY); 137 138 read = SAFE_READ(0, dfd, &rbuf, sizeof(rbuf)); 139 while (read) { 140 switch (rbuf[pos]) { 141 case 'x': 142 ycount = 0; 143 expectc = 'y'; 144 break; 145 case 'y': 146 if (expectc == 'y') { 147 ycount++; 148 } else { 149 expectc = 'x'; 150 break; 151 } 152 153 if (ycount == YCOUNT) 154 expectc = 'z'; 155 break; 156 case 'z': 157 if (expectc == 'z') { 158 SAFE_CLOSE(dfd); 159 return 1; 160 } 161 default: 162 expectc = 'x'; 163 } 164 if (++pos >= read) { 165 read = SAFE_READ(0, dfd, &rbuf, sizeof(rbuf)); 166 pos = 0; 167 } 168 } 169 170 SAFE_CLOSE(dfd); 171 return 0; 172 } 173 174 static pid_t run_child(int advice) 175 { 176 int status; 177 pid_t pid; 178 char *advstr = 179 advice == MADV_DONTDUMP ? "MADV_DONTDUMP" : "MADV_DODUMP"; 180 181 pid = SAFE_FORK(); 182 if (pid == 0) { 183 if (madvise(fmem, FMEMSIZE, advice) == -1) { 184 tst_res(TFAIL | TERRNO, 185 "madvise(%p, %lu, %s) = -1", 186 fmem, 187 FMEMSIZE, 188 advstr); 189 exit(1); 190 } 191 abort(); 192 } 193 194 SAFE_WAITPID(pid, &status, 0); 195 if (WIFSIGNALED(status) && WCOREDUMP(status)) 196 return pid; 197 if (WIFEXITED(status)) 198 return 0; 199 200 tst_res(TCONF, "No coredump produced after signal (%d)", 201 WTERMSIG(status)); 202 203 return 0; 204 } 205 206 static void run(unsigned int test_nr) 207 { 208 pid_t pid; 209 210 if (!test_nr) { 211 pid = run_child(MADV_DONTDUMP); 212 if (pid && find_sequence(pid)) 213 tst_res(TFAIL, 214 "Found sequence in dump when MADV_DONTDUMP set"); 215 else if (pid) 216 tst_res(TPASS, "madvise(..., MADV_DONTDUMP)"); 217 } else { 218 pid = run_child(MADV_DODUMP); 219 if (pid && find_sequence(pid)) 220 tst_res(TPASS, "madvise(..., MADV_DODUMP)"); 221 else if (pid) 222 tst_res(TFAIL, 223 "No sequence in dump after MADV_DODUMP."); 224 } 225 } 226 227 static struct tst_test test = { 228 .tid = "madvise08", 229 .test = run, 230 .tcnt = 2, 231 .setup = setup, 232 .cleanup = cleanup, 233 .min_kver = "3.4.0", 234 .needs_tmpdir = 1, 235 .needs_root = 1, 236 .forks_child = 1 237 }; 238