1 /*
2 * Copyright (c) 2016 Richard Palethorpe <richiejp@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
setup(void)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
cleanup(void)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
find_sequence(int pid)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
run_child(int advice)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
run(unsigned int test_nr)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