Home | History | Annotate | Download | only in madvise
      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