xref: /xnu-11215/tests/ecc_test.c (revision 4f1223e8)
1 #include <unistd.h>
2 #include <stdio.h>
3 #include <signal.h>
4 
5 #include <darwintest.h>
6 #include <darwintest_utils.h>
7 
8 /*
9  * We're going to inject ECC errors into shared library text, so don't
10  * run with other tests.
11  */
12 T_GLOBAL_META(T_META_RUN_CONCURRENTLY(false),
13     T_META_OWNER("josephb_22"), T_META_OWNER("y_feigelson"),
14     T_META_NAMESPACE("xnu.vm"),
15     T_META_RADAR_COMPONENT_NAME("xnu"),
16     T_META_RADAR_COMPONENT_VERSION("VM"));
17 
18 /*
19  * No system(3c) on watchOS, so provide our own.
20  * returns -1 if fails to run
21  * returns 0 if process exits normally.
22  * returns +n if process exits due to signal N
23  */
24 static int
my_system(const char * command,const char * arg)25 my_system(const char *command, const char *arg)
26 {
27 	pid_t pid;
28 	int status = 0;
29 	int signal = 0;
30 	int ret;
31 	const char *argv[] = {
32 		command,
33 		"-v",
34 		arg,
35 		NULL
36 	};
37 
38 	if (dt_launch_tool(&pid, (char **)(void *)argv, FALSE, NULL, NULL)) {
39 		return -1;
40 	}
41 
42 	ret = dt_waitpid(pid, &status, &signal, 100);
43 	if (signal != 0) {
44 		return signal;
45 	} else if (status != 0) {
46 		return status;
47 	}
48 	return 0;
49 }
50 
51 static int
run_helper(const char * arg)52 run_helper(const char *arg)
53 {
54 	printf("\nNow running \"%s\":\n", arg);
55 	return my_system("./ecc_test_helper", arg);
56 }
57 
58 static void
cleanup_after_injections(void)59 cleanup_after_injections(void)
60 {
61 	(void)sysctlbyname("vm.retired_pages_end_test", NULL, NULL, NULL, 0);
62 }
63 
64 
65 /*
66  * The tests are run in the following order:
67  *
68  * - call foo (i.e. private TEXT page)
69  * - Inject ECC error into foo, then call foo
70  *
71  * - call atan (i.e. shared TEXT page)
72  * - inject ecc error into atan, then call atan
73  *
74  * atan() was picked as a shared region function that isn't likely used by any normal daemons.
75  *
76  * - reference to clean DATA page with injected error
77  *
78  * - reference to dirty DATA page with injected error
79  *
80  * - reference to clean private anonymous mmap'd page with injected error
81  *
82  * - reference to dirty private anonymous mmap'd page with injected error
83  *
84  * - copyout to a page with injected error
85  */
86 static void
test_body(void)87 test_body(void)
88 {
89 	int ret;
90 
91 	T_ATEND(cleanup_after_injections);
92 
93 	/*
94 	 * test of process TEXT page
95 	 * since the page is not writeable (therefore clean), we expect to recover
96 	 */
97 	ret = run_helper("Yfoo");
98 	T_QUIET; T_ASSERT_EQ(ret, 0, "First call of foo");
99 
100 	ret = run_helper("Xfoo");
101 	T_QUIET; T_ASSERT_EQ(ret, 0, "Failed to recover from UE in clean app text page");
102 
103 	ret = run_helper("Yfoo");
104 	T_QUIET; T_ASSERT_EQ(ret, 0, "Fixed call of foo");
105 
106 	/*
107 	 * test of shared library TEXT page
108 	 * since the page is not writeable (therefore clean), we expect to recover
109 	 */
110 	ret = run_helper("Yatan");
111 	T_QUIET; T_ASSERT_EQ(ret, 0, "First call of atan");
112 
113 	ret = run_helper("Xatan");
114 	T_QUIET; T_ASSERT_EQ(ret, 0, "Failed to recover from UE in clean shared region page");
115 
116 	ret = run_helper("Yatan");
117 	T_QUIET; T_ASSERT_EQ(ret, 0, "Fixed call of atan");
118 
119 	/*
120 	 * test of clean DATA page
121 	 * since the page is clean, we expect to recover
122 	 */
123 	ret = run_helper("Xclean");
124 	T_QUIET; T_ASSERT_EQ(ret, 0, "Failed to recover from UE in clean page");
125 
126 	/*
127 	 * test of dirty DATA page
128 	 * since the page is dirty, we expect the app to SIGBUS
129 	 */
130 	ret = run_helper("Xdirty");
131 	T_QUIET; T_ASSERT_NE(ret, 0, "Expected to fail from UE in dirty DATA page");
132 
133 	/*
134 	 * test of clean dynamically allocated page
135 	 * since the page is clean, we expect to recover
136 	 *
137 	 * Test is disabled - rdar://124132874 (XNU ECC unit tests - "Xmmap_clean" fails)
138 	 */
139 	// ret = run_helper("Xmmap_clean");
140 	// T_QUIET; T_ASSERT_EQ(ret, 0, "Failed to recover from ECC to clean dynamically allocated page");
141 
142 	/*
143 	 * test of dirty dynamically allocated page
144 	 * since the page is dirty, we expect the app to SIGBUS
145 	 */
146 	ret = run_helper("Xmmap_dirty");
147 	T_QUIET; T_ASSERT_NE(ret, 0, "Expected to fail from UE in dirty dynamically allocated page");
148 
149 	/*
150 	 * test of ecc during copyout
151 	 *
152 	 * although the page is dirty, the page fault error is handled by failing
153 	 * the copyout syscall.
154 	 */
155 	ret = run_helper("Xcopyout");
156 	T_QUIET; T_ASSERT_NE(ret, 0, "Uncorrected ECC copyout didn't fail");
157 }
158 
159 static void
cleanup_ecc_test(void)160 cleanup_ecc_test(void)
161 {
162 	uint value;
163 	size_t s = sizeof value;
164 
165 	// Set testing mode back to default(ACC)
166 	value = 0;
167 	(void)sysctlbyname("vm.test_ecc_dcs", NULL, NULL, &value, s);
168 
169 	// Restore side effects to default(enabled)
170 	value = 1;
171 	(void)sysctlbyname("vm.test_ecc_sideeffects", NULL, NULL, &value, s);
172 }
173 
174 static void
run_test(bool use_dcs)175 run_test(bool use_dcs)
176 {
177 	int err;
178 	uint value = 0;
179 	size_t s = sizeof value;
180 
181 	T_ATEND(cleanup_ecc_test);
182 
183 	// Set testing mode to ACC(0) or DCS(1)
184 	value = (uint)use_dcs;
185 	err = sysctlbyname("vm.test_ecc_dcs", NULL, NULL, &value, s);
186 	if (err) {
187 		T_SKIP("Failed to clear dcs mode");
188 	}
189 
190 	// Set testing mode to uncorrected.
191 	value = 0;
192 	err = sysctlbyname("vm.test_corrected_ecc", NULL, NULL, &value, s);
193 	if (err) {
194 		T_SKIP("Failed to set uncorrected mode");
195 	}
196 
197 	// Disable side effects for the duration of the test
198 	value = 0;
199 	err = sysctlbyname("vm.test_ecc_sideeffects", NULL, NULL, &value, s);
200 	if (err) {
201 		T_SKIP("Failed to disable side effects");
202 	}
203 
204 	test_body();
205 }
206 
207 T_DECL(ecc_uncorrected_test, "test detection and handling of non-fatal ECC uncorrected errors",
208     T_META_IGNORECRASHES(".*ecc_test_helper.*"),
209     T_META_ASROOT(true),
210     T_META_ENABLED(false /* TARGET_CPU_ARM64 && TARGET_OS_OSX */) /* rdar://133461215 */,
211     T_META_REQUIRES_SYSCTL_EQ("vm.retired_pages_end_test", 0),
212     T_META_TAG_VM_NOT_ELIGIBLE)
213 {
214 	run_test(false);
215 }
216 
217 /* DCS injection was fixed but then broke again..
218  * Waiting on rdar://115998013 (WRDIS_DRAM_RAS_ERR needs to be disabled for dev fused units)
219  */
220 #if 0
221 T_DECL(dcs_uncorrected_test, "test detection and handling from non-fatal ECC uncorrected errors (injected via DCS)",
222     T_META_IGNORECRASHES(".*ecc_test_helper.*"),
223     T_META_ASROOT(true),
224     T_META_ENABLED(TARGET_CPU_ARM64 && TARGET_OS_OSX),
225     T_META_REQUIRES_SYSCTL_EQ("vm.retired_pages_end_test", 0), T_META_TAG_VM_NOT_ELIGIBLE)
226 {
227 	run_test(true);
228 }
229 #endif
230