1 #include "mach_vm_tests.h"
2
3 T_GLOBAL_META(
4 T_META_NAMESPACE("xnu.vm"),
5 T_META_RADAR_COMPONENT_NAME("xnu"),
6 T_META_RADAR_COMPONENT_VERSION("VM"));
7
8 extern char **environ;
9
10 static void
11 spawn_process(char *action, char *serviceName, char *extraArg,
12 mach_port_t *server_Port, pid_t *serverPid, boolean_t use4k);
13
14 static void mach_client(void);
15
16 mach_port_t serverPort;
17 static pid_t serverPid;
18
19 boolean_t debug = TRUE;
20
21 void
spawn_process(char * action,char * serviceName,char * extraArg,mach_port_t * server_Port,pid_t * server_Pid,boolean_t use4k)22 spawn_process(char *action, char *serviceName, char *extraArg,
23 mach_port_t *server_Port, pid_t *server_Pid, boolean_t use4k)
24 {
25 char buffer[PATH_MAX];
26 char *argv[10] = {0};
27 int arg_index = 0;
28 pid_t pid = -1;
29 int r = proc_pidpath(getpid(), buffer, sizeof(buffer));
30 T_ASSERT_NE(r, -1, "proc_pidpath");
31 r = (int)strlcat(buffer, "_server", sizeof(buffer));
32 T_ASSERT_LT(r, (int)sizeof(buffer), "strlcat");
33
34 if (use4k) {
35 int supported = 0;
36 size_t supported_size = sizeof(supported);
37
38 r = sysctlbyname("debug.vm_mixed_pagesize_supported", &supported, &supported_size, NULL, 0);
39 if (r == 0 && supported) {
40 T_LOG("Using %s to spawn process with 4k", VM_SPAWN_TOOL);
41 argv[arg_index++] = VM_SPAWN_TOOL;
42 } else {
43 /*
44 * We didnt find debug.vm.mixed_page.supported OR its set to 0.
45 * Skip the test.
46 */
47 T_SKIP("Hardware doesn't support 4K pages, skipping test...");
48 exit(0);
49 }
50 }
51 argv[arg_index++] = (char *)&buffer[0];
52 argv[arg_index++] = (char *)action;
53 argv[arg_index++] = (char *)serviceName;
54 argv[arg_index++] = (char *)extraArg;
55 argv[arg_index++] = NULL;
56
57 printf("posix_spawn with argv: ");
58 for (r = 0; r <= arg_index; r++) {
59 printf("%s ", argv[r]);
60 }
61 printf("\n");
62
63 T_LOG("Spawning %s process(%s) with service name %s at %s\n", action, buffer, serviceName, buffer);
64
65
66 posix_spawn_file_actions_t actions;
67 posix_spawn_file_actions_init(&actions);
68
69 if (use4k) {
70 T_ASSERT_POSIX_ZERO(posix_spawn(&pid, VM_SPAWN_TOOL, &actions, NULL, argv, environ), "spawn %s", serviceName);
71 } else {
72 T_ASSERT_POSIX_ZERO(posix_spawn(&pid, buffer, &actions, NULL, argv, environ), "spawn %s", serviceName);
73 }
74 posix_spawn_file_actions_destroy(&actions);
75
76 kern_return_t ret;
77 mach_port_t servicePort;
78 int attempts = 0;
79 const int kMaxAttempts = 10;
80 do {
81 sleep(1);
82 ret = bootstrap_look_up(bootstrap_port, serviceName, &servicePort);
83 attempts++;
84 } while (ret == BOOTSTRAP_UNKNOWN_SERVICE && attempts < kMaxAttempts);
85
86 if (ret != KERN_SUCCESS) {
87 printf("ERROR: Failed bootstrap lookup for process with mach service name '%s': (%d) %s\n", serviceName, ret, mach_error_string(ret));
88 if (pid > 0) {
89 kill(pid, SIGKILL);
90 }
91 T_FAIL("Failed bootstrap lookup for process with mach service");
92 }
93
94 *server_Port = servicePort;
95 *server_Pid = pid;
96 T_LOG("Server pid=%d port 0x%x", pid, servicePort);
97 }
98
99 void
mach_client()100 mach_client()
101 {
102 mach_port_t replyPort;
103 T_ASSERT_POSIX_ZERO(mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &replyPort), "create recieve port");
104 T_ASSERT_POSIX_ZERO(mach_port_insert_right(mach_task_self(), replyPort, replyPort, MACH_MSG_TYPE_MAKE_SEND), "insert send port");
105
106 ipc_message_t message;
107 bzero(&message, sizeof(message));
108 message.header.msgh_id = 1;
109
110 message.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_COPY_SEND);
111 message.header.msgh_remote_port = serverPort;
112 message.header.msgh_local_port = replyPort;
113 message.header.msgh_size = sizeof(message);
114
115 /* reply creation is not necessary in this case.
116 * mach_msg_size_t replySize = sizeof(ipc_message_t) + sizeof(mach_msg_trailer_t) + 64;
117 * ipc_message_t *reply = calloc(1, replySize);
118 */
119 T_LOG("sending message to %d of size %u", message.header.msgh_remote_port, message.header.msgh_size);
120 kern_return_t ret = mach_msg(&message.header, MACH_SEND_MSG, message.header.msgh_size, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
121 T_ASSERT_MACH_SUCCESS(ret, "mach_msg to serverProcess");
122 mach_vm_client(replyPort);
123 T_LOG("Sending SIGKILL to server(%d)", serverPid);
124 kill(serverPid, SIGKILL);
125 }
126
127 T_DECL(memory_share_tests,
128 "test vm memory sharing between client and server process with different process PAGE_SIZE",
129 T_META_ASROOT(true), T_META_TAG_VM_PREFERRED)
130 {
131 boolean_t use4k = FALSE;
132 char serviceName[64];
133
134 struct sigaction action = {
135 .sa_handler = SIG_IGN,
136 .sa_flags = SA_NOCLDWAIT
137 };
138 sigaction(SIGCHLD, &action, NULL);
139
140 if (getenv("USE4K")) {
141 use4k = TRUE;
142 }
143
144 if (getenv("QUIET")) {
145 debug = FALSE;
146 }
147
148 T_LOG("running with use4k=%d debug=%d", use4k, (int)debug);
149
150 strcpy(serviceName, MACH_VM_TEST_SERVICE_NAME);
151
152 spawn_process("machserver", serviceName, NULL, &serverPort, &serverPid, use4k);
153 mach_client();
154 }
155
156 /*
157 * This posix spawn attribute used in the 4K test should only be valid on apple
158 * silicon macs. But we've had issues in the past where it would accidently
159 * trigger incorrect behavior on other platforms.
160 * So we run the test on all platforms to catch issues like that.
161 * On all non apple silcon mac platforms, this test should be identical to
162 * running without the 4K flag.
163 */
164 T_DECL_REF(memory_share_tests_4k, memory_share_tests, "vm memory sharing with 4k processes",
165 T_META_ENVVAR("USE4K=YES"),
166 T_META_ASROOT(true),
167 T_META_ENABLED(false /* rdar://133462123 */)
168 );
169