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