• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2016 Richard Palethorpe <richiejp@f-m.fm>
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 /*
19  * Check that accessing a page marked with MADV_HWPOISON results in SIGBUS.
20  *
21  * Test flow: create child process,
22  *	      map and write to memory,
23  *	      mark memory with MADV_HWPOISON,
24  *	      access memory,
25  *	      if SIGBUS is delivered to child the test passes else it fails
26  *
27  * If the underlying page type of the memory we have mapped does not support
28  * poisoning then the test will fail. We try to map and write to the memory in
29  * such a way that by the time madvise is called the virtual memory address
30  * points to a supported page. However there may be some rare circumstances
31  * where the test produces the wrong result because we have somehow obtained
32  * an unsupported page. In such cases madvise will probably return success,
33  * but no SIGBUS will be produced.
34  *
35  * For more information see <linux source>/Documentation/vm/hwpoison.txt.
36  */
37 
38 #include <stdlib.h>
39 #include <stdio.h>
40 #include <sys/wait.h>
41 #include <fcntl.h>
42 #include <unistd.h>
43 #include <signal.h>
44 #include <errno.h>
45 #include <string.h>
46 
47 #include "tst_test.h"
48 #include "lapi/mmap.h"
49 
run_child(void)50 static void run_child(void)
51 {
52 	const size_t msize = getpagesize();
53 	void *mem = NULL;
54 
55 	tst_res(TINFO,
56 		"mmap(0, %zu, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)",
57 		msize);
58 	mem = SAFE_MMAP(NULL,
59 			msize,
60 			PROT_READ | PROT_WRITE,
61 			MAP_ANONYMOUS | MAP_PRIVATE,
62 			-1,
63 			0);
64 	memset(mem, 'L', msize);
65 
66 	tst_res(TINFO, "madvise(%p, %zu, MADV_HWPOISON)", mem, msize);
67 	if (madvise(mem, msize, MADV_HWPOISON) == -1) {
68 		if (errno == EINVAL) {
69 			tst_res(TCONF | TERRNO,
70 				"CONFIG_MEMORY_FAILURE probably not set in kconfig");
71 		} else {
72 			tst_res(TFAIL | TERRNO, "Could not poison memory");
73 		}
74 		exit(0);
75 	}
76 
77 	*((char *)mem) = 'd';
78 
79 	tst_res(TFAIL, "Did not receive SIGBUS on accessing poisoned page");
80 }
81 
run(void)82 static void run(void)
83 {
84 	int status;
85 	pid_t pid;
86 
87 	pid = SAFE_FORK();
88 	if (pid == 0) {
89 		run_child();
90 		exit(0);
91 	}
92 
93 	SAFE_WAITPID(pid, &status, 0);
94 	if (WIFSIGNALED(status) && WTERMSIG(status) == SIGBUS) {
95 		tst_res(TPASS, "Received SIGBUS after accessing poisoned page");
96 		return;
97 	}
98 
99 	if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
100 		return;
101 
102 	tst_res(TFAIL, "Child %s", tst_strstatus(status));
103 }
104 
105 static struct tst_test test = {
106 	.test_all = run,
107 	.min_kver = "2.6.31",
108 	.needs_root = 1,
109 	.forks_child = 1
110 };
111 
112