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