xref: /xnu-8019.80.24/tests/trial_experiments.c (revision a325d9c4a84054e40bbe985afedcb50ab80993ea) !
1 #include <errno.h>
2 #include <stdlib.h>
3 #include <libgen.h>
4 #include <limits.h>
5 #include <mach-o/dyld.h>
6 #include <sys/types.h>
7 #include <sys/sysctl.h>
8 #include <xlocale.h>
9 
10 #include <darwintest.h>
11 #include <darwintest_utils.h>
12 
13 #include "drop_priv.h"
14 #include "test_utils.h"
15 
16 #if ENTITLED
17 #define SET_TREATMENT_ID set_treatment_id_entitled
18 #define SET_TREATMENT_ID_DESCR "Can set treatment id with entitlement"
19 #else /* ENTITLED */
20 #define SET_TREATMENT_ID set_treatment_id_unentitled
21 #define SET_TREATMENT_ID_DESCR "Can't set treatment id without entitlement"
22 #endif /* ENTITLED */
23 
24 T_DECL(SET_TREATMENT_ID, "Verifies that EXPERIMENT sysctls can only be set with the entitlement", T_META_ASROOT(false))
25 {
26 #define TEST_STR "testing"
27 #define IDENTIFIER_LENGTH 36
28 
29 	int ret;
30 	errno_t err;
31 	char val[IDENTIFIER_LENGTH + 1] = {0};
32 	size_t len = sizeof(val);
33 	char new_val[IDENTIFIER_LENGTH + 1] = {0};
34 
35 	if (!is_development_kernel()) {
36 		T_SKIP("skipping test on release kernel");
37 	}
38 
39 	strlcpy(new_val, TEST_STR, sizeof(new_val));
40 	drop_priv();
41 
42 	ret = sysctlbyname("kern.trial_treatment_id", val, &len, new_val, strlen(new_val));
43 	err = errno;
44 #if ENTITLED
45 	len = sizeof(val);
46 	memset(new_val, 0, sizeof(new_val));
47 	T_ASSERT_POSIX_SUCCESS(ret, "set kern.trial_treatment_id");
48 	/* Cleanup. Set it back to the empty string. */
49 	ret = sysctlbyname("kern.trial_treatment_id", val, &len, new_val, 1);
50 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "reset kern.trial_treatment_id");
51 #else
52 	T_ASSERT_POSIX_FAILURE(ret, EPERM, "set kern.trial_treatment_id");
53 #endif /* ENTITLED */
54 }
55 
56 #if ENTITLED
57 /* Check min and max value limits on numeric factors */
58 T_DECL(experiment_factor_numeric_limits,
59     "Can only set factors within the legal range.",
60     T_META_ASROOT(false))
61 {
62 #define kMinVal 5 /* The min value allowed for the testing factor. */
63 #define kMaxVal 10 /* The max value allowed for the testing factor. */
64 	errno_t err;
65 	int ret;
66 	unsigned int current_val;
67 	size_t len = sizeof(current_val);
68 	unsigned int new_val;
69 
70 	drop_priv();
71 	new_val = kMinVal - 1;
72 	ret = sysctlbyname("kern.testing_experiment_factor", &current_val, &len, &new_val, sizeof(new_val));
73 	err = errno;
74 	T_ASSERT_POSIX_FAILURE(ret, EINVAL, "set kern.testing_experiment_factor below range.");
75 
76 	new_val = kMaxVal + 1;
77 	ret = sysctlbyname("kern.testing_experiment_factor", &current_val, &len, &new_val, sizeof(new_val));
78 	err = errno;
79 	T_ASSERT_POSIX_FAILURE(ret, EINVAL, "set kern.testing_experiment_factor above range.");
80 
81 	new_val = kMaxVal;
82 	ret = sysctlbyname("kern.testing_experiment_factor", &current_val, &len, &new_val, sizeof(new_val));
83 	T_ASSERT_POSIX_SUCCESS(ret, "set kern.testing_experiment_factor at top of range.");
84 
85 	new_val = kMinVal;
86 	ret = sysctlbyname("kern.testing_experiment_factor", &current_val, &len, &new_val, sizeof(new_val));
87 	T_ASSERT_POSIX_SUCCESS(ret, "set kern.testing_experiment_factor at bottom of range.");
88 }
89 
90 static uint64_t original_libmalloc_experiment_value = 0;
91 
92 static void
reset_libmalloc_experiment()93 reset_libmalloc_experiment()
94 {
95 	int ret = sysctlbyname("kern.libmalloc_experiments", NULL, NULL, &original_libmalloc_experiment_value, sizeof(original_libmalloc_experiment_value));
96 	T_ASSERT_POSIX_SUCCESS(ret, "reset kern.libmalloc_experiments");
97 }
98 
99 static void
set_libmalloc_experiment(uint64_t val)100 set_libmalloc_experiment(uint64_t val)
101 {
102 	T_LOG("Setting kern.libmalloc_experiments to %llu", val);
103 	size_t len = sizeof(original_libmalloc_experiment_value);
104 	int ret = sysctlbyname("kern.libmalloc_experiments", &original_libmalloc_experiment_value, &len, &val, sizeof(val));
105 	T_ASSERT_POSIX_SUCCESS(ret, "set kern.libmalloc_experiments");
106 	T_ATEND(reset_libmalloc_experiment);
107 }
108 
109 #define PRINT_APPLE_ARRAY_TOOL "tools/print_apple_array"
110 /*
111  * Spawns a new binary and returns the contents of its apple array
112  * (after libsystem initialization).
113  */
114 static char **
get_apple_array(size_t * num_array_entries)115 get_apple_array(size_t *num_array_entries)
116 {
117 	int ret;
118 	char stdout_path[MAXPATHLEN] = "apple_array.txt";
119 	dt_resultfile(stdout_path, MAXPATHLEN);
120 	int exit_status = 0, signum = 0;
121 	char binary_path[MAXPATHLEN], binary_dir[MAXPATHLEN];
122 	char *char_ret;
123 	const static size_t kMaxNumArguments = 256;
124 	size_t linecap = 0;
125 	ssize_t linelen = 0;
126 	char **apple_array;
127 	char **line = NULL;
128 	size_t num_lines = 0;
129 	FILE *stdout_f = NULL;
130 	uint32_t name_size = MAXPATHLEN;
131 
132 	ret = _NSGetExecutablePath(binary_path, &name_size);
133 	T_QUIET; T_ASSERT_EQ(ret, 0, "_NSGetExecutablePath");
134 	char_ret = dirname_r(binary_path, binary_dir);
135 	T_QUIET; T_ASSERT_TRUE(char_ret != NULL, "dirname_r");
136 	snprintf(binary_path, MAXPATHLEN, "%s/%s", binary_dir, PRINT_APPLE_ARRAY_TOOL);
137 
138 	char *launch_tool_args[] = {
139 		binary_path,
140 		NULL
141 	};
142 	pid_t child_pid;
143 	ret = dt_launch_tool(&child_pid, launch_tool_args, false, stdout_path, NULL);
144 	T_WITH_ERRNO; T_ASSERT_EQ(ret, 0, "dt_launch_tool: %s", binary_path);
145 
146 	ret = dt_waitpid(child_pid, &exit_status, &signum, 60 * 5);
147 	T_ASSERT_EQ(ret, 1, "dt_waitpid");
148 	T_QUIET; T_ASSERT_EQ(exit_status, 0, "dt_waitpid: exit_status");
149 	T_QUIET; T_ASSERT_EQ(signum, 0, "dt_waitpid: signum");
150 
151 	stdout_f = fopen(stdout_path, "r");
152 	T_WITH_ERRNO; T_ASSERT_NOTNULL(stdout_f, "open(%s)", stdout_path);
153 	apple_array = calloc(kMaxNumArguments, sizeof(char *));
154 	T_QUIET; T_ASSERT_NOTNULL(apple_array, "calloc: %lu\n", sizeof(char *) * kMaxNumArguments);
155 	while (num_lines < kMaxNumArguments) {
156 		line = &(apple_array[num_lines++]);
157 		linecap = 0;
158 		linelen = getline(line, &linecap, stdout_f);
159 		if (linelen == -1) {
160 			break;
161 		}
162 	}
163 	*num_array_entries = num_lines - 1;
164 
165 	ret = fclose(stdout_f);
166 	T_ASSERT_POSIX_SUCCESS(ret, "fclose(%s)", stdout_path);
167 
168 	return apple_array;
169 }
170 
171 #define LIBMALLOC_EXPERIMENT_FACTORS_KEY "MallocExperiment="
172 
173 /*
174  * Get the value of the MallocExperiment key in the apple array.
175  * Returns true iff the key is present.
176  */
177 static bool
get_libmalloc_experiment_factors(char ** apple_array,size_t num_array_entries,uint64_t * factors)178 get_libmalloc_experiment_factors(char **apple_array, size_t num_array_entries, uint64_t *factors)
179 {
180 	bool found = false;
181 	for (size_t i = 0; i < num_array_entries; i++) {
182 		char *str = apple_array[i];
183 		if (strstr(str, LIBMALLOC_EXPERIMENT_FACTORS_KEY)) {
184 			found = true;
185 			if (factors != NULL) {
186 				str = strchr(str, '=');
187 				T_ASSERT_NOTNULL(str, "skip over =");
188 				++str;
189 				*factors = strtoull_l(str, NULL, 16, NULL);
190 			}
191 			break;
192 		}
193 	}
194 	return found;
195 }
196 
197 T_DECL(libmalloc_experiment,
198     "libmalloc experiment flags show up in apple array if we're doing an experiment",
199     T_META_ASROOT(false))
200 {
201 	uint64_t new_val, apple_array_val = 0;
202 	size_t num_array_entries = 0;
203 	char **apple_array;
204 	bool found = false;
205 
206 	drop_priv();
207 	new_val = (1ULL << 63) - 1;
208 	set_libmalloc_experiment(new_val);
209 
210 	apple_array = get_apple_array(&num_array_entries);
211 	found = get_libmalloc_experiment_factors(apple_array, num_array_entries, &apple_array_val);
212 	T_ASSERT_TRUE(found, "Found " LIBMALLOC_EXPERIMENT_FACTORS_KEY " in apple array");
213 	T_ASSERT_EQ(apple_array_val, new_val, "Experiment value matches");
214 	free(apple_array);
215 }
216 
217 T_DECL(libmalloc_experiment_not_in_array,
218     "libmalloc experiment flags do not show up in apple array if we're not doing an experiment",
219     T_META_ASROOT(false))
220 {
221 	size_t num_array_entries = 0;
222 	char **apple_array;
223 	bool found = false;
224 
225 	drop_priv();
226 	set_libmalloc_experiment(0);
227 
228 	apple_array = get_apple_array(&num_array_entries);
229 	found = get_libmalloc_experiment_factors(apple_array, num_array_entries, NULL);
230 	T_ASSERT_TRUE(!found, "Did not find " LIBMALLOC_EXPERIMENT_FACTORS_KEY " in apple array");
231 	free(apple_array);
232 }
233 #endif /* ENTITLED */
234