• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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