1 /* Copyright (c) 2020 Apple Computer, Inc. All rights reserved. */
3 #include <CoreSymbolication/CoreSymbolication.h>
4 #include <CoreSymbolication/CoreSymbolicationPrivate.h>
5 #include <darwintest.h>
6 #include <dispatch/dispatch.h>
8 #include <mach-o/loader.h>
10 #include <sys/kas_info.h>
14 #include <sys/sysctl.h>
15 #include <sys/types.h>
22 T_META_NAMESPACE("xnu.kas_info"),
23 T_META_CHECK_LEAKS(false),
29 int slide_enabled
, err
;
30 size_t size
= sizeof(slide_enabled
);
31 err
= sysctlbyname("kern.slide", &slide_enabled
, &size
, NULL
, 0);
32 T_ASSERT_POSIX_SUCCESS(err
, "sysctl(\"kern.slide\");");
33 return slide_enabled
!= 0;
40 size_t size
= sizeof(slide
);
41 int err
= kas_info(KAS_INFO_KERNEL_TEXT_SLIDE_SELECTOR
, &slide
, &size
);
42 if (err
&& errno
== ENOTSUP
) {
43 T_SKIP("Running on kernel without kas_info");
46 T_ASSERT_POSIX_SUCCESS(errno
, "kas_info KAS_INFO_KERNEL_TEXT_SLIDE_SELECTOR");
47 T_ASSERT_EQ(size
, sizeof(slide
), "returned size is valid");
52 T_DECL(kernel_text_slide
,
53 "ensures that kas_info can return the kernel text slide")
55 if (!slide_enabled()) {
56 T_SKIP("KASLR is not enabled");
57 __builtin_unreachable();
60 uint64_t slide
= kernel_slide();
62 T_ASSERT_GT_ULLONG(slide
, 0ULL, "kernel slide is non-zero");
65 T_DECL(kernel_text_slide_invalid
,
66 "ensures that kas_info handles invalid input to KERNEL_TEXT_SLIDE_SELECTOR")
72 err
= kas_info(KAS_INFO_KERNEL_TEXT_SLIDE_SELECTOR
, &slide
, NULL
);
73 if (errno
== ENOTSUP
) {
74 T_SKIP("Running on kernel without kas_info");
76 T_ASSERT_POSIX_FAILURE(err
, EFAULT
, "kas_info with NULL size");
78 size
= sizeof(uint64_t);
79 err
= kas_info(KAS_INFO_KERNEL_TEXT_SLIDE_SELECTOR
, NULL
, &size
);
80 T_ASSERT_POSIX_FAILURE(err
, EFAULT
, "kas_info with NULL slide");
82 size
= sizeof(uint32_t);
83 err
= kas_info(KAS_INFO_KERNEL_TEXT_SLIDE_SELECTOR
, &slide
, &size
);
84 T_ASSERT_POSIX_FAILURE(err
, EINVAL
, "kas_info with invalid size");
90 static CSSymbolicatorRef symbolicator
;
91 static char const* path
;
92 static dispatch_once_t once
;
93 dispatch_once(&once
, ^{
94 uint32_t flags
= kCSSymbolicatorDefaultCreateFlags
;
95 symbolicator
= CSSymbolicatorCreateWithMachKernelFlagsAndNotification(flags
, NULL
);
96 T_QUIET
; T_ASSERT_TRUE(!CSIsNull(symbolicator
), "CSSymbolicatorCreateWithMachKernelFlagsAndNotification");
97 path
= CSSymbolOwnerGetPath(CSSymbolicatorGetAOutSymbolOwner(symbolicator
));
99 path
= CSSymbolOwnerGetPath(CSSymbolicatorGetSymbolOwner(symbolicator
));
101 T_QUIET
; T_ASSERT_NOTNULL(path
, "CSSymbolOwnerGetPath/CSSymbolicatorGetSymbolOwner");
107 disk_kernel_segments(uint64_t **segs_out
, size_t *nsegs_out
)
109 char const* path
= kernel_path();
110 int fd
= open(path
, O_RDONLY
);
114 uint64_t *segs
= NULL
;
117 T_LOG("Kernel file is %s", path
);
118 T_QUIET
; T_ASSERT_POSIX_SUCCESS(fd
, "open kernel file");
120 err
= fstat(fd
, &sb
);
121 T_ASSERT_POSIX_SUCCESS(err
, "fstat kernel file");
123 data
= mmap(NULL
, (size_t)sb
.st_size
, PROT_READ
, MAP_SHARED
, fd
, 0);
124 T_ASSERT_NE(data
, MAP_FAILED
, "mmap kernel file");
127 * TODO: If we bring back FAT kernel binaries
128 * this will need to be fixed to handle them properly
130 uint32_t magic
= *(uint32_t*)data
;
131 struct load_command
*cmd
= NULL
;
134 case MH_MAGIC
: OS_FALLTHROUGH
;
136 struct mach_header
*mh
= (struct mach_header
*)data
;
137 cmd
= (struct load_command
*)(&(mh
[1]));
141 case MH_MAGIC_64
: OS_FALLTHROUGH
;
143 struct mach_header_64
*mh
= (struct mach_header_64
*)data
;
144 cmd
= (struct load_command
*)(&(mh
[1]));
149 T_FAIL("kernel file is not a Mach-O file, magic is %x", magic
);
152 /* Adjust for the LC_UUID && LC_BUILD_VERSION commands in front of
153 * load commands for dSYMs
155 while (cmd
->cmd
!= LC_SEGMENT
&& cmd
->cmd
!= LC_SEGMENT_64
) {
156 cmd
= (struct load_command
*) ((uintptr_t) cmd
+ cmd
->cmdsize
);
160 segs
= calloc(nsegs
, sizeof(*segs
));
161 T_ASSERT_NOTNULL(segs
, "calloc disk segment array");
163 for (uint8_t i
= 0; i
< nsegs
; i
++) {
164 if (cmd
->cmd
== LC_SEGMENT
) {
165 struct segment_command
*sg
= (struct segment_command
*) cmd
;
166 if (sg
->vmsize
> 0) {
167 segs
[i
] = sg
->vmaddr
;
169 } else if (cmd
->cmd
== LC_SEGMENT_64
) {
170 struct segment_command_64
*sg
= (struct segment_command_64
*) cmd
;
171 if (sg
->vmsize
> 0) {
172 segs
[i
] = sg
->vmaddr
;
175 cmd
= (struct load_command
*) ((uintptr_t) cmd
+ cmd
->cmdsize
);
181 err
= munmap(data
, (size_t)sb
.st_size
);
184 T_ASSERT_POSIX_SUCCESS(err
, "close kernel fd");
192 size_t size
= sizeof(uuid
);
193 err
= sysctlbyname("kern.filesetuuid", uuid
, &size
, NULL
, 0);
197 #define KAS_INFO_KERNEL_SEGMENT_LOCATION_SELECTOR 1
199 T_DECL(kernel_segment_location
,
200 "ensures that KAS_INFO_KERNEL_SEGMENT_LOCATION returns correct segment locations")
204 if (!slide_enabled()) {
205 T_SKIP("KASLR is not enabled");
206 __builtin_unreachable();
211 disk_kernel_segments(&disk_segs
, &disk_nsegs
);
215 err
= kas_info(KAS_INFO_KERNEL_SEGMENT_VMADDR_SELECTOR
, NULL
, &size
);
216 if (errno
== ENOTSUP
) {
217 T_SKIP("KAS_INFO_KERNEL_SEGMENT_VMADDR_SELECTOR not supported");
219 T_ASSERT_POSIX_SUCCESS(err
, "kas_info KAS_INFO_KERNEL_SEGMENT_VMADDR_SELECTOR for size");
221 uint64_t mem_nsegs
= size
/ sizeof(uint64_t);
222 uint64_t *mem_segs
= calloc(mem_nsegs
, sizeof(*disk_segs
));
224 err
= kas_info(KAS_INFO_KERNEL_SEGMENT_VMADDR_SELECTOR
, mem_segs
, &size
);
225 if (errno
== ENOTSUP
) {
226 T_SKIP("KAS_INFO_KERNEL_SEGMENT_VMADDR_SELECTOR not supported");
229 T_ASSERT_POSIX_SUCCESS(err
, "kas_info KAS_INFO_KERNEL_SEGMENT_VMADDR_SELECTOR for data");
231 T_LOG("Kernel has %zu segments on disk, %zu in memory:", disk_nsegs
, mem_nsegs
);
232 for (size_t i
= 0; i
< disk_nsegs
; i
++) {
233 T_LOG("%zu %llx %llx", i
, disk_segs
[i
], mem_segs
[i
]);
237 * If the kernel is not a fileset, verify that all
238 * the segments in memory are the segment on disk
241 if (!is_fileset_kc()) {
242 T_LOG("Kernelcache is not a fileset kernelcache");
244 uint64_t slide
= kernel_slide();
245 for (size_t i
= 0; i
< disk_nsegs
; i
++) {
246 if (disk_segs
[i
] == 0 || mem_segs
[i
] == 0) {
249 T_ASSERT_EQ(disk_segs
[i
] + slide
, mem_segs
[i
], "segment %zu is slid", i
);