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 #define CORENAME_MAX_SIZE 512 53 54 static int dfd; 55 static void *fmem; 56 static char cpattern[CORENAME_MAX_SIZE]; 57 static int restore_cpattern; 58 59 static void setup(void) 60 { 61 char cwd[1024]; 62 char tmpcpattern[1048]; 63 char *fmemc; 64 int i; 65 unsigned int filter; 66 struct rlimit limit; 67 68 limit.rlim_max = RLIM_INFINITY; 69 limit.rlim_cur = limit.rlim_max; 70 SAFE_SETRLIMIT(RLIMIT_CORE, &limit); 71 72 switch (prctl(PR_GET_DUMPABLE)) { 73 case 0: 74 tst_brk(TCONF, "Process is not dumpable."); 75 case 1: 76 break; 77 default: 78 tst_brk(TBROK | TERRNO, "prctl(PR_GET_DUMPABLE)"); 79 } 80 81 SAFE_FILE_SCANF(CORE_FILTER, "%x", &filter); 82 if (!(0x1 & filter)) 83 tst_brk(TCONF, "Anonymous private memory is not dumpable."); 84 85 SAFE_FILE_SCANF(CORE_PATTERN, "%s[^\n]", cpattern); 86 restore_cpattern = 1; 87 tst_res(TINFO, "System core pattern is '%s'", cpattern); 88 89 SAFE_GETCWD(cwd, sizeof(cwd)); 90 snprintf(tmpcpattern, sizeof(tmpcpattern), "%s/dump-%%p", cwd); 91 tst_res(TINFO, "Temporary core pattern is '%s'", tmpcpattern); 92 SAFE_FILE_PRINTF(CORE_PATTERN, "%s", tmpcpattern); 93 94 fmem = SAFE_MMAP(NULL, 95 FMEMSIZE, 96 PROT_READ | PROT_WRITE, 97 MAP_ANONYMOUS | MAP_PRIVATE, 98 -1, 99 0); 100 101 /* 102 * Write a generated character sequence to the mapped memory, 103 * which we later look for in the core dump. 104 */ 105 fmemc = (char *)fmem; 106 *fmemc = 'x'; 107 for (i = 0; i < YCOUNT; i++) 108 fmemc[i + 1] = 'y'; 109 fmemc[++i] = 'z'; 110 } 111 112 static void cleanup(void) 113 { 114 if (restore_cpattern) 115 SAFE_FILE_PRINTF(CORE_PATTERN, "%s", cpattern); 116 117 if (fmem) 118 SAFE_MUNMAP(fmem, FMEMSIZE); 119 120 if (dfd > 0) 121 SAFE_CLOSE(dfd); 122 } 123 124 static int find_sequence(int pid) 125 { 126 char expectc = 'x'; 127 ssize_t read, pos = 0; 128 char rbuf[1024]; 129 int ycount = 0; 130 char dumpname[256]; 131 132 snprintf(dumpname, 256, "dump-%d", pid); 133 tst_res(TINFO, "Dump file should be %s", dumpname); 134 if (access(dumpname, F_OK)) 135 tst_brk(TBROK | TERRNO, "Dump file was not found."); 136 137 dfd = SAFE_OPEN(dumpname, O_RDONLY); 138 139 read = SAFE_READ(0, dfd, &rbuf, sizeof(rbuf)); 140 while (read) { 141 switch (rbuf[pos]) { 142 case 'x': 143 ycount = 0; 144 expectc = 'y'; 145 break; 146 case 'y': 147 if (expectc == 'y') { 148 ycount++; 149 } else { 150 expectc = 'x'; 151 break; 152 } 153 154 if (ycount == YCOUNT) 155 expectc = 'z'; 156 break; 157 case 'z': 158 if (expectc == 'z') { 159 SAFE_CLOSE(dfd); 160 return 1; 161 } 162 default: 163 expectc = 'x'; 164 } 165 if (++pos >= read) { 166 read = SAFE_READ(0, dfd, &rbuf, sizeof(rbuf)); 167 pos = 0; 168 } 169 } 170 171 SAFE_CLOSE(dfd); 172 return 0; 173 } 174 175 static pid_t run_child(int advice) 176 { 177 int status; 178 pid_t pid; 179 char *advstr = 180 advice == MADV_DONTDUMP ? "MADV_DONTDUMP" : "MADV_DODUMP"; 181 182 pid = SAFE_FORK(); 183 if (pid == 0) { 184 if (madvise(fmem, FMEMSIZE, advice) == -1) { 185 tst_res(TFAIL | TERRNO, 186 "madvise(%p, %lu, %s) = -1", 187 fmem, 188 FMEMSIZE, 189 advstr); 190 exit(1); 191 } 192 abort(); 193 } 194 195 SAFE_WAITPID(pid, &status, 0); 196 if (WIFSIGNALED(status) && WCOREDUMP(status)) 197 return pid; 198 if (WIFEXITED(status)) 199 return 0; 200 201 tst_res(TCONF, "No coredump produced after signal (%d)", 202 WTERMSIG(status)); 203 204 return 0; 205 } 206 207 static void run(unsigned int test_nr) 208 { 209 pid_t pid; 210 211 if (!test_nr) { 212 pid = run_child(MADV_DONTDUMP); 213 if (pid && find_sequence(pid)) 214 tst_res(TFAIL, 215 "Found sequence in dump when MADV_DONTDUMP set"); 216 else if (pid) 217 tst_res(TPASS, "madvise(..., MADV_DONTDUMP)"); 218 } else { 219 pid = run_child(MADV_DODUMP); 220 if (pid && find_sequence(pid)) 221 tst_res(TPASS, "madvise(..., MADV_DODUMP)"); 222 else if (pid) 223 tst_res(TFAIL, 224 "No sequence in dump after MADV_DODUMP."); 225 } 226 } 227 228 static struct tst_test test = { 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