1 #include <darwintest.h>
2 #include <darwintest_utils.h>
3 #include <mach/mach.h>
4 #include <mach/mach_vm.h>
5 #include <stdlib.h>
6 #include <string.h>
7
8 #define KB4 ((mach_vm_size_t)4*1024)
9 #define KB16 ((mach_vm_size_t)16*1024)
10
11 T_GLOBAL_META(
12 T_META_NAMESPACE("xnu.vm"),
13 T_META_RADAR_COMPONENT_NAME("xnu"),
14 T_META_RADAR_COMPONENT_VERSION("VM"),
15 T_META_ALL_VALID_ARCHS(true));
16
17 #ifdef __x86_64__
18 // return true if the process is running under Rosetta translation
19 // https://developer.apple.com/documentation/apple-silicon/about-the-rosetta-translation-environment#Determine-Whether-Your-App-Is-Running-as-a-Translated-Binary
20 static bool
isRosetta(void)21 isRosetta(void)
22 {
23 int out_value = 0;
24 size_t io_size = sizeof(out_value);
25 if (sysctlbyname("sysctl.proc_translated", &out_value, &io_size, NULL, 0) == 0) {
26 assert(io_size >= sizeof(out_value));
27 return out_value;
28 }
29 return false;
30 }
31 #endif /* __x86_64__ */
32
33 T_DECL(vm_memory_entry_parent,
34 "Test that we properly align child memory_entries after vm_map",
35 T_META_RUN_CONCURRENTLY(true))
36 {
37 mach_vm_address_t src_addr, mapped_addr;
38 mach_vm_size_t size, parent_offset;
39 mach_port_t named_me_port, child_me_port;
40 kern_return_t kr;
41
42 size = KB16 * 2;
43
44 kr = mach_vm_allocate(mach_task_self(), &src_addr, size, VM_FLAGS_ANYWHERE);
45 T_EXPECT_MACH_SUCCESS(kr, "vm_allocate");
46
47 for (size_t i = 0; i < size / KB4; i++) {
48 memset((void *)(src_addr + KB4 * i), (i + 1) * 0x11, KB4);
49 }
50
51 /*
52 * Create a memory entry offset by KB4 * 2.
53 * On userspaces with a vm_map_page_size of KB16,
54 * this should be rounded back to 0 when used as the offset in the kernel.
55 */
56 parent_offset = KB4 * 2;
57 mach_vm_size_t parent_entry_size = size;
58 kr = mach_make_memory_entry_64(mach_task_self(),
59 &parent_entry_size,
60 src_addr + parent_offset,
61 VM_PROT_READ | VM_PROT_WRITE,
62 &named_me_port,
63 MACH_PORT_NULL);
64 T_EXPECT_MACH_SUCCESS(kr, "parent mach_make_memory_entry()");
65
66 /*
67 * Create a memory entry offset into its parent by KB4 * 3.
68 * On kernels with a PAGE_SIZE of KB16,
69 * this should be rounded back to 0 when used as the offset in the kernel.
70 */
71 mach_vm_offset_t child_offset = KB4 * 3;
72 mach_vm_size_t child_entry_size = KB4 * 1;
73 kr = mach_make_memory_entry_64(mach_task_self(),
74 &child_entry_size,
75 child_offset,
76 VM_PROT_READ | VM_PROT_WRITE | MAP_MEM_USE_DATA_ADDR,
77 &child_me_port,
78 named_me_port
79 );
80 T_EXPECT_MACH_SUCCESS(kr, "child mach_make_memory_entry()");
81
82 /*
83 * Map in our child memory entry.
84 */
85 kr = mach_vm_map(mach_task_self(),
86 &mapped_addr,
87 child_entry_size,
88 0,
89 VM_FLAGS_ANYWHERE,
90 child_me_port,
91 0,
92 false,
93 VM_PROT_READ | VM_PROT_WRITE,
94 VM_PROT_READ | VM_PROT_WRITE,
95 VM_INHERIT_NONE);
96 T_EXPECT_MACH_SUCCESS(kr, "mach_vm_map()");
97
98 /*
99 * On rosetta, we expect the mapped address to be offset by the offset of the parent.
100 * On arm64, we expect the child offset to be ignored, and the mapped address to be offset by 0 from the src.
101 * On intel, we expect the mapped address to by offset by KB16.
102 */
103
104 #if __x86_64__
105 if (isRosetta()) {
106 T_ASSERT_EQ(0, memcmp((void *)mapped_addr, (void *) (src_addr + parent_offset), child_entry_size), "Mapped values equal src values");
107 } else {
108 T_ASSERT_EQ(0, memcmp((void *)mapped_addr, (void *) (src_addr + (parent_offset + child_offset)), child_entry_size), "Mapped values equal src values");
109 }
110 #else
111 T_ASSERT_EQ(0, memcmp((void *)mapped_addr, (void *) src_addr, child_entry_size), "Mapped values equal src values");
112 #endif
113 }
114