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 #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