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 memory marked with MADV_HWPOISON results in SIGBUS.
20 *
21 * Test flow: create child process,
22 * map memory,
23 * mark memory with MADV_HWPOISON inside child process,
24 * access memory,
25 * if SIGBUS is delivered to child the test passes else it fails
26 *
27 * When MAP_PRIVATE is set (without MAP_POPULATE) madvise() may error with
28 * EBUSY on the first attempt and succeed on the second, but without poisoning
29 * any memory. A private mapping is only populated with pages once it is
30 * accessed and poisoning an unmapped VM range is essentially undefined
31 * behaviour. However madvise() itself causes the address to be mapped to the
32 * zero page. If/when the zero page can be poisoned then the test may pass
33 * without any error. For now we just consider it a configuration failure.
34 */
35
36 #include <stdlib.h>
37 #include <stdio.h>
38 #include <sys/wait.h>
39 #include <fcntl.h>
40 #include <unistd.h>
41 #include <signal.h>
42 #include <errno.h>
43
44 #include "tst_test.h"
45 #include "lapi/mmap.h"
46
47 static int maptypes[] = {
48 MAP_PRIVATE,
49 MAP_PRIVATE | MAP_POPULATE,
50 MAP_SHARED
51 };
52
mapname(int maptype)53 static char *mapname(int maptype)
54 {
55 switch (maptype) {
56 case MAP_PRIVATE: return "MAP_SHARED";
57 case MAP_PRIVATE | MAP_POPULATE: return "MAP_PRIVATE | MAP_POPULATE";
58 case MAP_SHARED: return "MAP_SHARED";
59 default:
60 tst_res(TWARN, "Unknown map type: %d", maptype);
61 }
62 return "Unknown";
63 }
64
run_child(int maptype)65 static void run_child(int maptype)
66 {
67 const size_t msize = getpagesize();
68 void *mem = NULL;
69 int first_attempt = 1;
70
71 tst_res(TINFO,
72 "mmap(..., MAP_ANONYMOUS | %s, ...)", mapname(maptype));
73 mem = SAFE_MMAP(NULL,
74 msize,
75 PROT_READ | PROT_WRITE,
76 MAP_ANONYMOUS | maptype,
77 -1,
78 0);
79
80 do_madvise:
81 tst_res(TINFO, "madvise(%p, %zu, MADV_HWPOISON)", mem, msize);
82 if (madvise(mem, msize, MADV_HWPOISON) == -1) {
83 if (errno == EINVAL) {
84 tst_res(TCONF | TERRNO,
85 "CONFIG_MEMORY_FAILURE probably not set in kconfig");
86 } else if (errno == EBUSY && maptype == MAP_PRIVATE) {
87 tst_res(TCONF,
88 "Madvise failed with EBUSY");
89 if (first_attempt--)
90 goto do_madvise;
91 } else {
92 tst_res(TFAIL | TERRNO, "Could not poison memory");
93 }
94 exit(0);
95 }
96
97 *((char *)mem) = 'd';
98
99 if (maptype == MAP_PRIVATE) {
100 tst_res(TCONF,
101 "Zero page poisoning is probably not implemented");
102 } else {
103 tst_res(TFAIL,
104 "Did not receive SIGBUS after accessing %s memory marked with MADV_HWPOISON",
105 mapname(maptype));
106 }
107 }
108
run(unsigned int n)109 static void run(unsigned int n)
110 {
111 int status;
112 pid_t pid;
113
114 pid = SAFE_FORK();
115 if (pid == 0) {
116 run_child(maptypes[n]);
117 exit(0);
118 }
119
120 SAFE_WAITPID(pid, &status, 0);
121 if (WIFSIGNALED(status) && WTERMSIG(status) == SIGBUS)
122 tst_res(TPASS,
123 "madvise(..., MADV_HWPOISON) on %s memory",
124 mapname(maptypes[n]));
125 else if (WIFEXITED(status) && WEXITSTATUS(status) == TBROK)
126 tst_res(TBROK, "Child exited abnormally");
127 }
128
129 static struct tst_test test = {
130 .tid = "madvise07",
131 .test = run,
132 .tcnt = ARRAY_SIZE(maptypes),
133 .min_kver = "2.6.31",
134 .needs_root = 1,
135 .forks_child = 1
136 };
137
138