build: port the arch and OS patch series to xnu-12377 and share the kernel target

This commit is contained in:
bluseliu50 committed 2026-10-26 15:10:41 +08:00
1 parent 26c0b775ea
commit 2ab05c11ae
36 files changed
+3474 -18

No files matched your search

@@ -0,0 +1,13 @@
diff --git a/bsd/miscfs/mockfs/mockfs_vnops.c b/bsd/miscfs/mockfs/mockfs_vnops.c
index 5dbbbc64..8ce4c11e 100644
--- a/bsd/miscfs/mockfs/mockfs_vnops.c
+++ b/bsd/miscfs/mockfs/mockfs_vnops.c
@@ -103,7 +103,7 @@ mockfs_lookup(struct vnop_lookup_args * ap)
fsnode = (mockfs_fsnode_t) dvp->v_data;
target_fsnode = NULL;
- if ((op == LOOKUP) && (fsnode->type == MOCKFS_ROOT)) {
+ if ((op == LOOKUP || !(cnp->cn_flags & ISLASTCN)) && (fsnode->type == MOCKFS_ROOT)) {
/*
* Okay, we're looking in the root directory, so we aren't necessarily
* going to fail. What are we looking for?
@@ -0,0 +1,18 @@
diff --git a/bsd/miscfs/mockfs/mockfs_vfsops.c b/bsd/miscfs/mockfs/mockfs_vfsops.c
index 325bb060..bea8e6e1 100644
--- a/bsd/miscfs/mockfs/mockfs_vfsops.c
+++ b/bsd/miscfs/mockfs/mockfs_vfsops.c
@@ -99,9 +99,11 @@ mockfs_mountroot(mount_t mp, vnode_t rvp, __unused vfs_context_t ctx)
if (!VNOP_IOCTL(rvp, DKIOCGETMEMDEVINFO, (caddr_t)&memdev_info, 0, NULL)) {
/*
- * For the moment, we won't try to optimize when mi_phys is true.
+ * The legacy ioctl exposes a 32-bit base page. A 64-bit kernel
+ * virtual address cannot be represented; use the real device I/O
+ * path instead. Physical memory is not supported by this optimization.
*/
- if (!mockfs_mount_data->mockfs_physical_memory) {
+ if (!memdev_info.mi_phys && (sizeof(uintptr_t) == sizeof(uint32_t))) {
mockfs_mount_data->mockfs_memory_backed = memdev_info.mi_mdev;
mockfs_mount_data->mockfs_physical_memory = memdev_info.mi_phys;
mockfs_mount_data->mockfs_memdev_base = memdev_info.mi_base;
+39
View File
@@ -0,0 +1,39 @@
diff --git a/bsd/miscfs/mockfs/mockfs_vfsops.c b/bsd/miscfs/mockfs/mockfs_vfsops.c
index bea8e6e1..20cec71f 100644
--- a/bsd/miscfs/mockfs/mockfs_vfsops.c
+++ b/bsd/miscfs/mockfs/mockfs_vfsops.c
@@ -227,6 +227,27 @@ mockfs_sync(__unused struct mount *mp, __unused int waitfor, __unused vfs_contex
return 0;
}
+static int
+mockfs_getattr(mount_t mp, struct vfs_attr *attributes, __unused vfs_context_t ctx)
+{
+ mockfs_fsnode_t root = ((mockfs_mount_t)mp->mnt_data)->mockfs_root;
+ uint64_t nodes = 1 + (root->child_a != NULL) + (root->child_b != NULL) + (root->child_c != NULL);
+ uint64_t bytes = mp->mnt_devvp->v_specinfo->si_devsize;
+ uint64_t blocks = bytes / 4096 + ((bytes & 4095) != 0);
+ VFSATTR_RETURN(attributes, f_bsize, 4096);
+ VFSATTR_RETURN(attributes, f_iosize, 4096);
+ VFSATTR_RETURN(attributes, f_blocks, blocks);
+ VFSATTR_RETURN(attributes, f_bused, blocks);
+ VFSATTR_RETURN(attributes, f_bfree, 0);
+ VFSATTR_RETURN(attributes, f_bavail, 0);
+ VFSATTR_RETURN(attributes, f_files, nodes);
+ VFSATTR_RETURN(attributes, f_ffree, 0);
+ VFSATTR_RETURN(attributes, f_objcount, nodes);
+ VFSATTR_RETURN(attributes, f_maxobjcount, nodes);
+ VFSATTR_RETURN(attributes, f_fssubtype, 0);
+ return 0;
+}
+
int
mockfs_init(__unused struct vfsconf * vfsc)
{
@@ -237,5 +258,6 @@ struct vfsops mockfs_vfsops = {
.vfs_unmount = mockfs_unmount,
.vfs_root = mockfs_root,
.vfs_sync = mockfs_sync,
+ .vfs_getattr = mockfs_getattr,
.vfs_init = mockfs_init,
};
+280
View File
@@ -0,0 +1,280 @@
diff --git a/bsd/miscfs/mockfs/mockfs_fsnode.c b/bsd/miscfs/mockfs/mockfs_fsnode.c
index e0001262..378b4c37 100644
--- a/bsd/miscfs/mockfs/mockfs_fsnode.c
+++ b/bsd/miscfs/mockfs/mockfs_fsnode.c
@@ -74,6 +74,7 @@ mockfs_fsnode_create(mount_t mp, uint8_t type, mockfs_fsnode_t * fsnpp)
case MOCKFS_DEV:
break;
case MOCKFS_FILE:
+ case MOCKFS_TEST_FILE:
/*
* For a regular file, size is meaningful, but it will always be equal to the
* size of the backing device.
@@ -145,6 +146,12 @@ mockfs_fsnode_destroy(mockfs_fsnode_t fsnp)
}
}
+ if (fsnp->child_c) {
+ if ((rvalue = mockfs_fsnode_destroy(fsnp->child_c))) {
+ panic("mockfs: failed to destroy test node");
+ }
+ }
+
/*
* We need to orphan this node before we destroy it.
*/
@@ -202,6 +209,9 @@ mockfs_fsnode_adopt(mockfs_fsnode_t parent, mockfs_fsnode_t child)
} else if (!parent->child_b) {
parent->child_b = child;
child->parent = parent;
+ } else if (!parent->child_c) {
+ parent->child_c = child;
+ child->parent = parent;
} else {
rvalue = ENOMEM;
}
@@ -243,6 +253,9 @@ mockfs_fsnode_orphan(mockfs_fsnode_t fsnp)
} else if (parent->child_b == fsnp) {
parent->child_b = NULL;
fsnp->parent = NULL;
+ } else if (parent->child_c == fsnp) {
+ parent->child_c = NULL;
+ fsnp->parent = NULL;
} else {
panic("mockfs_fsnode_orphan insanity, fsnp->parent != parent->child; fsnp = %p (in case gdb is screwing with you)", fsnp);
}
@@ -278,6 +291,8 @@ mockfs_fsnode_child_by_type(mockfs_fsnode_t parent, uint8_t type, mockfs_fsnode_
*child = parent->child_a;
} else if ((parent->child_b) && (parent->child_b->type == type)) {
*child = parent->child_b;
+ } else if ((parent->child_c) && (parent->child_c->type == type)) {
+ *child = parent->child_c;
} else {
rvalue = ENOENT;
}
@@ -322,7 +337,7 @@ mockfs_fsnode_vnode(mockfs_fsnode_t fsnp, vnode_t * vpp)
* We need to create the vnode; this will be unpleasant.
*/
vnfs_param.vnfs_mp = fsnp->mnt;
- vnfs_param.vnfs_vtype = (fsnp->type == MOCKFS_FILE) ? VREG : VDIR;
+ vnfs_param.vnfs_vtype = ((fsnp->type == MOCKFS_FILE) || (fsnp->type == MOCKFS_TEST_FILE)) ? VREG : VDIR;
vnfs_param.vnfs_str = "mockfs";
vnfs_param.vnfs_dvp = (fsnp->type == MOCKFS_ROOT) ? NULL : fsnp->parent->vp;
vnfs_param.vnfs_fsnode = fsnp;
diff --git a/bsd/miscfs/mockfs/mockfs_fsnode.h b/bsd/miscfs/mockfs/mockfs_fsnode.h
index f2620bfb..fcaf6747 100644
--- a/bsd/miscfs/mockfs/mockfs_fsnode.h
+++ b/bsd/miscfs/mockfs/mockfs_fsnode.h
@@ -46,7 +46,8 @@
enum mockfs_fsnode_type {
MOCKFS_ROOT,
MOCKFS_DEV,
- MOCKFS_FILE
+ MOCKFS_FILE,
+ MOCKFS_TEST_FILE
};
/*
@@ -58,6 +59,7 @@ enum mockfs_fsnode_type {
* VREG access).
*/
struct mockfs_fsnode {
+ uint64_t backing_offset; /* Offline two-file image only. */
uint64_t size; /* Bytes of data; 0 unless type is MOCKFS_FILE */
uint8_t type; /* Serves as a unique identifier for now */
mount_t mnt; /* The mount that this node belongs to */
@@ -66,6 +68,7 @@ struct mockfs_fsnode {
/* TODO: Replace child_a/child_b with something more flexible */
struct mockfs_fsnode * child_a; /* TEMPORARY */
struct mockfs_fsnode * child_b; /* TEMPORARY */
+ struct mockfs_fsnode * child_c; /* Offline test input. */
};
typedef struct mockfs_fsnode * mockfs_fsnode_t;
diff --git a/bsd/miscfs/mockfs/mockfs_vfsops.c b/bsd/miscfs/mockfs/mockfs_vfsops.c
index 20cec71f..ed8f0413 100644
--- a/bsd/miscfs/mockfs/mockfs_vfsops.c
+++ b/bsd/miscfs/mockfs/mockfs_vfsops.c
@@ -34,6 +34,8 @@
#include <miscfs/mockfs/mockfs_vnops.h>
#include <miscfs/specfs/specdev.h>
#include <sys/disk.h>
+#include <sys/buf.h>
+#include <sys/kauth.h>
#include <sys/errno.h>
#include <sys/malloc.h>
#include <sys/mount_internal.h>
@@ -41,6 +43,15 @@
static LCK_GRP_DECLARE(mockfs_mtx_grp, "mockfs-mutex");
+/* Bounded offline input container, not a deployment filesystem or trust claim.
+ * Two unmodified files: /sbin/launchd and /probe.bin; all offsets native 4K.
+ */
+struct mockfs_test_header {
+ char magic[8];
+ uint32_t version, count;
+ uint64_t launch_offset, launch_size, test_offset, test_size;
+};
+
int mockfs_mountroot(mount_t mp, vnode_t rvp, __unused vfs_context_t ctx);
/*
@@ -68,6 +79,10 @@ mockfs_mountroot(mount_t mp, vnode_t rvp, __unused vfs_context_t ctx)
mockfs_fsnode_t root_fsnode = NULL;
mockfs_fsnode_t dev_fsnode = NULL;
mockfs_fsnode_t file_fsnode = NULL;
+ mockfs_fsnode_t test_fsnode = NULL;
+ struct mockfs_test_header header = {0};
+ buf_t header_buf = NULL;
+ boolean_t test_image = FALSE;
mockfs_mount_t mockfs_mount_data = NULL;
dk_memdev_info_t memdev_info;
@@ -113,6 +128,42 @@ mockfs_mountroot(mount_t mp, vnode_t rvp, __unused vfs_context_t ctx)
lck_mtx_init(&mockfs_mount_data->mockfs_mnt_mtx, &mockfs_mtx_grp, LCK_ATTR_NULL);
+ mp->mnt_data = (typeof(mp->mnt_data))mockfs_mount_data;
+
+ /* Legacy single-Mach-O images retain their original mapping. */
+ rvalue = buf_meta_bread(rvp, 0, 4096, vfs_context_ucred(ctx), &header_buf);
+ if (!rvalue) {
+ memcpy(&header, (void *)buf_dataptr(header_buf), sizeof(header));
+ }
+ if (header_buf) {
+ buf_brelse(header_buf);
+ }
+ if (rvalue) {
+ goto done;
+ }
+ if (!memcmp(header.magic, "N4KROOT", 8)) {
+ uint64_t bytes = rvp->v_specinfo->si_devsize;
+ if (header.version != 1 || header.count != 2 || bytes > 32 * 1024 * 1024 ||
+ header.launch_offset != 4096 || !header.launch_size || !header.test_size ||
+ (header.test_offset & 4095) || header.test_offset < 8192 || header.test_offset > bytes ||
+ header.launch_size > header.test_offset - header.launch_offset ||
+ header.test_size > bytes - header.test_offset) {
+ rvalue = EINVAL;
+ goto done;
+ }
+ rvalue = mockfs_fsnode_create(mp, MOCKFS_TEST_FILE, &test_fsnode);
+ if (rvalue) {
+ goto done;
+ }
+ file_fsnode->backing_offset = header.launch_offset;
+ file_fsnode->size = header.launch_size;
+ test_fsnode->backing_offset = header.test_offset;
+ test_fsnode->size = header.test_size;
+ mockfs_mount_data->mockfs_memory_backed = FALSE;
+ test_image = TRUE;
+ }
+
+
/*
* All of the needed nodes/structures have been set up; now we just need to establish the relationships
* between the various mockfs nodes.
@@ -125,11 +176,18 @@ mockfs_mountroot(mount_t mp, vnode_t rvp, __unused vfs_context_t ctx)
goto done;
}
+ if (test_image && (rvalue = mockfs_fsnode_adopt(root_fsnode, test_fsnode))) {
+ goto done;
+ }
+
mockfs_mount_data->mockfs_root = root_fsnode;
mp->mnt_data = (typeof(mp->mnt_data))mockfs_mount_data;
done:
if (rvalue) {
+ if (test_fsnode) {
+ mockfs_fsnode_destroy(test_fsnode);
+ }
if (file_fsnode) {
mockfs_fsnode_destroy(file_fsnode);
}
@@ -142,6 +200,7 @@ done:
if (mockfs_mount_data) {
lck_mtx_destroy(&mockfs_mount_data->mockfs_mnt_mtx, &mockfs_mtx_grp);
kfree_type(struct mockfs_mount, mockfs_mount_data);
+ mp->mnt_data = NULL;
}
}
diff --git a/bsd/miscfs/mockfs/mockfs_vnops.c b/bsd/miscfs/mockfs/mockfs_vnops.c
index 8ce4c11e..6cbfce25 100644
--- a/bsd/miscfs/mockfs/mockfs_vnops.c
+++ b/bsd/miscfs/mockfs/mockfs_vnops.c
@@ -29,6 +29,7 @@
#include <miscfs/mockfs/mockfs.h>
#include <miscfs/mockfs/mockfs_vnops.h>
#include <sys/ubc.h>
+#include <sys/fcntl.h>
#include <sys/mount_internal.h>
#include <sys/vnode_internal.h>
#include <vfs/vfs_support.h>
@@ -46,6 +47,11 @@
* each function does in generic terms.
*/
int mockfs_lookup(struct vnop_lookup_args * ap);
+static int mockfs_readonly_open(struct vnop_open_args *ap)
+{
+ return (ap->a_mode & FWRITE) ? EROFS : 0;
+}
+
int mockfs_getattr(struct vnop_getattr_args * ap);
int mockfs_read(struct vnop_read_args * ap);
int mockfs_strategy(struct vnop_strategy_args * ap);
@@ -117,10 +123,15 @@ mockfs_lookup(struct vnop_lookup_args * ap)
* accidentally commit a change to the init_process pathname. We map from name to node type
* here, as mockfs doesn't current use names; just unique types.
*/
- if (!strncmp(cnp->cn_nameptr, "sbin", 5)) {
+ if (!strncmp(cnp->cn_nameptr, "sbin", 5) ||
+ !strncmp(cnp->cn_nameptr, "usr", 4) || !strncmp(cnp->cn_nameptr, "lib", 4)) {
target_fsnode = fsnode;
} else if (!strncmp(cnp->cn_nameptr, "dev", 4)) {
mockfs_fsnode_child_by_type(fsnode, MOCKFS_DEV, &target_fsnode);
+ } else if (!strncmp(cnp->cn_nameptr, "dyld", 5)) {
+ mockfs_fsnode_child_by_type(fsnode, MOCKFS_TEST_FILE, &target_fsnode);
+ } else if (!strncmp(cnp->cn_nameptr, "probe.bin", 10)) {
+ mockfs_fsnode_child_by_type(fsnode, MOCKFS_TEST_FILE, &target_fsnode);
} else if (!strncmp(cnp->cn_nameptr, "launchd", 8)) {
mockfs_fsnode_child_by_type(fsnode, MOCKFS_FILE, &target_fsnode);
} else {
@@ -392,8 +403,12 @@ mockfs_blockmap(struct vnop_blockmap_args * ap)
* This also means that their request should always be contiguous, so the run calculation is easy!
*/
if (vp->v_type == VREG) {
- *bpn = foffset / blksize;
- *run = fsnode->size - foffset;
+ if (foffset < 0 || (uint64_t)foffset > fsnode->size) {
+ return EINVAL;
+ }
+ *bpn = (fsnode->backing_offset + foffset) / blksize;
+ /* File metadata is exact; block I/O may include allocated zero padding. */
+ *run = ((fsnode->size + 4095) & ~4095ULL) - foffset;
if (ap->a_size > *run) {
/* We've been asked for more data than the backing device can provide; we're done. */
@@ -411,9 +426,9 @@ const struct vnodeopv_entry_desc mockfs_vnodeop_entries[] = {
{ .opve_op = &vnop_default_desc, .opve_impl = (VOPFUNC) vn_default_error }, /* default */
{ .opve_op = &vnop_lookup_desc, .opve_impl = (VOPFUNC) mockfs_lookup }, /* lookup */
{ .opve_op = &vnop_create_desc, .opve_impl = (VOPFUNC) err_create },/* create */
- { .opve_op = &vnop_open_desc, .opve_impl = (VOPFUNC) err_open }, /* open */
+ { .opve_op = &vnop_open_desc, .opve_impl = (VOPFUNC) mockfs_readonly_open }, /* open */
{ .opve_op = &vnop_mknod_desc, .opve_impl = (VOPFUNC) err_mknod }, /* mknod */
- { .opve_op = &vnop_close_desc, .opve_impl = (VOPFUNC) err_close }, /* close */
+ { .opve_op = &vnop_close_desc, .opve_impl = (VOPFUNC) nop_close }, /* close */
{ .opve_op = &vnop_access_desc, .opve_impl = (VOPFUNC) err_access }, /* access */
{ .opve_op = &vnop_getattr_desc, .opve_impl = (VOPFUNC) mockfs_getattr }, /* getattr */
{ .opve_op = &vnop_setattr_desc, .opve_impl = (VOPFUNC) err_setattr }, /* setattr */
@@ -421,7 +436,7 @@ const struct vnodeopv_entry_desc mockfs_vnodeop_entries[] = {
{ .opve_op = &vnop_write_desc, .opve_impl = (VOPFUNC) err_write }, /* write */
{ .opve_op = &vnop_ioctl_desc, .opve_impl = (VOPFUNC) err_ioctl }, /* ioctl */
{ .opve_op = &vnop_select_desc, .opve_impl = (VOPFUNC) err_select }, /* select */
- { .opve_op = &vnop_mmap_desc, .opve_impl = (VOPFUNC) err_mmap }, /* mmap */
+ { .opve_op = &vnop_mmap_desc, .opve_impl = (VOPFUNC) nop_mmap }, /* mmap */
{ .opve_op = &vnop_fsync_desc, .opve_impl = (VOPFUNC) nop_fsync }, /* fsync */
{ .opve_op = &vnop_remove_desc, .opve_impl = (VOPFUNC) err_remove }, /* remove */
{ .opve_op = &vnop_link_desc, .opve_impl = (VOPFUNC) err_link }, /* link */
@@ -0,0 +1,22 @@
diff --git a/bsd/miscfs/mockfs/mockfs_fsnode.c b/bsd/miscfs/mockfs/mockfs_fsnode.c
index 378b4c37..70b6bcb6 100644
--- a/bsd/miscfs/mockfs/mockfs_fsnode.c
+++ b/bsd/miscfs/mockfs/mockfs_fsnode.c
@@ -86,7 +86,7 @@ mockfs_fsnode_create(mount_t mp, uint8_t type, mockfs_fsnode_t * fsnpp)
goto done;
}
- *fsnpp = kalloc_type(mockfs_fsnode_t, Z_WAITOK | Z_ZERO | Z_NOFAIL);
+ *fsnpp = kalloc_type(struct mockfs_fsnode, Z_WAITOK | Z_ZERO | Z_NOFAIL);
(*fsnpp)->size = new_size;
(*fsnpp)->type = type;
(*fsnpp)->mnt = mp;
@@ -161,7 +161,7 @@ mockfs_fsnode_destroy(mockfs_fsnode_t fsnp)
}
}
- kfree_type(mockfs_fsnode_t, fsnp);
+ kfree_type(struct mockfs_fsnode, fsnp);
done:
return rvalue;
}
@@ -0,0 +1,277 @@
diff --git a/bsd/kern/ubc_subr.c b/bsd/kern/ubc_subr.c
index 79bf0cb7..b5ef6a71 100644
--- a/bsd/kern/ubc_subr.c
+++ b/bsd/kern/ubc_subr.c
@@ -103,6 +103,9 @@ static void ubc_cs_free(struct ubc_info *uip);
static boolean_t ubc_cs_supports_multilevel_hash(struct cs_blob *blob);
static kern_return_t ubc_cs_convert_to_multilevel_hash(struct cs_blob *blob);
+#if defined(XNU_NATIVE_CS_HASH_CACHE) && XNU_NATIVE_CS_HASH_CACHE
+static int cs_native4k_prepare(vnode_t vp, struct cs_blob *blob);
+#endif
ZONE_DEFINE_TYPE(ubc_info_zone, "ubc_info zone", struct ubc_info,
ZC_ZFREE_CLEARMEM);
@@ -366,7 +369,11 @@ cs_validate_codedirectory(const CS_CodeDirectory *cd, size_t length)
if (ntohl(cd->magic) != CSMAGIC_CODEDIRECTORY) {
return EBADEXEC;
}
- if ((cd->pageSize != PAGE_SHIFT_4K) && (cd->pageSize != PAGE_SHIFT)) {
+ if ((cd->pageSize != PAGE_SHIFT_4K) && (cd->pageSize != PAGE_SHIFT)
+#if defined(XNU_NATIVE_CS_HASH_CACHE) && XNU_NATIVE_CS_HASH_CACHE
+ && !(PAGE_SHIFT == 12 && cd->pageSize == 14)
+#endif
+ ) {
printf("disallowing unsupported code signature page shift: %u\n", cd->pageSize);
return EBADEXEC;
}
@@ -3954,6 +3961,13 @@ ubc_cs_convert_to_multilevel_hash(struct cs_blob *blob)
static void
cs_blob_cleanup(struct cs_blob *blob)
{
+ if (blob->csb_native4k_hashes != NULL) {
+ kmem_free(kernel_map, (vm_offset_t)blob->csb_native4k_hashes,
+ blob->csb_native4k_hashes_size);
+ blob->csb_native4k_hashes = NULL;
+ blob->csb_native4k_hashes_size = 0;
+ blob->csb_native4k_hash_count = 0;
+ }
if (blob->csb_entitlements != NULL) {
amfi->OSEntitlements_invalidate(blob->csb_entitlements);
osobject_release(blob->csb_entitlements);
@@ -4967,6 +4981,14 @@ ubc_cs_blob_add(
goto out;
}
+#if defined(XNU_NATIVE_CS_HASH_CACHE) && XNU_NATIVE_CS_HASH_CACHE
+ /* Authenticate before publishing the signed object or taking its locks. */
+ error = cs_native4k_prepare(vp, &tmp_blob);
+ if (error != 0) {
+ goto out;
+ }
+#endif
+
/*
* The vnode_lock, linked list traversal, and marking of the memory object as
* signed can all be blocking operations. Compute a PAC over the tmp_blob.
@@ -5834,6 +5856,145 @@ ubc_get_cs_mtime(
unsigned long cs_validate_page_no_hash = 0;
unsigned long cs_validate_page_bad_hash = 0;
+#if defined(XNU_NATIVE_CS_HASH_CACHE) && XNU_NATIVE_CS_HASH_CACHE
+__attribute__((used)) const uint32_t cs_native4k_hash_cache_version = 1;
+unsigned long cs_native4k_full_blocks = 0;
+unsigned long cs_native4k_page_checks = 0;
+unsigned long cs_native4k_ready_blobs = 0;
+/* The boot cost, for a board's diagnostics: mach_absolute_time ticks in vn_rdwr (the storage driver's work included) and in the
+ * hashing, and the bytes read. */
+uint64_t cs_native4k_read_ticks = 0, cs_native4k_hash_ticks = 0, cs_native4k_bytes = 0;
+extern uint64_t mach_absolute_time(void);
+
+/* The original signed 16K hash authenticates the whole immutable input
+ * buffer first. Only then derive native-page digests from that same buffer.
+ * Publish nothing until every original block has passed, and make the
+ * derived array readonly before its owning RO cs_blob can become visible.
+ * VM fault validation consequently performs no vnode I/O under object locks.
+ */
+/* One read covers 64 original blocks. Files above 32 MiB (the 3 GB dyld shared cache, read whole by launchd at every boot) are read
+ * uncached: through the page cache they left almost no free memory when the first daemons started. */
+#define CS_NATIVE4K_CHUNK (1024 * 1024)
+#define CS_NATIVE4K_UNCACHED (32 * 1024 * 1024)
+
+__attribute__((noinline))
+static int
+cs_native4k_prepare(vnode_t vp, struct cs_blob *blob)
+{
+ const CS_CodeDirectory *cd = blob->csb_cd;
+ const struct cs_hash *md = blob->csb_hashtype;
+ vm_offset_t storage = 0;
+ vm_size_t allocation = 0, count = 0;
+ uint8_t *buffer = NULL;
+ uint64_t limit, offset, chunk;
+ int error = EBADEXEC, ioflags;
+ kern_return_t kr;
+
+ if (blob->csb_hash_pageshift <= PAGE_SHIFT) {
+ return 0;
+ }
+ if (PAGE_SHIFT != 12 || blob->csb_hash_pageshift != 14 || cd == NULL || md == NULL ||
+ md->cs_size == 0 || md->cs_size > CS_HASH_MAX_SIZE ||
+ md->cs_digest_size > CS_HASH_MAX_SIZE || blob->csb_base_offset < 0 ||
+ (blob->csb_base_offset & PAGE_MASK) || blob->csb_hash_firstlevel_pageshift ||
+ ((ntohl(cd->version) >= CS_SUPPORTSSCATTER) && ntohl(cd->scatterOffset))) {
+ return ENOTSUP;
+ }
+ limit = ntohl(cd->codeLimit);
+ if (!limit || ((ntohl(cd->version) >= CS_SUPPORTSCODELIMIT64) &&
+ ntohll(cd->codeLimit64) && ntohll(cd->codeLimit64) != limit) ||
+ ntohl(cd->nCodeSlots) != ((limit + 16383) >> 14)) {
+ return ENOTSUP;
+ }
+ count = (limit + PAGE_MASK) >> PAGE_SHIFT;
+ if (os_mul_overflow(count, (vm_size_t)md->cs_size, &allocation) ||
+ round_page_overflow(allocation, &allocation)) {
+ return EOVERFLOW;
+ }
+ kr = kmem_alloc(kernel_map, &storage, allocation,
+ KMA_KOBJECT | KMA_DATA | KMA_ZERO, VM_KERN_MEMORY_SECURITY);
+ if (kr != KERN_SUCCESS) {
+ return ENOMEM;
+ }
+ buffer = kalloc_data(CS_NATIVE4K_CHUNK, Z_WAITOK);
+ if (buffer == NULL) {
+ error = ENOMEM;
+ goto out;
+ }
+ /* Executables are faulted in right after their signature is added and stay cached. */
+ ioflags = limit > CS_NATIVE4K_UNCACHED ? IO_NOCACHE : 0;
+ for (chunk = 0; chunk < limit; chunk += CS_NATIVE4K_CHUNK) {
+ size_t chunk_length = MIN((uint64_t)CS_NATIVE4K_CHUNK, limit - chunk);
+ int resid = 0;
+ off_t position;
+ if (os_add_overflow(blob->csb_base_offset, (off_t)chunk, &position)) {
+ error = EOVERFLOW;
+ goto out;
+ }
+ const uint64_t read_start = mach_absolute_time();
+ error = vn_rdwr(UIO_READ, vp, (caddr_t)buffer, (int)chunk_length, position,
+ UIO_SYSSPACE, ioflags, vfs_context_ucred(vfs_context_current()),
+ &resid, current_proc());
+ const uint64_t hash_start = mach_absolute_time();
+ os_atomic_add(&cs_native4k_read_ticks, hash_start - read_start, relaxed);
+ os_atomic_add(&cs_native4k_bytes, (uint64_t)chunk_length, relaxed);
+ if (error || resid) {
+ error = error ? error : EIO;
+ goto out;
+ }
+ for (size_t block = 0; block < chunk_length; block += 16384) {
+ union cs_hash_union ctx;
+ uint8_t digest[CS_HASH_MAX_SIZE];
+ const uint8_t *expected;
+ const uint8_t *data = buffer + block;
+ const char *lower = blob->csb_mem_kaddr;
+ const char *upper = lower + blob->csb_mem_size;
+ size_t length = MIN((size_t)16384, chunk_length - block);
+ offset = chunk + block;
+ expected = hashes(cd, (uint32_t)(offset >> 14), md->cs_size, lower, upper);
+ if (expected == NULL) {
+ error = EBADEXEC;
+ goto out;
+ }
+ md->cs_init(&ctx);
+ md->cs_update(&ctx, data, length);
+ md->cs_final(digest, &ctx);
+ if (bcmp(expected, digest, md->cs_size)) {
+ os_atomic_inc(&cs_validate_page_bad_hash, relaxed);
+ error = EBADEXEC;
+ goto out;
+ }
+ os_atomic_inc(&cs_native4k_full_blocks, relaxed);
+ for (size_t sub = 0; sub < length; sub += PAGE_SIZE) {
+ size_t bytes = MIN((size_t)PAGE_SIZE, length - sub);
+ vm_size_t index = (offset + sub) >> PAGE_SHIFT;
+ md->cs_init(&ctx);
+ md->cs_update(&ctx, data + sub, bytes);
+ md->cs_final(digest, &ctx);
+ memcpy((void *)(storage + index * md->cs_size), digest, md->cs_size);
+ }
+ }
+ os_atomic_add(&cs_native4k_hash_ticks, mach_absolute_time() - hash_start, relaxed);
+ }
+ kr = vm_map_protect(kernel_map, storage, storage + allocation, VM_PROT_READ, TRUE);
+ if (kr != KERN_SUCCESS) {
+ error = EPERM;
+ goto out;
+ }
+ blob->csb_native4k_hashes = (const uint8_t *)storage;
+ blob->csb_native4k_hashes_size = allocation;
+ blob->csb_native4k_hash_count = count;
+ storage = 0;
+ os_atomic_inc(&cs_native4k_ready_blobs, relaxed);
+ error = 0;
+out:
+ kfree_data(buffer, CS_NATIVE4K_CHUNK);
+ if (storage) {
+ kmem_free(kernel_map, storage, allocation);
+ }
+ return error;
+}
+#endif
static boolean_t
cs_validate_hash(
struct cs_blob *blobs,
@@ -5856,6 +6017,7 @@ cs_validate_hash(
size_t size;
off_t codeLimit = 0;
const char *lower_bound, *upper_bound;
+ boolean_t native4k_cached = FALSE;
vm_offset_t kaddr, blob_addr;
/* retrieve the expected hash */
@@ -5892,15 +6054,31 @@ cs_validate_hash(
if (hashtype->cs_digest_size > sizeof(actual_hash)) {
panic("hash size too large");
}
- if (offset & ((1U << blob->csb_hash_pageshift) - 1)) {
- panic("offset not aligned to cshash boundary");
- }
-
codeLimit = ntohl(cd->codeLimit);
-
- hash = hashes(cd, (uint32_t)(offset >> blob->csb_hash_pageshift),
- hashtype->cs_size,
- lower_bound, upper_bound);
+#if defined(XNU_NATIVE_CS_HASH_CACHE) && XNU_NATIVE_CS_HASH_CACHE
+ if (PAGE_SHIFT == 12 && blob->csb_hash_pageshift == 14) {
+ vm_size_t index = (vm_size_t)offset >> PAGE_SHIFT;
+ if ((offset & PAGE_MASK) || offset >= codeLimit ||
+ blob->csb_native4k_hashes == NULL ||
+ index >= blob->csb_native4k_hash_count ||
+ hashtype->cs_size > blob->csb_native4k_hashes_size ||
+ index > (blob->csb_native4k_hashes_size - hashtype->cs_size) / hashtype->cs_size) {
+ *bytes_processed = PAGE_SIZE;
+ *tainted = CS_VALIDATE_TAINTED;
+ return FALSE;
+ }
+ hash = blob->csb_native4k_hashes + index * hashtype->cs_size;
+ native4k_cached = TRUE;
+ os_atomic_inc(&cs_native4k_page_checks, relaxed);
+ } else
+#endif
+ {
+ if (offset & ((1U << blob->csb_hash_pageshift) - 1)) {
+ panic("offset not aligned to cshash boundary");
+ }
+ hash = hashes(cd, (uint32_t)(offset >> blob->csb_hash_pageshift),
+ hashtype->cs_size, lower_bound, upper_bound);
+ }
if (hash != NULL) {
bcopy(hash, expected_hash, hashtype->cs_size);
found_hash = TRUE;
@@ -5930,7 +6108,7 @@ cs_validate_hash(
} else {
*tainted = 0;
- size = (1U << blob->csb_hash_pageshift);
+ size = native4k_cached ? PAGE_SIZE : (1U << blob->csb_hash_pageshift);
*bytes_processed = size;
const uint32_t *asha1, *esha1;
diff --git a/bsd/sys/ubc_internal.h b/bsd/sys/ubc_internal.h
index 56f04445..923d38bb 100644
--- a/bsd/sys/ubc_internal.h
+++ b/bsd/sys/ubc_internal.h
@@ -162,6 +162,13 @@ struct cs_blob {
bool csb_csm_managed;
uint32_t csb_csm_trust_level;
#endif
+
+ /* Native 4K authenticators derived only from fully verified original
+ * 16K blocks. Append fields to preserve the existing binary prefix.
+ */
+ const uint8_t *csb_native4k_hashes;
+ vm_size_t csb_native4k_hashes_size;
+ vm_size_t csb_native4k_hash_count;
};
/*
@@ -0,0 +1,85 @@
diff --git a/bsd/kern/mach_loader.c b/bsd/kern/mach_loader.c
index 2c16ff7a..18cc33d3 100644
--- a/bsd/kern/mach_loader.c
+++ b/bsd/kern/mach_loader.c
@@ -800,8 +800,12 @@ load_machfile(
int vm_map_pageshift = PAGE_SHIFT;
#if defined(__arm64__)
if (result->is_64bit_addr) {
+#if !defined(APPLE_ARM64_ARCH_FAMILY) && !__ARM_16K_PG__
+ vm_map_pageshift = FOURK_PAGE_SHIFT;
+#else
/* enforce 16KB alignment of VM map entries */
vm_map_pageshift = SIXTEENK_PAGE_SHIFT;
+#endif
} else {
vm_map_pageshift = (int)page_shift_user32;
}
diff --git a/osfmk/arm/arm_init.c b/osfmk/arm/arm_init.c
index 6690846b..d1324571 100644
--- a/osfmk/arm/arm_init.c
+++ b/osfmk/arm/arm_init.c
@@ -422,6 +422,10 @@ arm_init(
{
+ /* Native non-Apple 4K builds expose the hardware page size at any RAM size. */
+#if !defined(APPLE_ARM64_ARCH_FAMILY) && !__ARM_16K_PG__
+ PAGE_SHIFT_CONST = ARM_PGSHIFT;
+#else
/*
* Select the advertised kernel page size.
*/
@@ -440,8 +444,14 @@ arm_init(
PAGE_SHIFT_CONST = ARM_PGSHIFT;
}
+#endif
+
+#if !defined(APPLE_ARM64_ARCH_FAMILY) && !__ARM_16K_PG__
+ page_shift_user32 = ARM_PGSHIFT;
+#else
/* 32-bit apps always see 16KB page size */
page_shift_user32 = PAGE_MAX_SHIFT;
+#endif
#ifdef APPLETYPHOON
if (PE_parse_boot_argn("cpus_defeatures", &cpus_defeatures, sizeof(cpus_defeatures))) {
if ((cpus_defeatures & 0xF) != 0) {
diff --git a/osfmk/arm/commpage/commpage.c b/osfmk/arm/commpage/commpage.c
index 29846632..1f672e93 100644
--- a/osfmk/arm/commpage/commpage.c
+++ b/osfmk/arm/commpage/commpage.c
@@ -154,8 +154,13 @@ commpage_populate(void)
#if __arm64__
*((uint8_t*)(_COMM_PAGE_USER_PAGE_SHIFT_32_LEGACY + _COMM_PAGE_RW_OFFSET)) = (uint8_t) page_shift_user32;
*((uint8_t*)(_COMM_PAGE_USER_PAGE_SHIFT_32 + _COMM_PAGE_RO_OFFSET)) = (uint8_t) page_shift_user32;
+#if !defined(APPLE_ARM64_ARCH_FAMILY) && !__ARM_16K_PG__
+ *((uint8_t*)(_COMM_PAGE_USER_PAGE_SHIFT_64_LEGACY + _COMM_PAGE_RW_OFFSET)) = (uint8_t) FOURK_PAGE_SHIFT;
+ *((uint8_t*)(_COMM_PAGE_USER_PAGE_SHIFT_64 + _COMM_PAGE_RO_OFFSET)) = (uint8_t) FOURK_PAGE_SHIFT;
+#else
*((uint8_t*)(_COMM_PAGE_USER_PAGE_SHIFT_64_LEGACY + _COMM_PAGE_RW_OFFSET)) = (uint8_t) SIXTEENK_PAGE_SHIFT;
*((uint8_t*)(_COMM_PAGE_USER_PAGE_SHIFT_64 + _COMM_PAGE_RO_OFFSET)) = (uint8_t) SIXTEENK_PAGE_SHIFT;
+#endif
#endif /* __arm64__ */
commpage_update_timebase();
diff --git a/osfmk/vm/vm_shared_region.c b/osfmk/vm/vm_shared_region.c
index 0b688296..e6d4790d 100644
--- a/osfmk/vm/vm_shared_region.c
+++ b/osfmk/vm/vm_shared_region.c
@@ -787,11 +787,15 @@ vm_shared_region_create(
csm_setup_nested_address_space(nested_pmap, base_address, size);
#endif /* CODE_SIGNING_MONITOR */
int vm_map_pageshift = PAGE_SHIFT;
+#if !defined(APPLE_ARM64_ARCH_FAMILY) && !__ARM_16K_PG__
+ vm_map_pageshift = FOURK_PAGE_SHIFT;
+#else
if (is_64bit ||
page_shift_user32 == SIXTEENK_PAGE_SHIFT) {
/* enforce 16KB alignment of VM map entries */
vm_map_pageshift = SIXTEENK_PAGE_SHIFT;
}
+#endif
#if __ARM_MIXED_PAGE_SIZE__
if (cputype == CPU_TYPE_ARM64 &&
target_page_shift == FOURK_PAGE_SHIFT) {
@@ -0,0 +1,52 @@
diff --git a/bsd/vfs/vfs_lookup.c b/bsd/vfs/vfs_lookup.c
index b897c9ca..772cee80 100644
--- a/bsd/vfs/vfs_lookup.c
+++ b/bsd/vfs/vfs_lookup.c
@@ -100,6 +100,47 @@
#if NAMEDRSRCFORK
#include <sys/xattr.h>
#endif
+/*
+ * Board bring-up diagnostic: the distinct lookups that failed for MobileSafari (S) and lsd (L), errno and path (its last 96 characters
+ * when longer), read by the boot log. Localization folders, Trial and logging preferences are left out: they fail by design.
+ */
+char native_lookup_log[160][112];
+volatile unsigned native_lookup_count;
+static bool
+native_lookup_has(const char *text, const char *part)
+{
+ const size_t length = strlen(part);
+ for (; *text; ++text) {
+ if (strncmp(text, part, length) == 0) {
+ return true;
+ }
+ }
+ return false;
+}
+static void
+native_lookup_note(const char *path, int error)
+{
+ char name[MAXCOMLEN + 1];
+ proc_selfname(name, sizeof(name));
+ const char tag = !strcmp(name, "MobileSafari") ? 'S' : !strcmp(name, "lsd") ? 'L' : 0;
+ if (!tag || native_lookup_has(path, ".lproj") || native_lookup_has(path, "/Trial/") ||
+ native_lookup_has(path, "/Preferences/Logging/")) {
+ return;
+ }
+ const size_t length = strlen(path);
+ char entry[112];
+ snprintf(entry, sizeof(entry), "%c e%d %s%s", tag, error, length > 96 ? "..." : "", length > 96 ? path + length - 96 : path);
+ unsigned seen = native_lookup_count < 160 ? native_lookup_count : 160;
+ for (unsigned i = 0; i < seen; ++i) {
+ if (strncmp(native_lookup_log[i], entry, sizeof(entry)) == 0) {
+ return;
+ }
+ }
+ unsigned slot = os_atomic_inc_orig(&native_lookup_count, relaxed);
+ if (slot < 160) {
+ strlcpy(native_lookup_log[slot], entry, sizeof(native_lookup_log[slot]));
+ }
+}
/*
* The minimum volfs-style pathname is 9.
* Example: "/.vol/1/2"
+218
View File
@@ -0,0 +1,218 @@
diff --git a/bsd/kern/ubc_subr.c b/bsd/kern/ubc_subr.c
index b5ef6a71..3d96b9ac 100644
--- a/bsd/kern/ubc_subr.c
+++ b/bsd/kern/ubc_subr.c
@@ -6174,6 +6174,172 @@ cs_validate_hash(
return validated;
}
+/*
+ * Fixups of signed file text, each <file name>,<file offset>,<original>,<replacement> (offset in hex, byte strings in hex, at
+ * most 16 bytes): the board's table in the device tree (/chosen/text-fixups, NUL-separated entries, so it takes no command line
+ * space), then the boot arguments text-fixup0..7. A page is changed only after it passed signature validation and only when
+ * the original bytes match, so a different file or build is left alone.
+ */
+#include <pexpert/device_tree.h>
+#define CS_TEXT_FIXUP_MAX 32
+#define CS_TEXT_FIXUP_BYTES 16
+struct cs_text_fixup {
+ char name[64];
+ uint64_t offset;
+ uint8_t original[CS_TEXT_FIXUP_BYTES];
+ uint8_t replacement[CS_TEXT_FIXUP_BYTES];
+ uint32_t length;
+};
+static TUNABLE_STR(cs_text_fixup_arg0, 160, "text-fixup0", "");
+static TUNABLE_STR(cs_text_fixup_arg1, 160, "text-fixup1", "");
+static TUNABLE_STR(cs_text_fixup_arg2, 160, "text-fixup2", "");
+static TUNABLE_STR(cs_text_fixup_arg3, 160, "text-fixup3", "");
+static TUNABLE_STR(cs_text_fixup_arg4, 160, "text-fixup4", "");
+static TUNABLE_STR(cs_text_fixup_arg5, 160, "text-fixup5", "");
+static TUNABLE_STR(cs_text_fixup_arg6, 160, "text-fixup6", "");
+static TUNABLE_STR(cs_text_fixup_arg7, 160, "text-fixup7", "");
+static struct cs_text_fixup cs_text_fixups[CS_TEXT_FIXUP_MAX];
+static uint32_t cs_text_fixup_count;
+uint32_t cs_text_fixup_parsed;
+uint32_t cs_text_fixup_applied;
+
+static int
+cs_text_fixup_hex(char c)
+{
+ if (c >= '0' && c <= '9') {
+ return c - '0';
+ }
+ if (c >= 'a' && c <= 'f') {
+ return c - 'a' + 10;
+ }
+ if (c >= 'A' && c <= 'F') {
+ return c - 'A' + 10;
+ }
+ return -1;
+}
+
+/* Hex bytes up to the next ',' or the end; returns the count or 0 on a malformed field. */
+static uint32_t
+cs_text_fixup_bytes(const char **cursor, uint8_t *out)
+{
+ const char *s = *cursor;
+ uint32_t n = 0;
+ while (*s && *s != ',') {
+ int hi = cs_text_fixup_hex(s[0]), lo = s[1] ? cs_text_fixup_hex(s[1]) : -1;
+ if (hi < 0 || lo < 0 || n == CS_TEXT_FIXUP_BYTES) {
+ return 0;
+ }
+ out[n++] = (uint8_t)(hi << 4 | lo);
+ s += 2;
+ }
+ *cursor = *s ? s + 1 : s;
+ return n;
+}
+
+static bool
+cs_text_fixup_parse(const char *arg, struct cs_text_fixup *f)
+{
+ const char *comma = strchr(arg, ',');
+ if (comma == NULL || comma == arg || (size_t)(comma - arg) >= sizeof(f->name)) {
+ return false;
+ }
+ strlcpy(f->name, arg, (size_t)(comma - arg) + 1);
+ const char *s = comma + 1;
+ f->offset = 0;
+ for (; *s && *s != ','; s++) {
+ int v = cs_text_fixup_hex(*s);
+ if (v < 0) {
+ return false;
+ }
+ f->offset = f->offset << 4 | (uint64_t)v;
+ }
+ if (*s++ != ',') {
+ return false;
+ }
+ uint32_t n = cs_text_fixup_bytes(&s, f->original);
+ if (n == 0 || cs_text_fixup_bytes(&s, f->replacement) != n || *s) {
+ return false;
+ }
+ /* One page holds the whole fixup, so a single validated page carries it. */
+ if ((f->offset & PAGE_MASK) + n > PAGE_SIZE) {
+ return false;
+ }
+ f->length = n;
+ return true;
+}
+
+static void
+cs_text_fixup_init(void)
+{
+ DTEntry chosen;
+ void const *table;
+ unsigned int size;
+ if (SecureDTLookupEntry(NULL, "/chosen", &chosen) == kSuccess &&
+ SecureDTGetProperty(chosen, "text-fixups", &table, &size) == kSuccess) {
+ const char *s = table, *end = s + size;
+ for (int i = 0; s < end && *s; i++) {
+ char entry[160];
+ size_t length = strnlen(s, (size_t)(end - s));
+ if (length < sizeof(entry) && cs_text_fixup_count < CS_TEXT_FIXUP_MAX) {
+ strlcpy(entry, s, length + 1);
+ if (cs_text_fixup_parse(entry, &cs_text_fixups[cs_text_fixup_count])) {
+ cs_text_fixup_count++;
+ } else {
+ printf("text-fixups[%d]: malformed, ignored\n", i);
+ }
+ } else {
+ printf("text-fixups[%d]: too long or table full, ignored\n", i);
+ }
+ s += length + 1;
+ }
+ }
+ const char *args[] = {
+ cs_text_fixup_arg0, cs_text_fixup_arg1, cs_text_fixup_arg2, cs_text_fixup_arg3,
+ cs_text_fixup_arg4, cs_text_fixup_arg5, cs_text_fixup_arg6, cs_text_fixup_arg7,
+ };
+ for (int i = 0; i < (int)(sizeof(args) / sizeof(args[0])); i++) {
+ if (args[i][0] == '\0' || cs_text_fixup_count == CS_TEXT_FIXUP_MAX) {
+ continue;
+ }
+ if (cs_text_fixup_parse(args[i], &cs_text_fixups[cs_text_fixup_count])) {
+ cs_text_fixup_count++;
+ } else {
+ printf("text-fixup%d: malformed, ignored\n", i);
+ }
+ }
+ cs_text_fixup_parsed = cs_text_fixup_count;
+}
+STARTUP(EARLY_BOOT, STARTUP_RANK_MIDDLE, cs_text_fixup_init);
+
+/*
+ * Applies the fixups that fall in this validated page of vp. kva is a writable kernel address of the page. Returns the
+ * number of fixups written, so the caller makes the instruction cache coherent.
+ */
+uint32_t
+cs_text_fixup_page(struct vnode *vp, memory_object_offset_t page_offset, void *kva)
+{
+ uint32_t written = 0;
+ for (uint32_t i = 0; i < cs_text_fixup_count; i++) {
+ const struct cs_text_fixup *f = &cs_text_fixups[i];
+ if (f->offset < page_offset || f->offset + f->length > page_offset + PAGE_SIZE) {
+ continue;
+ }
+ if (vp == NULLVP || vp->v_name == NULL || strcmp(vp->v_name, f->name) != 0) {
+ continue;
+ }
+ uint8_t *at = (uint8_t *)kva + (f->offset - page_offset);
+ if (memcmp(at, f->original, f->length) != 0) {
+ continue;
+ }
+ memcpy(at, f->replacement, f->length);
+ written++;
+ if (os_atomic_inc(&cs_text_fixup_applied, relaxed) <= 16) {
+ printf("text-fixup: %s+0x%llx (%u bytes) applied\n", f->name, f->offset, f->length);
+ }
+ }
+ return written;
+}
+
boolean_t
cs_validate_range(
struct vnode *vp,
diff --git a/osfmk/vm/vm_fault.c b/osfmk/vm/vm_fault.c
index 79f983bb..81edcc8a 100644
--- a/osfmk/vm/vm_fault.c
+++ b/osfmk/vm/vm_fault.c
@@ -106,6 +106,9 @@
#include <vm/memory_object.h>
#include <vm/vm_purgeable_internal.h> /* Needed by some vm_page.h macros */
#include <vm/vm_shared_region.h>
+#if __arm64__
+#include <arm/caches_internal.h>
+#endif
#include <vm/vm_page_internal.h>
#include <vm/vm_map_lock_internal.h>
#include <vm/vm_lock_contention.h>
@@ -8949,6 +8952,14 @@ vm_page_validate_cs_mapped_slow(
page->vmp_cs_tainted |= tainted;
page->vmp_cs_nx |= nx;
+#if __arm64__
+ /* Boot-argument fixups of signed text land after validation, through the physical aperture, before any mapping. */
+ if (validated == VMP_CS_ALL_TRUE && tainted == VMP_CS_ALL_FALSE &&
+ cs_text_fixup_page(vnode, mo_offset, (void *)phystokv((pmap_paddr_t)VM_PAGE_GET_PHYS_PAGE(page) << PAGE_SHIFT)) != 0) {
+ cache_sync_page(VM_PAGE_GET_PHYS_PAGE(page));
+ }
+#endif
+
#if CHECK_CS_VALIDATION_BITMAP
if (page->vmp_cs_validated == VMP_CS_ALL_TRUE &&
page->vmp_cs_tainted == VMP_CS_ALL_FALSE) {
diff --git a/osfmk/vm/vm_protos.h b/osfmk/vm/vm_protos.h
index b8b9efab..bf510c90 100644
--- a/osfmk/vm/vm_protos.h
+++ b/osfmk/vm/vm_protos.h
@@ -285,6 +285,7 @@ extern boolean_t cs_validate_range(struct vnode *vp,
const void *data,
vm_size_t size,
unsigned *result);
+extern uint32_t cs_text_fixup_page(struct vnode *vp, memory_object_offset_t page_offset, void *kva);
extern void cs_validate_page(
struct vnode *vp,
memory_object_t pager,
@@ -0,0 +1,67 @@
diff --git a/bsd/vfs/vfs_attrlist.c b/bsd/vfs/vfs_attrlist.c
index f83e29bc..de5e05a2 100644
--- a/bsd/vfs/vfs_attrlist.c
+++ b/bsd/vfs/vfs_attrlist.c
@@ -4372,6 +4372,9 @@ getattrlistbulk(proc_t p, struct getattrlistbulk_args *uap, int32_t *retval)
FV_LOCK(fvdata);
+#if CONFIG_UNION_MOUNTS
+unionread:
+#endif
/*
* getattrlistbulk(2) maintains its offset in fv_offset. However
* if the offset in the file glob is set (or reset) to 0, the directory
@@ -4464,6 +4467,52 @@ getattrlistbulk(proc_t p, struct getattrlistbulk_args *uap, int32_t *retval)
ut->uu_flag &= ~UT_KERN_RAGE_VNODES;
}
+#if CONFIG_UNION_MOUNTS
+ /* Match getdirentriesattr: consume the upper directory before moving
+ * this open descriptor to the corresponding lower union directory.
+ * Snapshot enumeration must stay on the requested snapshot directory.
+ * Recheck lower-directory access before publishing the new vnode.
+ */
+ if (!error && !count && eofflag &&
+ !(uap->options & FSOPT_LIST_SNAPSHOT) &&
+ (dvp->v_mount->mnt_flag & MNT_UNION)) {
+ vnode_t uvp;
+ int union_error = lookup_traverse_union(dvp, &uvp, ctx);
+ if (!union_error) {
+ if (uvp->v_type != VDIR) {
+ union_error = ENOTDIR;
+ }
+#if CONFIG_MACF
+ if (!union_error) {
+ union_error = mac_vnode_check_readdir(ctx, uvp);
+ }
+#endif
+ if (!union_error) {
+ union_error = vnode_authorize(uvp, NULL, action, ctx);
+ }
+ if (!union_error) {
+ union_error = vnode_ref_ext(uvp, fp->fp_glob->fg_flag & O_EVTONLY, 0);
+ }
+ if (!union_error) {
+ fp_set_data(fp, uvp);
+ fp->fp_glob->fg_offset = 0;
+ vnode_rele_internal(dvp, fp->fp_glob->fg_flag & O_EVTONLY, 0, 0);
+ vnode_put(dvp);
+ dvp = uvp;
+ /* The existing zero-offset path discards upper EOF and
+ * buffered dirents before constructing the lower UIO. */
+ goto unionread;
+ }
+ vnode_put(uvp);
+ }
+ /* No corresponding lower directory is ordinary end-of-directory;
+ * other failures must not silently hide lower entries. */
+ if (union_error != ENOENT) {
+ error = union_error;
+ }
+ }
+#endif
+
if (count) {
fvdata->fv_offset = uio_offset(auio);
fp->fp_glob->fg_offset = fvdata->fv_offset;
@@ -0,0 +1,22 @@
diff --git a/bsd/vfs/vfs_lookup.c b/bsd/vfs/vfs_lookup.c
index 772cee80..b8cf563c 100644
--- a/bsd/vfs/vfs_lookup.c
+++ b/bsd/vfs/vfs_lookup.c
@@ -1926,7 +1926,16 @@ lookup_traverse_union(vnode_t dvp, vnode_t *new_dvp, vfs_context_t ctx)
nd.ni_dvp = dvp->v_mount->mnt_vnodecovered;
error = namei(&nd);
if (error == 0) {
- *new_dvp = nd.ni_vp;
+ /*
+ * Only a directory below can hold the name: the base system keeps a symlink where the
+ * mounted system has a framework directory, and searching that symlink gave ENOTDIR.
+ */
+ if (nd.ni_vp->v_type != VDIR) {
+ vnode_put(nd.ni_vp);
+ error = ENOENT;
+ } else {
+ *new_dvp = nd.ni_vp;
+ }
}
nameidone(&nd);
done:
@@ -0,0 +1,43 @@
diff --git a/bsd/kern/sys_work_interval.c b/bsd/kern/sys_work_interval.c
index 3edde7c2..527f0290 100644
--- a/bsd/kern/sys_work_interval.c
+++ b/bsd/kern/sys_work_interval.c
@@ -38,6 +38,22 @@
#include <libkern/libkern.h>
+#include <sys/proc.h>
+#include <kern/policy_internal.h>
+
+/* Board bring-up diagnostic: the last work-interval CREATE2 results, read by the boot-progress banner. */
+char native_wi_log[4][100];
+volatile unsigned native_wi_count;
+static void
+native_wi_note(const char *what, int error, int kret, uint32_t flags)
+{
+ char name[20];
+ proc_selfname(name, sizeof(name));
+ unsigned slot = native_wi_count++ % 4;
+ snprintf(native_wi_log[slot], sizeof(native_wi_log[slot]), "WI#%u p%d %s %s err=%d kret=%d flags=%x app=%d",
+ native_wi_count, proc_selfpid(), name, what, error, kret, flags, (int)task_is_app(current_task()));
+}
+
int
work_interval_ctl(__unused proc_t p, struct work_interval_ctl_args *uap,
__unused int32_t *retval)
@@ -69,6 +85,7 @@ work_interval_ctl(__unused proc_t p, struct work_interval_ctl_args *uap,
}
if ((error = priv_check_cred(kauth_cred_get(), PRIV_WORK_INTERVAL, 0)) != 0) {
+ native_wi_note("priv", error, 0, create_params.wicp_create_flags);
return error;
}
@@ -79,6 +96,7 @@ work_interval_ctl(__unused proc_t p, struct work_interval_ctl_args *uap,
};
kret = kern_work_interval_create(current_thread(), &create_args);
+ native_wi_note("create", 0, kret, create_params.wicp_create_flags);
/* thread already has a work interval */
if (kret == KERN_FAILURE) {
@@ -0,0 +1,34 @@
diff --git a/osfmk/arm/pmap/pmap.c b/osfmk/arm/pmap/pmap.c
index e2f5a63a..9760c9ce 100644
--- a/osfmk/arm/pmap/pmap.c
+++ b/osfmk/arm/pmap/pmap.c
@@ -85,8 +85,12 @@
#include <arm64/proc_reg.h>
#include <pexpert/arm64/boot.h>
#if __has_include(<arm64/ppl/sart.h>)
+#if XNU_MONITOR_ANS2_SART
#include <arm64/ppl/sart.h>
+#endif
+#if XNU_MONITOR_UAT_PPL
#include <arm64/ppl/uat.h>
+#endif
#else
/* Public source omits the SART DMA filter; non-Apple SoCs have none. */
static inline void
@@ -12249,6 +12253,7 @@ pmap_unpin_kernel_pages(vm_offset_t kva __unused, size_t nbytes __unused)
#endif /* !XNU_MONITOR */
+#if PMAP_CS_PPL_MONITOR || PMAP_CS_INCLUDE_CODE_SIGNING
MARK_AS_PMAP_TEXT static inline void
pmap_cs_lockdown_pages(vm_address_t kva, vm_size_t size, bool ppl_writable)
{
@@ -12271,6 +12276,8 @@ pmap_cs_unlockdown_pages(vm_address_t kva, vm_size_t size, bool ppl_writable)
#endif
}
+#endif /* PMAP_CS_PPL_MONITOR || PMAP_CS_INCLUDE_CODE_SIGNING */
+
/**
* Perform basic validation checks on the destination only and
* corresponding offset/sizes prior to writing to a read only allocation.
@@ -0,0 +1,195 @@
diff --git a/bsd/kern/bsd_init.c b/bsd/kern/bsd_init.c
index ffe7ce09..a18d0876 100644
--- a/bsd/kern/bsd_init.c
+++ b/bsd/kern/bsd_init.c
@@ -481,6 +481,91 @@ bsd_rooted_ramdisk(void)
* of the tro_ucred field so that the uthread structure can be
* used like any other.
*/
+#if DEVELOPMENT || DEBUG
+extern kern_return_t OSKextLoadKextWithIdentifier(const char *identifier);
+volatile int native_apfs_load_status = -1;
+volatile uint32_t native_apfs_registered = 0;
+volatile uintptr_t native_apfs_vfsops = 0;
+
+__attribute__((noinline)) void
+native_apfs_probe_checkpoint(void)
+{
+ __asm__ volatile("" ::: "memory");
+}
+
+static void
+native_apfs_load_probe(void)
+{
+ int enabled = 0;
+ if (!PE_parse_boot_argn("offline-apfs-load", &enabled, sizeof(enabled)) || !enabled) {
+ return;
+ }
+ native_apfs_load_status = OSKextLoadKextWithIdentifier("com.apple.filesystems.apfs");
+ mount_list_lock();
+ for (struct vfstable *entry = vfsconf; entry; entry = entry->vfc_next) {
+ if (!strncmp(entry->vfc_name, "apfs", MFSNAMELEN)) {
+ native_apfs_registered = 1;
+ native_apfs_vfsops = (uintptr_t)entry->vfc_vfsops;
+ break;
+ }
+ }
+ mount_list_unlock();
+ native_apfs_probe_checkpoint();
+}
+
+volatile int native_iogpu_load_status = -1;
+
+__attribute__((noinline)) void
+native_iogpu_probe_checkpoint(void)
+{
+ __asm__ volatile("" ::: "memory");
+}
+
+static void
+native_iogpu_load_probe(void)
+{
+ int enabled = 0;
+ if (!PE_parse_boot_argn("offline-iogpu-load", &enabled, sizeof(enabled)) || !enabled) {
+ return;
+ }
+ native_iogpu_load_status = OSKextLoadKextWithIdentifier("com.apple.iokit.IOGPUFamily");
+ native_iogpu_probe_checkpoint();
+}
+
+volatile int native_mobile_graphics_load_status = -1;
+
+__attribute__((noinline)) void
+native_mobile_graphics_probe_checkpoint(void)
+{
+ __asm__ volatile("" ::: "memory");
+}
+
+static void
+native_mobile_graphics_load_probe(void)
+{
+ int enabled = 0;
+ if (!PE_parse_boot_argn("offline-mobile-graphics-load", &enabled, sizeof(enabled)) || !enabled) {
+ return;
+ }
+ native_mobile_graphics_load_status = OSKextLoadKextWithIdentifier("com.apple.iokit.IOMobileGraphicsFamily");
+ native_mobile_graphics_probe_checkpoint();
+#if defined(XNU_NATIVE_LEGACY_DISPLAY) && XNU_NATIVE_LEGACY_DISPLAY
+ int legacy_register = 0;
+ if (native_mobile_graphics_load_status == 0 &&
+ PE_parse_boot_argn("offline-legacy-register", &legacy_register, sizeof(legacy_register)) && legacy_register) {
+ extern void native_legacy_display_probe(void);
+ native_legacy_display_probe();
+ }
+#endif
+ int client_probe = 0;
+ if (native_mobile_graphics_load_status == 0 &&
+ PE_parse_boot_argn("offline-mobile-graphics-client", &client_probe, sizeof(client_probe)) && client_probe) {
+ extern void native_mobile_graphics_client_probe(void);
+ native_mobile_graphics_client_probe();
+ }
+}
+#endif
+
void
bsd_init(void)
{
@@ -700,6 +785,27 @@ bsd_init(void)
/* Initialize the file systems. */
bsd_init_kprintf("calling vfsinit\n");
vfsinit();
+#if DEVELOPMENT || DEBUG
+ native_apfs_load_probe();
+ native_mobile_graphics_load_probe();
+ native_iogpu_load_probe();
+#if defined(XNU_ADRENO_FIRMWARE_PROBE) && XNU_ADRENO_FIRMWARE_PROBE
+ extern void native_adreno_firmware_probe(void);
+ native_adreno_firmware_probe();
+#endif
+#if defined(XNU_ADRENO_PM4_PROBE) && XNU_ADRENO_PM4_PROBE
+ extern void native_adreno_pm4_probe(void);
+ native_adreno_pm4_probe();
+#endif
+#if defined(XNU_SMMU4K_PROBE) && XNU_SMMU4K_PROBE
+ extern void native_smmu4k_probe(void);
+ native_smmu4k_probe();
+#endif
+#if defined(XNU_CONSTANT_UPLOADS_PROBE) && XNU_CONSTANT_UPLOADS_PROBE
+ extern void native_constants_probe(void);
+ native_constants_probe();
+#endif
+#endif
#if CONFIG_PROC_UUID_POLICY
/* Initial proc_uuid_policy subsystem */
diff --git a/iokit/Kernel/IOUserClient.cpp b/iokit/Kernel/IOUserClient.cpp
index e3c0911f..206fd50f 100644
--- a/iokit/Kernel/IOUserClient.cpp
+++ b/iokit/Kernel/IOUserClient.cpp
@@ -6850,3 +6850,66 @@ OSMetaClassDefineReservedUnused(IOUserClient2022, 0);
OSMetaClassDefineReservedUnused(IOUserClient2022, 1);
OSMetaClassDefineReservedUnused(IOUserClient2022, 2);
OSMetaClassDefineReservedUnused(IOUserClient2022, 3);
+
+#if DEVELOPMENT || DEBUG
+extern "C" {
+volatile uint32_t native_mobile_graphics_abstract_null_mask;
+volatile uint64_t native_mobile_graphics_client_object;
+volatile uint64_t native_mobile_graphics_client_vptr;
+volatile uint64_t native_mobile_graphics_client_meta;
+volatile uint32_t native_mobile_graphics_client_size;
+volatile int native_mobile_graphics_client_init = -1;
+volatile uint32_t native_mobile_graphics_client_results[3] = {~0U, ~0U, ~0U};
+volatile int native_mobile_graphics_client_released;
+
+__attribute__((noinline)) void
+native_mobile_graphics_client_checkpoint(void)
+{
+ __asm__ volatile("" ::: "memory");
+}
+
+void
+native_mobile_graphics_client_probe(void)
+{
+ const char *abstract_names[] = {"IOMobileFramebuffer", "IOMobileFramebufferService", "IOMobileFramebufferLegacy"};
+ for (unsigned i = 0; i < 3; i++) {
+ OSObject *object = OSMetaClass::allocClassWithName(abstract_names[i]);
+ if (!object) {
+ native_mobile_graphics_abstract_null_mask |= 1U << i;
+ } else {
+ object->release();
+ }
+ }
+ OSObject *object = OSMetaClass::allocClassWithName("IOMobileFramebufferUserClient");
+ IOUserClient *client = OSDynamicCast(IOUserClient, object);
+ if (client) {
+ native_mobile_graphics_client_object = (uintptr_t)client;
+ native_mobile_graphics_client_vptr = *(const uintptr_t *)client;
+ native_mobile_graphics_client_meta = (uintptr_t)client->getMetaClass();
+ native_mobile_graphics_client_size = client->getMetaClass()->getClassSize();
+ native_mobile_graphics_client_init = client->init();
+ if (native_mobile_graphics_client_init) {
+ IOExternalMethodArguments arguments = {};
+ const uint32_t selectors[] = {90, 0x80000000U, ~0U};
+ for (unsigned i = 0; i < 3; i++) {
+ arguments.selector = selectors[i];
+ native_mobile_graphics_client_results[i] = client->externalMethod(selectors[i], &arguments, NULL, NULL, NULL);
+ }
+ }
+ }
+ if (object) {
+ object->release();
+ native_mobile_graphics_client_released = 1;
+ }
+ native_mobile_graphics_client_checkpoint();
+#if defined(XNU_NATIVE_MOBILE_DISPLAY) && XNU_NATIVE_MOBILE_DISPLAY
+ extern void native_mobile_display_provider_probe(void);
+ native_mobile_display_provider_probe();
+#endif
+#if defined(XNU_NATIVE_LEGACY_DISPLAY) && XNU_NATIVE_LEGACY_DISPLAY
+ extern void native_legacy_display_probe(void);
+ native_legacy_display_probe();
+#endif
+}
+}
+#endif
@@ -0,0 +1,21 @@
diff --git a/bsd/kern/bsd_init.c b/bsd/kern/bsd_init.c
index a18d0876..943459a1 100644
--- a/bsd/kern/bsd_init.c
+++ b/bsd/kern/bsd_init.c
@@ -1232,7 +1232,15 @@ bsd_autoconf(void)
}
}
- return IOKitBSDInit();
+ kern_return_t result = IOKitBSDInit();
+#if (DEVELOPMENT || DEBUG) && defined(XNU_NATIVE_LEGACY_DISPLAY) && XNU_NATIVE_LEGACY_DISPLAY
+ /* The original IOSurface personality requires the genuine IOBSD resource.
+ * The default-off framebuffer instrument may start only after publication.
+ */
+ extern void native_legacy_display_late_probe(void);
+ native_legacy_display_late_probe();
+#endif
+ return result;
}
@@ -0,0 +1,15 @@
diff --git a/bsd/kern/bsd_init.c b/bsd/kern/bsd_init.c
index 943459a1..a6c3a05b 100644
--- a/bsd/kern/bsd_init.c
+++ b/bsd/kern/bsd_init.c
@@ -916,6 +916,10 @@ bsd_init(void)
#if CONFIG_DTRACE
dtrace_postinit();
#endif
+#if (DEVELOPMENT || DEBUG) && defined(XNU_NATIVE_OFFLINE_GPU) && XNU_NATIVE_OFFLINE_GPU
+ extern void native_offline_gpu_publish(void);
+ native_offline_gpu_publish();
+#endif
/*
* We attach the loopback interface *way* down here to ensure
@@ -0,0 +1,74 @@
diff --git a/iokit/bsddev/IOKitBSDInit.cpp b/iokit/bsddev/IOKitBSDInit.cpp
index 2bbd161e..2b49e3c9 100644
--- a/iokit/bsddev/IOKitBSDInit.cpp
+++ b/iokit/bsddev/IOKitBSDInit.cpp
@@ -572,6 +572,69 @@ IOGetBootManifestHash(char *hash_data, size_t *hash_data_size)
return result;
}
+#if defined(XNU_OWNER_BOOT_MANIFEST) && XNU_OWNER_BOOT_MANIFEST
+#if !DEVELOPMENT && !DEBUG
+#error Owner image-set metadata is an offline development instrument
+#endif
+#include <libkern/crypto/sha2.h>
+extern "C" int native_verified_block_manifest_sha384(dev_t, const char *, uint8_t[48]);
+extern "C" int native_owner_publish_boot_manifest(dev_t, const char *, dev_t, const char *);
+extern "C" {
+volatile int native_owner_boot_manifest_status = -1;
+uint8_t native_owner_boot_manifest_hash[48];
+}
+
+extern "C" int
+native_owner_publish_boot_manifest(dev_t rootDevice, const char *rootUUID,
+ dev_t cacheDevice, const char *cacheUUID)
+{
+ static const char domain[] = "VerifiedRoot.OwnerBootSet.v1";
+ uint8_t rootDigest[48], cacheDigest[48], digest[48];
+ uint8_t rootRole = 1, cacheRole = 2;
+ if (rootDevice == cacheDevice ||
+ !native_verified_block_manifest_sha384(rootDevice, rootUUID, rootDigest) ||
+ !native_verified_block_manifest_sha384(cacheDevice, cacheUUID, cacheDigest)) {
+ native_owner_boot_manifest_status = 1;
+ return 0;
+ }
+ SHA384_CTX context;
+ SHA384_Init(&context);
+ SHA384_Update(&context, domain, sizeof(domain));
+ SHA384_Update(&context, &rootRole, sizeof(rootRole));
+ SHA384_Update(&context, rootDigest, sizeof(rootDigest));
+ SHA384_Update(&context, &cacheRole, sizeof(cacheRole));
+ SHA384_Update(&context, cacheDigest, sizeof(cacheDigest));
+ SHA384_Final(digest, &context);
+ IORegistryEntry *chosen = IORegistryEntry::fromPath("/chosen", gIODTPlane);
+ if (!chosen) { native_owner_boot_manifest_status = 2; return 0; }
+ // Never replace an existing firmware/Apple identity with an owner identity.
+ if (chosen->getProperty("boot-manifest-hash") || chosen->getProperty("owner-boot-manifest-domain")) {
+ chosen->release();
+ native_owner_boot_manifest_status = 3;
+ return 0;
+ }
+ OSData *value = OSData::withBytes(digest, sizeof(digest));
+ OSData *tag = OSData::withBytes(domain, sizeof(domain));
+ bool tagged = tag && chosen->setProperty("owner-boot-manifest-domain", tag);
+ bool published = tagged && value && chosen->setProperty("boot-manifest-hash", value);
+ uint8_t actual[48];
+ size_t actualSize = sizeof(actual);
+ bool result = published && IOGetBootManifestHash(reinterpret_cast<char *>(actual), &actualSize) &&
+ actualSize == sizeof(digest) && !memcmp(actual, digest, sizeof(digest));
+ if (!result) {
+ if (published) chosen->removeProperty("boot-manifest-hash");
+ if (tagged) chosen->removeProperty("owner-boot-manifest-domain");
+ } else {
+ memcpy(native_owner_boot_manifest_hash, actual, sizeof(actual));
+ }
+ OSSafeReleaseNULL(tag);
+ OSSafeReleaseNULL(value);
+ chosen->release();
+ native_owner_boot_manifest_status = result ? 0 : 4;
+ return result;
+}
+#endif
+
/*
* Set NVRAM to boot into the right flavor of Recovery,
* optionally passing a UUID of a volume that failed to boot.
@@ -0,0 +1,110 @@
diff --git a/bsd/vm/vm_unix.c b/bsd/vm/vm_unix.c
index 64d187a5..01684803 100644
--- a/bsd/vm/vm_unix.c
+++ b/bsd/vm/vm_unix.c
@@ -1384,6 +1384,65 @@ static void shared_region_map_and_slide_cleanup(
struct _sr_file_mappings *sr_file_mappings,
struct vm_shared_region *shared_region);
+#if defined(XNU_OWNER_CACHE_CARRIER) && XNU_OWNER_CACHE_CARRIER && !CONFIG_CSR
+#if !DEVELOPMENT && !DEBUG
+#error The independent owner cache carrier is a development instrument
+#endif
+extern int native_verified_block_root_authorized(dev_t);
+extern int native_verified_block_volume_device(const char *, dev_t *, char *, size_t);
+unsigned long native_owner_cache_carrier_accepts = 0;
+unsigned long native_owner_cache_carrier_rejects = 0;
+volatile dev_t native_owner_cache_carrier_root_device = NODEV;
+volatile dev_t native_owner_cache_carrier_file_device = NODEV;
+
+static boolean_t
+native_owner_shared_cache_carrier(vnode_t process_root, vnode_t file)
+{
+ const char *root_uuid = "04769D26-2CA0-4825-B443-E2E5F88300F2";
+#if defined(XNU_OWNER_CACHE_CARRIER_WRONG_UUID) && XNU_OWNER_CACHE_CARRIER_WRONG_UUID
+ const char *cache_uuid = "9846FE78-2910-4297-AF97-D59BB6F8FC16";
+#else
+ const char *cache_uuid = "8846FE78-2910-4297-AF97-D59BB6F8FC16";
+#endif
+ const char *directory = "/private/preboot/System/Library/Caches/com.apple.dyld";
+ vnode_t expected_parent = NULLVP, device_vnode;
+ dev_t root_device = NODEV, cache_device = NODEV, expected = NODEV;
+ char device_path[64];
+ boolean_t authorized = FALSE;
+ if (process_root != rootvnode || !file || vnode_vtype(file) != VREG) goto out;
+ mount_t root_mount = vnode_mount(process_root), cache_mount = vnode_mount(file);
+ if (!root_mount || !cache_mount || root_mount == cache_mount ||
+ !(vfs_flags(root_mount) & MNT_RDONLY) || !(vfs_flags(cache_mount) & MNT_RDONLY) ||
+ strcmp(vfs_statfs(root_mount)->f_fstypename, "apfs") ||
+ strcmp(vfs_statfs(cache_mount)->f_fstypename, "apfs")) goto out;
+ device_vnode = vfs_devvp(root_mount);
+ if (device_vnode) {
+ root_device = vnode_specrdev(device_vnode);
+ vnode_put(device_vnode);
+ }
+ device_vnode = vfs_devvp(cache_mount);
+ if (device_vnode) {
+ cache_device = vnode_specrdev(device_vnode);
+ vnode_put(device_vnode);
+ }
+ if (root_device == NODEV || cache_device == NODEV ||
+ !native_verified_block_volume_device(root_uuid, &expected, device_path, sizeof(device_path)) ||
+ expected != root_device ||
+ !native_verified_block_volume_device(cache_uuid, &expected, device_path, sizeof(device_path)) ||
+ expected != cache_device) goto out;
+ if (vnode_lookup(directory, 0, &expected_parent, vfs_context_current())) goto out;
+ authorized = vnode_vtype(expected_parent) == VDIR && vnode_mount(expected_parent) == cache_mount &&
+ vnode_parent(file) == expected_parent && native_verified_block_root_authorized(root_device);
+out:
+ if (expected_parent) vnode_put(expected_parent);
+ native_owner_cache_carrier_root_device = root_device;
+ native_owner_cache_carrier_file_device = cache_device;
+ if (authorized) os_atomic_inc(&native_owner_cache_carrier_accepts, relaxed);
+ else os_atomic_inc(&native_owner_cache_carrier_rejects, relaxed);
+ return authorized;
+}
+#endif
+
/*
* Setup part of _shared_region_map_and_slide().
* It had to be broken out of _shared_region_map_and_slide() to
@@ -1716,7 +1775,13 @@ after_root_check:
* is either on the root volume or in the preboot cryptex volume.
*/
assert(rdir_vp != NULL);
+#if defined(XNU_OWNER_CACHE_CARRIER) && XNU_OWNER_CACHE_CARRIER
+ boolean_t owner_carrier = FALSE;
+#endif
if (srfmp->vp->v_mount != rdir_vp->v_mount) {
+#if defined(XNU_OWNER_CACHE_CARRIER) && XNU_OWNER_CACHE_CARRIER
+ owner_carrier = native_owner_shared_cache_carrier(rdir_vp, srfmp->vp);
+#endif
vnode_t preboot_vp = NULL;
#if XNU_TARGET_OS_OSX
#define PREBOOT_CRYPTEX_PATH "/System/Volumes/Preboot/Cryptexes"
@@ -1724,7 +1789,11 @@ after_root_check:
#define PREBOOT_CRYPTEX_PATH "/private/preboot/Cryptexes"
#endif
error = vnode_lookup(PREBOOT_CRYPTEX_PATH, 0, &preboot_vp, vfs_context_current());
- if (error || srfmp->vp->v_mount != preboot_vp->v_mount) {
+ if ((error || srfmp->vp->v_mount != preboot_vp->v_mount)
+#if defined(XNU_OWNER_CACHE_CARRIER) && XNU_OWNER_CACHE_CARRIER
+ && !owner_carrier
+#endif
+ ) {
SHARED_REGION_TRACE_ERROR(
("shared_region: %p [%d(%s)] map(%p:'%s'): "
"not on process' root volume nor preboot volume\n",
@@ -1743,7 +1812,11 @@ after_root_check:
}
#endif /* CONFIG_CSR */
- if (scdir_enforce) {
+ if (scdir_enforce
+#if defined(XNU_OWNER_CACHE_CARRIER) && XNU_OWNER_CACHE_CARRIER && !CONFIG_CSR
+ && !owner_carrier
+#endif
+ ) {
char **expected_scdir_path = is_driverkit ? driverkit_scdir_path : scdir_path;
struct vnode *scdir_vp = NULL;
for (expected_scdir_path = is_driverkit ? driverkit_scdir_path : scdir_path;
@@ -0,0 +1,80 @@
diff --git a/bsd/kern/bsd_init.c b/bsd/kern/bsd_init.c
index a6c3a05b..9c70d1f8 100644
--- a/bsd/kern/bsd_init.c
+++ b/bsd/kern/bsd_init.c
@@ -1057,9 +1057,41 @@ bsd_init(void)
if (require_rootauth) {
/* enforce sealedness */
int autherr = VNOP_IOCTL(rootvnode, FSIOC_KERNEL_ROOTAUTH, NULL, 0, vfs_context_kernel());
- if (autherr) {
+ int owner_authorized = 0;
+#if defined(XNU_OWNER_ROOT_AUDIT) && XNU_OWNER_ROOT_AUDIT
+ {
+ extern int native_verified_block_root_authorized(dev_t);
+ extern volatile int native_owner_root_apple_autherr, native_owner_root_decision, native_owner_root_readonly;
+ extern volatile dev_t native_owner_root_mount_device, native_owner_root_selected_device;
+ extern void native_owner_root_checkpoint(int, int, dev_t);
+ mount_t root_mount = vnode_mount(rootvnode);
+ vnode_t device_vnode = vfs_devvp(root_mount);
+ dev_t device = device_vnode ? vnode_specrdev(device_vnode) : NODEV;
+ int readonly = (vfs_flags(root_mount) & MNT_RDONLY) != 0;
+ int owner = readonly && device_vnode && native_verified_block_root_authorized(device);
+ if (device_vnode) vnode_put(device_vnode);
+ native_owner_root_mount_device = device;
+ native_owner_root_selected_device = rootdev;
+ native_owner_root_apple_autherr = autherr;
+ native_owner_root_decision = owner;
+ native_owner_root_readonly = readonly;
+#if defined(XNU_OWNER_ROOT_TRUST) && XNU_OWNER_ROOT_TRUST
+ owner_authorized = owner;
+#endif
+ native_owner_root_checkpoint(autherr, owner, device);
+ }
+#endif
+ if (autherr && !owner_authorized) {
panic("rootvp not authenticated after mounting");
}
+#if defined(XNU_OWNER_ROOT_TRUST) && XNU_OWNER_ROOT_TRUST
+ {
+ extern volatile int native_owner_root_accepted;
+ extern void native_owner_root_accepted_checkpoint(int, int);
+ native_owner_root_accepted = owner_authorized;
+ native_owner_root_accepted_checkpoint(autherr, owner_authorized);
+ }
+#endif
}
}
@@ -1189,6 +1221,32 @@ bsd_init(void)
bsd_init_kprintf("done\n");
}
+#if defined(XNU_OWNER_ROOT_AUDIT) && XNU_OWNER_ROOT_AUDIT
+#if defined(XNU_OWNER_ROOT_TRUST) && XNU_OWNER_ROOT_TRUST
+volatile int native_owner_root_trust_enabled = 1;
+#else
+volatile int native_owner_root_trust_enabled = 0;
+#endif
+volatile int native_owner_root_accepted = 0;
+volatile int native_owner_root_apple_autherr = -1;
+volatile int native_owner_root_decision = -1;
+volatile int native_owner_root_readonly = -1;
+volatile dev_t native_owner_root_mount_device = NODEV;
+volatile dev_t native_owner_root_selected_device = NODEV;
+void native_owner_root_accepted_checkpoint(int, int);
+__attribute__((noinline)) void
+native_owner_root_accepted_checkpoint(int apple, int owner)
+{
+ __asm__ volatile("" : : "r"(apple), "r"(owner) : "memory");
+}
+void native_owner_root_checkpoint(int, int, dev_t);
+__attribute__((noinline)) void
+native_owner_root_checkpoint(int apple, int owner, dev_t device)
+{
+ __asm__ volatile("" : : "r"(apple), "r"(owner), "r"(device) : "memory");
+}
+#endif
+
void
bsdinit_task(void)
{
@@ -0,0 +1,20 @@
diff --git a/bsd/kern/bsd_init.c b/bsd/kern/bsd_init.c
index 9c70d1f8..6799ecf0 100644
--- a/bsd/kern/bsd_init.c
+++ b/bsd/kern/bsd_init.c
@@ -909,6 +909,15 @@ bsd_init(void)
bsd_init_kprintf("calling sysctl_mib_init\n");
sysctl_mib_init();
+#if defined(XNU_VERIFIED_ROOT_PROBE) && XNU_VERIFIED_ROOT_PROBE
+ {
+ extern void native_verified_root_probe(void);
+ int enabled = 0;
+ if (PE_parse_boot_argn("offline-owner-root-check", &enabled, sizeof(enabled)) && enabled) {
+ native_verified_root_probe();
+ }
+ }
+#endif
bsd_init_kprintf("calling bsd_autoconf\n");
bsd_autoconf();
@@ -0,0 +1,314 @@
diff --git a/bsd/kern/kern_exec.c b/bsd/kern/kern_exec.c
index c3f9fca2..9969eb6f 100644
--- a/bsd/kern/kern_exec.c
+++ b/bsd/kern/kern_exec.c
@@ -7940,8 +7940,256 @@ create_unix_stack(vm_map_t map, load_result_t* load_result,
* word width of the kernel. Once the self-exec completes, the
* initproc might be different.
*/
+#if defined(XNU_OWNER_CACHE_MOUNT) && XNU_OWNER_CACHE_MOUNT
+#if !DEVELOPMENT && !DEBUG
+#error The offline owner cache mount is a development instrument
+#endif
+extern int native_verified_block_volume_device(const char *, dev_t *, char *, size_t);
+#if defined(XNU_OWNER_BOOT_MANIFEST) && XNU_OWNER_BOOT_MANIFEST
+extern int native_owner_publish_boot_manifest(dev_t, const char *, dev_t, const char *);
+#endif
+#if defined(XNU_OWNER_CACHE_STARTUP) && XNU_OWNER_CACHE_STARTUP
+extern void commpage_update_dyld_flags(uint64_t);
+volatile uint64_t native_owner_cache_dyld_flags = 0;
+#endif
+extern int native_verified_block_root_authorized(dev_t);
+volatile int native_owner_cache_stage = 0;
+volatile int native_owner_cache_status = -1;
+volatile int native_owner_cache_mount_status = -1;
+volatile int native_owner_cache_readonly = 0;
+volatile int native_owner_cache_authorized = 0;
+volatile int native_owner_cache_root_recheck = 0;
+volatile uint64_t native_owner_cache_actual_mount_flags = 0;
+volatile uintptr_t native_owner_cache_original_root = 0;
+volatile uintptr_t native_owner_cache_current_root = 0;
+volatile int native_owner_cache_native_union = 0;
+volatile dev_t native_owner_cache_selected_device = NODEV;
+volatile dev_t native_owner_cache_mounted_device = NODEV;
+char native_owner_cache_device_path[64];
+void native_owner_cache_checkpoint(int, int);
+__attribute__((noinline)) void
+native_owner_cache_checkpoint(int status, int authorized)
+{
+ __asm__ volatile("" : : "r"(status), "r"(authorized) : "memory");
+}
+
+static int
+native_owner_cache_prepare(proc_t p, user_addr_t scratch, user_addr_t *envp)
+{
+#ifndef XNU_OWNER_CACHE_VOLUME_UUID
+#define XNU_OWNER_CACHE_VOLUME_UUID "8846FE78-2910-4297-AF97-D59BB6F8FC16"
+#endif
+ const char *volume = XNU_OWNER_CACHE_VOLUME_UUID;
+#if defined(XNU_OWNER_UNION_SYSTEMOS) && XNU_OWNER_UNION_SYSTEMOS
+#if !CONFIG_UNION_MOUNTS
+#error The owner composition requires the original native union-mount implementation
+#endif
+ const char *mountpoint = "/";
+#else
+ const char *mountpoint = "/private/preboot";
+#endif
+#if !defined(XNU_OWNER_UNION_SYSTEMOS) || !XNU_OWNER_UNION_SYSTEMOS
+ const char *environment = "DYLD_SHARED_CACHE_DIR=/private/preboot/System/Library/Caches/com.apple.dyld";
+#endif
+ vnode_t original_os_root = rootvnode;
+ native_owner_cache_original_root = (uintptr_t)original_os_root;
+ mount_t root_mount = vnode_mount(original_os_root);
+ vnode_t device_vnode = vfs_devvp(root_mount);
+ dev_t root_device = device_vnode ? vnode_specrdev(device_vnode) : NODEV;
+ dev_t selected = NODEV;
+ vnode_t mounted_root = NULLVP;
+ int error = EAUTH, root_authorized = 0, cache_authorized = 0;
+ int32_t result = 0;
+ user64_addr_t spec = scratch + 0x160;
+ char device_path[64] = {0};
+ uint8_t mount_data[0x150] = {0};
+ uint64_t mount_flags = MNT_RDONLY;
+ uint16_t mount_mode = 1;
+ uint32_t anonymous_owner = 99;
+#if !defined(XNU_OWNER_UNION_SYSTEMOS) || !XNU_OWNER_UNION_SYSTEMOS
+#if defined(XNU_OWNER_CRYPTEX_FILES) && XNU_OWNER_CRYPTEX_FILES
+ const char *fallback = "DYLD_FALLBACK_LIBRARY_PATH=/private/preboot/usr/lib";
+ user64_addr_t environment_vector[3] = {scratch + 0x400, scratch + 0x600, USER_ADDR_NULL};
+#else
+ user64_addr_t environment_vector[2] = {scratch + 0x400, USER_ADDR_NULL};
+#endif
+#endif
+ struct mount_args request = {.type = scratch + 0x100, .path = scratch + 0x120,
+ .flags = MNT_RDONLY, .data = scratch + 0x200};
+ request.flags = (int)mount_flags;
+#if defined(XNU_OWNER_UNION_SYSTEMOS) && XNU_OWNER_UNION_SYSTEMOS
+ request.flags |= MNT_UNION;
+#endif
+ if (device_vnode) vnode_put(device_vnode);
+ root_authorized = (vfs_flags(root_mount) & MNT_RDONLY) && root_device != NODEV &&
+ native_verified_block_root_authorized(root_device);
+ if (!root_authorized) goto done;
+ native_owner_cache_stage = 1;
+ if (!native_verified_block_volume_device(volume, &selected, device_path,
+ sizeof(device_path))) goto done;
+ strlcpy(native_owner_cache_device_path, device_path, sizeof(native_owner_cache_device_path));
+ native_owner_cache_selected_device = selected;
+ native_owner_cache_stage = 2;
+ /* Exact user-mount payload already qualified against this original APFS. */
+ memcpy(mount_data, &spec, sizeof(spec));
+ memcpy(mount_data + 8, &mount_flags, sizeof(mount_flags));
+ memcpy(mount_data + 16, &mount_mode, sizeof(mount_mode));
+ memcpy(mount_data + 0x144, &anonymous_owner, sizeof(anonymous_owner));
+ memcpy(mount_data + 0x148, &anonymous_owner, sizeof(anonymous_owner));
+ if ((error = copyout("apfs", request.type, 5)) != 0 ||
+ (error = copyout(mountpoint, request.path, strlen(mountpoint) + 1)) != 0 ||
+ (error = copyout(device_path, spec, strlen(device_path) + 1)) != 0 ||
+ (error = copyout(mount_data, request.data, sizeof(mount_data))) != 0) goto done;
+ native_owner_cache_stage = 3;
+ error = mount(p, &request, &result);
+ native_owner_cache_mount_status = error;
+ if (error) goto done;
+ native_owner_cache_stage = 4;
+ error = vnode_lookup(mountpoint, 0, &mounted_root, vfs_context_current());
+ if (error) goto done;
+ mount_t cache_mount = vnode_mount(mounted_root);
+ device_vnode = vfs_devvp(cache_mount);
+ dev_t actual = device_vnode ? vnode_specrdev(device_vnode) : NODEV;
+ if (device_vnode) vnode_put(device_vnode);
+ native_owner_cache_mounted_device = actual;
+ int readonly = (vfs_flags(cache_mount) & MNT_RDONLY) != 0;
+ native_owner_cache_readonly = readonly;
+ native_owner_cache_actual_mount_flags = vfs_flags(cache_mount);
+ native_owner_cache_current_root = (uintptr_t)rootvnode;
+ int native_union = (vfs_flags(cache_mount) & MNT_UNION) &&
+ vnode_mountedhere(original_os_root) == cache_mount && rootvnode == mounted_root;
+ native_owner_cache_native_union = native_union;
+ cache_authorized = readonly && actual == selected &&
+ !strcmp(vfs_statfs(cache_mount)->f_fstypename, "apfs") && native_verified_block_root_authorized(actual);
+#if defined(XNU_OWNER_UNION_SYSTEMOS) && XNU_OWNER_UNION_SYSTEMOS
+ cache_authorized = cache_authorized && native_union;
+#endif
+ if (!cache_authorized) { error = EAUTH; goto done; }
+ native_owner_cache_stage = 5;
+#if !defined(XNU_OWNER_UNION_SYSTEMOS) || !XNU_OWNER_UNION_SYSTEMOS
+ if ((error = copyout(environment, environment_vector[0], strlen(environment) + 1)) != 0 ||
+ (error = copyout(environment_vector, scratch + 0x500, sizeof(environment_vector))) != 0) goto done;
+#if defined(XNU_OWNER_CRYPTEX_FILES) && XNU_OWNER_CRYPTEX_FILES
+ if ((error = copyout(fallback, scratch + 0x600, strlen(fallback) + 1)) != 0) goto done;
+#endif
+ *envp = scratch + 0x500;
+#else
+ *envp = USER_ADDR_NULL;
+#endif
+ native_owner_cache_stage = 6;
+done:
+ if (mounted_root) vnode_put(mounted_root);
+ int root_recheck = root_authorized && native_verified_block_root_authorized(root_device);
+ native_owner_cache_root_recheck = root_recheck;
+ if (!root_recheck) error = EAUTH;
+#if defined(XNU_OWNER_BOOT_MANIFEST) && XNU_OWNER_BOOT_MANIFEST
+ if (!error && cache_authorized && root_recheck &&
+ !native_owner_publish_boot_manifest(root_device, "04769D26-2CA0-4825-B443-E2E5F88300F2",
+ selected, volume)) error = EAUTH;
+#endif
+#if defined(XNU_OWNER_CACHE_STARTUP) && XNU_OWNER_CACHE_STARTUP
+ if (!error && cache_authorized && root_recheck) {
+ /* Original dyld bit 3 selects no ignition; the owner chain and actual
+ * readonly mounts above supply image authentication in this mode. */
+ dyld_flags |= UINT64_C(8);
+ commpage_update_dyld_flags(dyld_flags);
+ native_owner_cache_dyld_flags = dyld_flags;
+ }
+#endif
+ native_owner_cache_authorized = cache_authorized;
+ native_owner_cache_status = error;
+ native_owner_cache_checkpoint(error, cache_authorized);
+ return error;
+}
+#endif
+
+#if defined(XNU_OWNER_DATA_MOUNT) && XNU_OWNER_DATA_MOUNT
+#if !defined(XNU_OWNER_BOOT_MANIFEST) || !XNU_OWNER_BOOT_MANIFEST
+#error Scratch Data mounting requires the authenticated original OS startup path
+#endif
+#if defined(XNU_NATIVE_RAM_BLOCK) && XNU_NATIVE_RAM_BLOCK
+#ifndef XNU_OWNER_DATA_VOLUME_UUID
+#error Supply the observed scratch volume UUID for the IOKit backend
+#endif
+extern int native_ram_block_volume_seed(const char *, dev_t *, char *, size_t, uint64_t *, uint64_t *, uint8_t *);
+#endif
+extern dev_t mdevlookup(int);
+extern int mdevgetrange(int, uint64_t *, uint64_t *);
+volatile int native_owner_data_status = -1;
+volatile int native_owner_data_stage = 0;
+volatile int native_owner_data_readonly = -1;
+volatile dev_t native_owner_data_device = NODEV;
+uint64_t native_owner_data_seed_base, native_owner_data_seed_size;
+uint8_t native_owner_data_seed_sha256[32];
+char native_owner_data_device_path[64];
+
static int
-load_init_program_at_path(proc_t p, user_addr_t scratch_addr, const char* path)
+native_owner_data_prepare(proc_t p, user_addr_t scratch)
+{
+ uint64_t base = 0, size = 0;
+ dev_t device = NODEV;
+ int error = EINVAL;
+ vnode_t data_root = NULLVP;
+ char device_path[64] = "/dev/md0";
+#if defined(XNU_NATIVE_RAM_BLOCK) && XNU_NATIVE_RAM_BLOCK
+ /* The typed provider supplies logical seed identity for both dense and
+ * sparse media. A sparse disk has no contiguous logical-byte address. */
+ if (!native_ram_block_volume_seed(XNU_OWNER_DATA_VOLUME_UUID, &device, device_path,
+ sizeof(device_path), &base, &size, native_owner_data_seed_sha256)) { error = ENOENT; goto done; }
+ /* The typed provider's disk may be far larger than 2 GiB (a persistent Data disk keeps only its seed's nonzero pages). */
+ if (!size || size > UINT64_C(0x10000000000) || (size & 4095)) goto done;
+#else
+ device = mdevlookup(0);
+ if (device == NODEV || mdevgetrange(0, &base, &size) || base < VM_MIN_KERNEL_ADDRESS ||
+ !size || size > UINT64_C(0x80000000) || (base & 4095) || (size & 4095)) goto done;
+ SHA256_CTX context;
+ SHA256_Init(&context);
+ SHA256_Update(&context, (const void *)base, size);
+ SHA256_Final(native_owner_data_seed_sha256, &context);
+#endif
+ native_owner_data_seed_base = base;
+ native_owner_data_seed_size = size;
+ native_owner_data_stage = 1;
+ strlcpy(native_owner_data_device_path, device_path, sizeof(native_owner_data_device_path));
+ uint8_t payload[0x150] = {0};
+ user64_addr_t spec = scratch + 0x160;
+ uint16_t mode = 1;
+ uint32_t anonymous_owner = 99;
+ memcpy(payload, &spec, sizeof(spec));
+ memcpy(payload + 16, &mode, sizeof(mode));
+ memcpy(payload + 0x144, &anonymous_owner, sizeof(anonymous_owner));
+ memcpy(payload + 0x148, &anonymous_owner, sizeof(anonymous_owner));
+ struct mount_args request = {.type = scratch + 0x100, .path = scratch + 0x120,
+ .flags = 0, .data = scratch + 0x200};
+ int32_t result = 0;
+ if ((error = copyout("apfs", request.type, 5)) ||
+ (error = copyout("/private/var", request.path, 13)) ||
+ (error = copyout(device_path, spec, strlen(device_path) + 1)) ||
+ (error = copyout(payload, request.data, sizeof(payload)))) goto done;
+ native_owner_data_stage = 2;
+ error = mount(p, &request, &result);
+ if (error) goto done;
+ native_owner_data_stage = 3;
+ error = vnode_lookup("/private/var", 0, &data_root, vfs_context_current());
+ if (error) goto done;
+ mount_t data_mount = vnode_mount(data_root);
+ vnode_t device_node = vfs_devvp(data_mount);
+ dev_t actual = device_node ? vnode_specrdev(device_node) : NODEV;
+ if (device_node) vnode_put(device_node);
+ native_owner_data_device = actual;
+ native_owner_data_readonly = (vfs_flags(data_mount) & MNT_RDONLY) != 0;
+ if (actual != device || (vfs_flags(data_mount) & MNT_RDONLY) ||
+ strcmp(vfs_statfs(data_mount)->f_fstypename, "apfs")) error = EINVAL;
+ if (!error) native_owner_data_stage = 4;
+done:
+ if (data_root) vnode_put(data_root);
+ native_owner_data_status = error;
+ return error;
+}
+#endif
+
+static int
+load_init_program_at_path(proc_t p, user_addr_t scratch_addr, const char* path, user_addr_t envp)
{
int retval[2];
int error;
@@ -8013,7 +8261,7 @@ load_init_program_at_path(proc_t p, user_addr_t scratch_addr, const char* path)
*/
init_exec_args.fname = argv0;
init_exec_args.argp = scratch_addr;
- init_exec_args.envp = USER_ADDR_NULL;
+ init_exec_args.envp = envp;
/*
* So that init task is set with uid,gid 0 token
@@ -8081,6 +8329,15 @@ load_init_program(proc_t p)
(void) mach_vm_allocate_kernel(map, &scratch_addr, map_page_size,
VM_MAP_KERNEL_FLAGS_ANYWHERE());
+ user_addr_t init_envp = USER_ADDR_NULL;
+#if defined(XNU_OWNER_CACHE_MOUNT) && XNU_OWNER_CACHE_MOUNT
+ int cache_error = native_owner_cache_prepare(p, (user_addr_t)scratch_addr, &init_envp);
+ if (cache_error) panic("offline owner SystemOS mount failed: %d", cache_error);
+#endif
+#if defined(XNU_OWNER_DATA_MOUNT) && XNU_OWNER_DATA_MOUNT
+ int data_error = native_owner_data_prepare(p, (user_addr_t)scratch_addr);
+ if (data_error) panic("offline scratch Data mount failed: %d", data_error);
+#endif
#if CONFIG_MEMORYSTATUS
(void) memorystatus_init_at_boot_snapshot();
#endif /* CONFIG_MEMORYSTATUS */
@@ -8106,7 +8363,7 @@ load_init_program(proc_t p)
if (is_release_suffix) {
printf("load_init_program: attempting to load /sbin/launchd\n");
- error = load_init_program_at_path(p, (user_addr_t)scratch_addr, "/sbin/launchd");
+ error = load_init_program_at_path(p, (user_addr_t)scratch_addr, "/sbin/launchd", init_envp);
if (!error) {
return;
}
@@ -8117,7 +8374,7 @@ load_init_program(proc_t p)
strlcat(launchd_path, launchd_suffix, sizeof(launchd_path));
printf("load_init_program: attempting to load %s\n", launchd_path);
- error = load_init_program_at_path(p, (user_addr_t)scratch_addr, launchd_path);
+ error = load_init_program_at_path(p, (user_addr_t)scratch_addr, launchd_path, init_envp);
if (!error) {
return;
} else if (error != ENOENT) {
@@ -8130,7 +8387,7 @@ load_init_program(proc_t p)
error = ENOENT;
for (i = 0; i < sizeof(init_programs) / sizeof(init_programs[0]); i++) {
printf("load_init_program: attempting to load %s\n", init_programs[i]);
- error = load_init_program_at_path(p, (user_addr_t)scratch_addr, init_programs[i]);
+ error = load_init_program_at_path(p, (user_addr_t)scratch_addr, init_programs[i], init_envp);
if (!error) {
return;
} else if (error != ENOENT) {
@@ -0,0 +1,22 @@
diff --git a/bsd/kern/kern_exec.c b/bsd/kern/kern_exec.c
index 6cbeaadf..c3f9fca2 100644
--- a/bsd/kern/kern_exec.c
+++ b/bsd/kern/kern_exec.c
@@ -8085,6 +8085,17 @@ load_init_program(proc_t p)
(void) memorystatus_init_at_boot_snapshot();
#endif /* CONFIG_MEMORYSTATUS */
+#if (DEBUG || DEVELOPMENT) && defined(XNU_NATIVE_AIR_INIT) && XNU_NATIVE_AIR_INIT
+ int offline_air = 0;
+ if (PE_parse_boot_argn("offline-air-init", &offline_air, sizeof(offline_air)) && offline_air) {
+ if (offline_air != 1) panic("offline AIR init invalid selector");
+ error = load_init_program_at_path(p, (user_addr_t)scratch_addr,
+ "/private/var/tmp/air-native", init_envp);
+ if (error) panic("offline signed AIR instrument exec failed: %d", error);
+ return;
+ }
+#endif
+
#if DEBUG || DEVELOPMENT
/* Check for boot-arg suffix first */
char launchd_suffix[64];
@@ -0,0 +1,69 @@
diff --git a/iokit/Kernel/IOUserClient.cpp b/iokit/Kernel/IOUserClient.cpp
index 206fd50f..d0111c12 100644
--- a/iokit/Kernel/IOUserClient.cpp
+++ b/iokit/Kernel/IOUserClient.cpp
@@ -4373,6 +4373,36 @@ is_io_service_set_authorization_id(
}
/* Routine io_service_open_ndr */
+/*
+ * The native GPU driver names, by the property NativeSandboxGrantProcess on its accelerator service, the processes (a comma-separated list of
+ * process names, the render server and the other processes that run its Metal plugin) to which it grants its own user client in place of the
+ * sandbox's verdict: the original profiles name no class of that driver. Nothing else is affected.
+ */
+extern "C" void proc_selfname(char * buf, int size);
+static bool
+native_sandbox_grant(IOService * service)
+{
+ OSString * list = OSDynamicCast(OSString, service->getProperty("NativeSandboxGrantProcess"));
+ char name[17] = {};
+ if (!list) {
+ return false;
+ }
+ proc_selfname(name, sizeof(name));
+ for (const char * at = list->getCStringNoCopy(); *at;) {
+ size_t length = 0;
+ while (at[length] && at[length] != ',') {
+ ++length;
+ }
+ /* A process name holds 16 characters, so a longer entry is compared by its first 16. */
+ if (length && 0 == strncmp(name, at, length < sizeof(name) - 1 ? length : sizeof(name) - 1)
+ && (length >= sizeof(name) - 1 || name[length] == 0)) {
+ return true;
+ }
+ at += length + (at[length] == ',' ? 1 : 0);
+ }
+ return false;
+}
+
kern_return_t
is_io_service_open_extended(
io_object_t _service,
@@ -4401,7 +4431,7 @@ is_io_service_open_extended(
}
#if CONFIG_MACF
- if (mac_iokit_check_open_service(kauth_cred_get(), service, connect_type) != 0) {
+ if (!native_sandbox_grant(service) && mac_iokit_check_open_service(kauth_cred_get(), service, connect_type) != 0) {
return kIOReturnNotPermitted;
}
#endif
@@ -4560,7 +4590,9 @@ is_io_service_open_extended(
res = kIOReturnNotPrivileged;
}
#if CONFIG_MACF
- else if (0 != mac_iokit_check_open(kauth_cred_get(), client, connect_type)) {
+ /* The service's grant (native_sandbox_grant) stands in for the sandbox's verdict on the render server's open. */
+ else if (!native_sandbox_grant(service)
+ && 0 != mac_iokit_check_open(kauth_cred_get(), client, connect_type)) {
IOLog("IOUC %s failed MACF in process %s\n",
client->getMetaClass()->getClassName(), creatorNameCStr);
res = kIOReturnNotPermitted;
@@ -4568,6 +4600,7 @@ is_io_service_open_extended(
#endif
if ((kIOReturnSuccess == res)
+ && !native_sandbox_grant(service)
&& gIOUCFilterCallbacks
&& gIOUCFilterCallbacks->io_filter_resolver) {
io_filter_policy_t filterPolicy;
@@ -0,0 +1,24 @@
diff --git a/iokit/Kernel/IOUserClient.cpp b/iokit/Kernel/IOUserClient.cpp
index d0111c12..0d9c70a6 100644
--- a/iokit/Kernel/IOUserClient.cpp
+++ b/iokit/Kernel/IOUserClient.cpp
@@ -4101,6 +4101,7 @@ is_io_registry_entry_get_property_bin(
}
+static bool native_sandbox_grant(IOService * service);
/* Routine io_registry_entry_set_properties */
kern_return_t
is_io_registry_entry_set_properties
@@ -4136,7 +4137,10 @@ is_io_registry_entry_set_properties
res = kIOReturnBadArgument;
}
#if CONFIG_MACF
- else if (0 != mac_iokit_check_set_properties(kauth_cred_get(),
+ /* A service's grant (native_sandbox_grant) stands in for the sandbox's verdict on setting its properties as well: backboardd's
+ * profile refuses FadePeriod, which CoreBrightness sends with every brightness, on the panel backlight (int-276). */
+ else if (!(OSDynamicCast(IOService, entry) && native_sandbox_grant(OSDynamicCast(IOService, entry)))
+ && 0 != mac_iokit_check_set_properties(kauth_cred_get(),
registry_entry, obj)) {
res = kIOReturnNotPermitted;
}
@@ -0,0 +1,402 @@
diff --git a/bsd/kern/kern_exec.c b/bsd/kern/kern_exec.c
index 9969eb6f..166272c7 100644
--- a/bsd/kern/kern_exec.c
+++ b/bsd/kern/kern_exec.c
@@ -8123,6 +8123,321 @@ uint64_t native_owner_data_seed_base, native_owner_data_seed_size;
uint8_t native_owner_data_seed_sha256[32];
char native_owner_data_device_path[64];
+/* Boot-time read-only union views of owner-provided GPU addon content over sealed system directories, mounted after the Data mount and
+ * before launchd starts: the Adreno Metal plugin bundle over /System/Library/Extensions (the original Metal discovery loads
+ * /System/Library/Extensions/<MetalPluginName>.bundle) and an owner launchd job over /System/Library/LaunchDaemons (a process outside
+ * every sandbox profile). Only with boot-arg sheng-gpu-addon=1. Never panics: a missing source directory or a refused mount only
+ * records its status. The backing directories are owner Data (not an authenticated immutable volume); code in them still has to
+ * pass AMFI through the loaded trust cache. */
+extern volatile int native_bindfs_boot_mount;
+volatile int native_gpu_addon_stage = 0;
+volatile int native_gpu_addon_status = -1;
+volatile int native_gpu_addon2_stage = 0;
+volatile int native_gpu_addon2_status = -1;
+volatile int native_gpu_addon3_stage = 0;
+volatile int native_gpu_addon3_status = -1;
+static void
+native_gpu_addon_mount(proc_t p, user_addr_t scratch, user_addr_t offset, const char *source, size_t source_size,
+ const char *point, size_t point_size, const char *visible, const char *own, volatile int *stage, volatile int *status)
+{
+ vnode_t probe = NULLVP;
+ int error;
+ *stage = 1;
+ error = vnode_lookup(source, 0, &probe, vfs_context_current());
+ if (error) {
+ *status = error;
+ return;
+ }
+ vnode_put(probe);
+ probe = NULLVP;
+ *stage = 2;
+ struct mount_args request = {.type = scratch + offset, .path = scratch + offset + 0x20,
+ .flags = MNT_RDONLY | MNT_UNION, .data = scratch + offset + 0x100};
+ int32_t result = 0;
+ if ((error = copyout("bindfs", request.type, 7)) || (error = copyout(point, request.path, point_size)) ||
+ (error = copyout(source, request.data, source_size))) {
+ *status = error;
+ return;
+ }
+ native_bindfs_boot_mount = 1;
+ error = mount(p, &request, &result);
+ native_bindfs_boot_mount = 0;
+ if (error) {
+ *status = error;
+ return;
+ }
+ *stage = 3;
+ /* The union must still show a sealed lower entry next to the owner's. */
+ error = vnode_lookup(visible, 0, &probe, vfs_context_current());
+ if (!error) {
+ vnode_put(probe);
+ probe = NULLVP;
+ *stage = 4;
+ /* ... and the owner's own entry must be visible through it (stage 5). */
+ error = vnode_lookup(own, 0, &probe, vfs_context_current());
+ if (!error) {
+ vnode_put(probe);
+ probe = NULLVP;
+ *stage = 5;
+ }
+ }
+ *status = error;
+}
+/* The GPU addon of the boot payload (scripts/make-gpu-addon.py, handed over by the loader as /chosen/memory-map/GpuAddon): every file
+ * whose content or mode differs from the mounted Data volume is written there before the addon mounts, so the plugin no longer has to
+ * be part of the Data seed and the seed's persistent store survives a plugin rebuild. Missing directories below /private/var/ngpu are
+ * created. A malformed addon is refused as a whole; a file that cannot be written keeps its old content.
+ * Counters for the boot log: files in the addon (-1 none), files written, files already equal, failures and the last error. */
+extern int native_ram_block_gpu_addon(const uint8_t **, uint64_t *) __attribute__((weak_import));
+#include <libkern/zlib.h>
+/* zlib's state, from kalloc with its size in front (the default allocator of the kernel's zlib is not meant for this). */
+static voidpf
+native_gpu_addon_zalloc(voidpf opaque __unused, uInt items, uInt size)
+{
+ size_t bytes = (size_t)items * size + 16;
+ uint8_t *block = kalloc_data(bytes, Z_WAITOK | Z_ZERO);
+ if (!block) {
+ return Z_NULL;
+ }
+ memcpy(block, &bytes, sizeof(bytes));
+ return block + 16;
+}
+static void
+native_gpu_addon_zfree(voidpf opaque __unused, voidpf address)
+{
+ uint8_t *block = (uint8_t *)address - 16;
+ size_t bytes = 0;
+ memcpy(&bytes, block, sizeof(bytes));
+ kfree_data(block, bytes);
+}
+static int
+native_gpu_addon_inflate(const uint8_t *packed, uint64_t packed_size, uint8_t *data, uint64_t size)
+{
+ z_stream stream;
+ memset(&stream, 0, sizeof(stream));
+ stream.zalloc = native_gpu_addon_zalloc;
+ stream.zfree = native_gpu_addon_zfree;
+ stream.next_in = (Bytef *)(uintptr_t)packed;
+ stream.avail_in = (uInt)packed_size;
+ stream.next_out = data;
+ stream.avail_out = (uInt)size;
+ if (inflateInit(&stream) != Z_OK) {
+ return ENOMEM;
+ }
+ int result = inflate(&stream, Z_FINISH);
+ inflateEnd(&stream);
+ return result == Z_STREAM_END && stream.total_out == size && !stream.avail_in ? 0 : EINVAL;
+}
+volatile int native_gpu_addon_files = -1;
+volatile int native_gpu_addon_written = 0;
+volatile int native_gpu_addon_same = 0;
+volatile int native_gpu_addon_failed = 0;
+volatile int native_gpu_addon_error = 0;
+static int
+native_gpu_addon_equal(vnode_t vp, const uint8_t *data, uint64_t size, uint32_t mode, vfs_context_t ctx)
+{
+ struct vnode_attr va;
+ VATTR_INIT(&va);
+ VATTR_WANTED(&va, va_data_size);
+ VATTR_WANTED(&va, va_mode);
+ if (vnode_getattr(vp, &va, ctx) || va.va_data_size != size || (va.va_mode & 07777) != mode) {
+ return 0;
+ }
+ const uint32_t chunk = 1024 * 1024;
+ uint8_t *buffer = kalloc_data(chunk, Z_WAITOK);
+ if (!buffer) {
+ return 0;
+ }
+ int equal = 1;
+ for (uint64_t at = 0; equal && at < size; at += chunk) {
+ int length = (int)(size - at < chunk ? size - at : chunk), resid = 0;
+ if (vn_rdwr(UIO_READ, vp, (caddr_t)buffer, length, (off_t)at, UIO_SYSSPACE, IO_NOAUTH, vfs_context_ucred(ctx), &resid,
+ vfs_context_proc(ctx)) || resid || memcmp(buffer, data + at, length)) {
+ equal = 0;
+ }
+ }
+ kfree_data(buffer, chunk);
+ return equal;
+}
+static int
+native_gpu_addon_write(const char *path, const uint8_t *data, uint64_t size, uint32_t mode, vfs_context_t ctx)
+{
+ vnode_t vp = NULLVP;
+ int error = vnode_open(path, FREAD | O_NOFOLLOW, 0, 0, &vp, ctx);
+ if (!error) {
+ int equal = native_gpu_addon_equal(vp, data, size, mode, ctx);
+ vnode_close(vp, FREAD, ctx);
+ vp = NULLVP;
+ if (equal) {
+ return -1;
+ }
+ }
+ error = vnode_open(path, O_CREAT | O_TRUNC | FWRITE | O_NOFOLLOW, mode, 0, &vp, ctx);
+ if (error) {
+ return error;
+ }
+ int resid = 0;
+ error = vn_rdwr(UIO_WRITE, vp, (caddr_t)(uintptr_t)data, (int)size, 0, UIO_SYSSPACE, IO_NOAUTH | IO_UNIT, vfs_context_ucred(ctx),
+ &resid, vfs_context_proc(ctx));
+ if (!error && resid) {
+ error = EIO;
+ }
+ if (!error) {
+ struct vnode_attr va;
+ VATTR_INIT(&va);
+ VATTR_SET(&va, va_mode, mode);
+ error = vnode_setattr(vp, &va, ctx);
+ }
+ if (!error) {
+ error = VNOP_FSYNC(vp, MNT_WAIT, ctx);
+ }
+ int close_error = vnode_close(vp, FWRITE, ctx);
+ return error ? error : close_error;
+}
+/* Creates the missing parent directories of an addon path with the mkdir syscall, /private/var/ngpu itself included (a Data seed
+ * that never had add-gpu-plugin-data.py applied has none). The scratch is one 4 KiB page; the mount arguments end below 0xa00. */
+static int
+native_gpu_addon_parents(proc_t p, user_addr_t scratch, const char *path)
+{
+ char parent[192];
+ strlcpy(parent, path, sizeof(parent));
+ for (size_t i = sizeof("/private/var/ngpu") - 1; parent[i]; ++i) {
+ if (parent[i] != '/') {
+ continue;
+ }
+ parent[i] = 0;
+ struct mkdir_args request = {.path = scratch + 0xc00, .mode = 0755};
+ int32_t result = 0;
+ int error = copyout(parent, request.path, i + 1);
+ if (!error) {
+ error = mkdir(p, &request, &result);
+ }
+ parent[i] = '/';
+ if (error && error != EEXIST) {
+ return error;
+ }
+ }
+ return 0;
+}
+static void
+native_gpu_addon_install(proc_t p, user_addr_t scratch)
+{
+ const uint8_t *addon = NULL;
+ uint64_t bytes = 0;
+ if (!native_ram_block_gpu_addon || !native_ram_block_gpu_addon(&addon, &bytes)) {
+ return;
+ }
+ uint32_t count = 0;
+ uint64_t total = 0;
+ if (bytes < 24 || memcmp(addon, "NGPUADD2", 8)) {
+ native_gpu_addon_error = EINVAL;
+ return;
+ }
+ memcpy(&count, addon + 8, 4);
+ memcpy(&total, addon + 16, 8);
+ if (total != bytes || !count || count > 4096) {
+ native_gpu_addon_error = EINVAL;
+ return;
+ }
+ /* Validate the whole addon before writing anything. */
+ uint64_t at = 24;
+ for (uint32_t i = 0; i < count; ++i) {
+ uint32_t name_bytes = 0, mode = 0;
+ uint64_t size = 0, packed = 0;
+ if (bytes - at < 24) {
+ native_gpu_addon_error = EINVAL;
+ return;
+ }
+ memcpy(&name_bytes, addon + at, 4);
+ memcpy(&mode, addon + at + 4, 4);
+ memcpy(&size, addon + at + 8, 8);
+ memcpy(&packed, addon + at + 16, 8);
+ at += 24;
+ uint64_t name_padded = (name_bytes + 7) & ~7ULL, size_padded = (packed + 7) & ~7ULL;
+ const char *name = (const char *)(addon + at);
+ if (name_bytes < 2 || name_bytes > 160 || bytes - at < name_padded || name[name_bytes - 1] || strnlen(name, name_bytes) != name_bytes - 1 ||
+ name[0] == '/' || (mode & ~0777) || size > INT32_MAX || !packed || packed > INT32_MAX || bytes - at - name_padded < size_padded) {
+ native_gpu_addon_error = EINVAL;
+ return;
+ }
+ for (uint32_t k = 0; k + 1 < name_bytes - 1; ++k) {
+ if (name[k] == '.' && name[k + 1] == '.') {
+ native_gpu_addon_error = EINVAL;
+ return;
+ }
+ }
+ at += name_padded + size_padded;
+ }
+ if (at != bytes) {
+ native_gpu_addon_error = EINVAL;
+ return;
+ }
+ native_gpu_addon_files = (int)count;
+ vfs_context_t ctx = vfs_context_current();
+ char path[192];
+ at = 24;
+ for (uint32_t i = 0; i < count; ++i) {
+ uint32_t name_bytes = 0, mode = 0;
+ uint64_t size = 0, packed = 0;
+ memcpy(&name_bytes, addon + at, 4);
+ memcpy(&mode, addon + at + 4, 4);
+ memcpy(&size, addon + at + 8, 8);
+ memcpy(&packed, addon + at + 16, 8);
+ at += 24;
+ const char *name = (const char *)(addon + at);
+ at += (name_bytes + 7) & ~7ULL;
+ snprintf(path, sizeof(path), "/private/var/ngpu/%s", name);
+ uint8_t *data = kalloc_data(size ? size : 1, Z_WAITOK);
+ int error = data ? native_gpu_addon_inflate(addon + at, packed, data, size) : ENOMEM;
+ if (!error) {
+ error = native_gpu_addon_write(path, data, size, mode, ctx);
+ if (error == ENOENT && !(error = native_gpu_addon_parents(p, scratch, path))) {
+ error = native_gpu_addon_write(path, data, size, mode, ctx);
+ }
+ }
+ if (data) {
+ kfree_data(data, size ? size : 1);
+ }
+ if (error < 0) {
+ native_gpu_addon_same++;
+ } else if (error) {
+ native_gpu_addon_failed++;
+ native_gpu_addon_error = error;
+ } else {
+ native_gpu_addon_written++;
+ }
+ at += (packed + 7) & ~7ULL;
+ }
+}
+static void
+native_gpu_addon_prepare(proc_t p, user_addr_t scratch)
+{
+ uint32_t enabled = 0;
+ if (!PE_parse_boot_argn("sheng-gpu-addon", &enabled, sizeof(enabled)) || enabled != 1) {
+ return;
+ }
+ native_gpu_addon_install(p, scratch);
+ static const char extensions_source[] = "/private/var/ngpu/addons", extensions_point[] = "/System/Library/Extensions";
+ static const char daemons_source[] = "/private/var/ngpu/daemons", daemons_point[] = "/System/Library/LaunchDaemons";
+ native_gpu_addon_mount(p, scratch, 0x400, extensions_source, sizeof(extensions_source), extensions_point,
+ sizeof(extensions_point), "/System/Library/Extensions/AGXMetalA10.bundle",
+ "/System/Library/Extensions/NativeAdrenoMetal.bundle/Info.plist", &native_gpu_addon_stage,
+ &native_gpu_addon_status);
+ native_gpu_addon_mount(p, scratch, 0x600, daemons_source, sizeof(daemons_source), daemons_point, sizeof(daemons_point),
+ "/System/Library/LaunchDaemons/com.apple.backboardd.plist",
+ "/System/Library/LaunchDaemons/org.blu-sharky.adrenoprobed.plist", &native_gpu_addon2_stage, &native_gpu_addon2_status);
+ /* The default iPadOS 17 wallpaper's layered animation with PNG images in place of its HEIC ones (no hardware HEVC decoder here).
+ * The overlay carries the animation's documents and the PNG images; the sealed assets directory stays visible below it. */
+ static const char wallpaper_source[] = "/private/var/ngpu/wallpaper",
+ wallpaper_point[] = "/Library/Wallpaper/Collections/iPadOS_17~ipad.wallpaperCollection/Wallpapers/"
+ "7570.iPadOS_17_Flat-1024w-1366h@2x~ipad.wallpaper/7570.iPadOS_17_Flat_Background-1024w-1366h@2x~ipad.ca";
+ native_gpu_addon_mount(p, scratch, 0x800, wallpaper_source, sizeof(wallpaper_source), wallpaper_point, sizeof(wallpaper_point),
+ "/Library/Wallpaper/Collections/iPadOS_17~ipad.wallpaperCollection/Wallpapers/7570.iPadOS_17_Flat-1024w-1366h@2x~ipad.wallpaper/"
+ "7570.iPadOS_17_Flat_Background-1024w-1366h@2x~ipad.ca/assets/J120.HEIC",
+ "/Library/Wallpaper/Collections/iPadOS_17~ipad.wallpaperCollection/Wallpapers/7570.iPadOS_17_Flat-1024w-1366h@2x~ipad.wallpaper/"
+ "7570.iPadOS_17_Flat_Background-1024w-1366h@2x~ipad.ca/J120.png", &native_gpu_addon3_stage, &native_gpu_addon3_status);
+}
+
static int
native_owner_data_prepare(proc_t p, user_addr_t scratch)
{
@@ -8181,6 +8496,7 @@ native_owner_data_prepare(proc_t p, user_addr_t scratch)
if (actual != device || (vfs_flags(data_mount) & MNT_RDONLY) ||
strcmp(vfs_statfs(data_mount)->f_fstypename, "apfs")) error = EINVAL;
if (!error) native_owner_data_stage = 4;
+ if (!error) native_gpu_addon_prepare(p, scratch);
done:
if (data_root) vnode_put(data_root);
native_owner_data_status = error;
diff --git a/bsd/miscfs/bindfs/bind_vfsops.c b/bsd/miscfs/bindfs/bind_vfsops.c
index 4d50604b..199a91bd 100644
--- a/bsd/miscfs/bindfs/bind_vfsops.c
+++ b/bsd/miscfs/bindfs/bind_vfsops.c
@@ -78,6 +78,8 @@
#include "bindfs.h"
+volatile int native_bindfs_boot_mount = 0;
+
#define BINDFS_ENTITLEMENT "com.apple.private.bindfs-allow"
static int
@@ -136,8 +138,9 @@ bindfs_mount(struct mount * mp, __unused vnode_t devvp, user_addr_t user_data, v
return ENOTSUP;
}
- /* check entitlement */
- if (!IOCurrentTaskHasEntitlement(BINDFS_ENTITLEMENT)) {
+ /* check entitlement; the boot-time addon mount (kern_exec.c native_gpu_addon_prepare) runs before any
+ * entitled process exists and sets this flag around its own mount() call only */
+ if (!native_bindfs_boot_mount && !IOCurrentTaskHasEntitlement(BINDFS_ENTITLEMENT)) {
return EPERM;
}
@@ -227,6 +230,9 @@ bindfs_mount(struct mount * mp, __unused vnode_t devvp, user_addr_t user_data, v
sp->f_flags = flags;
xmp->bindm_flags = BINDM_CASEINSENSITIVE; /* default to case insensitive */
+ if (native_bindfs_boot_mount) {
+ xmp->bindm_flags |= BINDM_FORCE_ROOT;
+ }
error = bindfs_vfs_getlowerattr(vnode_mount(lowerrootvp), &vfa, ctx);
if (error == 0) {
diff --git a/bsd/miscfs/bindfs/bind_vnops.c b/bsd/miscfs/bindfs/bind_vnops.c
index 86090091..0c659c78 100644
--- a/bsd/miscfs/bindfs/bind_vnops.c
+++ b/bsd/miscfs/bindfs/bind_vnops.c
@@ -105,6 +105,14 @@ bindfs_getattr(struct vnop_getattr_args * args)
vnode_put(lowervp);
if (error == 0) {
+ if (MOUNTTOBINDMOUNT(vnode_mount(args->a_vp))->bindm_flags & BINDM_FORCE_ROOT) {
+ if (VATTR_IS_ACTIVE(args->a_vap, va_uid)) {
+ VATTR_RETURN(args->a_vap, va_uid, 0);
+ }
+ if (VATTR_IS_ACTIVE(args->a_vap, va_gid)) {
+ VATTR_RETURN(args->a_vap, va_gid, 0);
+ }
+ }
if (VATTR_IS_ACTIVE(args->a_vap, va_fsid)) {
/* fix up fsid so it doesn't say the underlying fs*/
VATTR_RETURN(args->a_vap, va_fsid, vfs_statfs(vnode_mount(args->a_vp))->f_fsid.val[0]);
diff --git a/bsd/miscfs/bindfs/bindfs.h b/bsd/miscfs/bindfs/bindfs.h
index c3bebc8a..45555305 100644
--- a/bsd/miscfs/bindfs/bindfs.h
+++ b/bsd/miscfs/bindfs/bindfs.h
@@ -81,6 +81,9 @@
//#define BINDFS_DEBUG 0
#define BINDM_CACHE 0x0001
+/* Owner boot overlay (native_bindfs_boot_mount): report every file as owned by root:wheel. The backing directory is owner data made
+ * without root; launchd and the loader expect system-directory content to be root owned. Mode bits are left as they are. */
+#define BINDM_FORCE_ROOT 0x80000000ULL
#define BINDM_CASEINSENSITIVE 0x0000000000000002
typedef int (*vop_t)(void *);
@@ -0,0 +1,326 @@
diff --git a/bsd/kern/kern_exec.c b/bsd/kern/kern_exec.c
index 166272c7..6d7777b9 100644
--- a/bsd/kern/kern_exec.c
+++ b/bsd/kern/kern_exec.c
@@ -8438,6 +8438,313 @@ native_gpu_addon_prepare(proc_t p, user_addr_t scratch)
"7570.iPadOS_17_Flat_Background-1024w-1366h@2x~ipad.ca/J120.png", &native_gpu_addon3_stage, &native_gpu_addon3_status);
}
+/* The deletable system apps (Weather, Notes, Calendar, ...) ship in the OS volume's /private/var/staged_system_apps. A restore copies
+ * them into the Data volume, where SystemAppMigrator installs them during the first-boot data migration; until then only the
+ * placeholders in /System/Library/AppPlaceholders exist and the widgets of those apps stay blank. The owner Data mount hides the OS
+ * directory, so with boot-arg sheng-staged-apps=1 it is copied once, through the vnode the Data mount covers, into Data's
+ * /private/var/staged_system_apps before launchd starts. /private/var/db/.sheng-staged-apps marks a complete copy. Directories and
+ * regular files only (the 21H461 set has nothing else), owned by mobile (uid and gid 501): installcoordinationd and the migration plugin,
+ * which move and remove them, run as mobile.
+ * Counters for the boot log: stage (1 copying, 2 copied, 3 copied before, -1 no
+ * source, -2 out of memory), directories, files, MiB, failures and the last error. */
+#include <sys/dirent.h>
+volatile int native_staged_apps_stage = 0;
+volatile int native_staged_apps_dirs = 0;
+volatile int native_staged_apps_files = 0;
+volatile int native_staged_apps_mib = 0;
+volatile int native_staged_apps_failed = 0;
+volatile int native_staged_apps_error = 0;
+static uint64_t native_staged_apps_bytes;
+char native_staged_apps_path[96]; /* the file being copied, for the boot log */
+static const char native_staged_apps_marker[] = "/private/var/db/.sheng-staged-apps";
+#define NATIVE_STAGED_APPS_OWNER 501
+static void
+native_staged_apps_fail(int error)
+{
+ native_staged_apps_failed++;
+ native_staged_apps_error = error;
+}
+static int
+native_staged_apps_mkdir(proc_t p, user_addr_t scratch, const char *path)
+{
+ /* The scratch is one 4 KiB page; the mount arguments above end below 0xa00. */
+ struct mkdir_args request = {.path = scratch + 0xa00, .mode = 0755};
+ int32_t result = 0;
+ int error = copyout(path, request.path, strlen(path) + 1);
+ if (!error) {
+ error = mkdir(p, &request, &result);
+ }
+ if (error && error != EEXIST) {
+ return error;
+ }
+ vnode_t vp = NULLVP;
+ error = vnode_lookup(path, VNODE_LOOKUP_NOFOLLOW, &vp, vfs_context_current());
+ if (!error) {
+ struct vnode_attr va;
+ VATTR_INIT(&va);
+ VATTR_SET(&va, va_uid, NATIVE_STAGED_APPS_OWNER);
+ VATTR_SET(&va, va_gid, NATIVE_STAGED_APPS_OWNER);
+ error = vnode_setattr(vp, &va, vfs_context_current());
+ vnode_put(vp);
+ }
+ return error;
+}
+/* Reads to the end of the source rather than trusting a size: most of these files are compressed on the OS volume. */
+static int
+native_staged_apps_file(vnode_t source, const char *path, int mode, uint8_t *buffer, uint32_t chunk, vfs_context_t ctx)
+{
+ int error = VNOP_OPEN(source, FREAD, ctx);
+ if (error) {
+ return error;
+ }
+ vnode_t vp = NULLVP;
+ size_t length = strlen(path);
+ strlcpy(native_staged_apps_path, path + (length > sizeof(native_staged_apps_path) - 1 ? length - (sizeof(native_staged_apps_path) - 1) : 0),
+ sizeof(native_staged_apps_path));
+ error = vnode_open(path, O_CREAT | O_TRUNC | FWRITE | O_NOFOLLOW, mode, 0, &vp, ctx);
+ for (off_t at = 0; !error;) {
+ int resid = 0;
+ error = vn_rdwr(UIO_READ, source, (caddr_t)buffer, (int)chunk, at, UIO_SYSSPACE, IO_NOAUTH, vfs_context_ucred(ctx), &resid,
+ vfs_context_proc(ctx));
+ int length = (int)chunk - resid;
+ if (error || !length) {
+ break;
+ }
+ error = vn_rdwr(UIO_WRITE, vp, (caddr_t)buffer, length, at, UIO_SYSSPACE, IO_NOAUTH | IO_UNIT, vfs_context_ucred(ctx), &resid,
+ vfs_context_proc(ctx));
+ if (!error && resid) {
+ error = EIO;
+ }
+ at += length;
+ native_staged_apps_bytes += (uint64_t)length;
+ }
+ if (vp) {
+ if (!error) {
+ struct vnode_attr va;
+ VATTR_INIT(&va);
+ VATTR_SET(&va, va_mode, mode);
+ VATTR_SET(&va, va_uid, NATIVE_STAGED_APPS_OWNER);
+ VATTR_SET(&va, va_gid, NATIVE_STAGED_APPS_OWNER);
+ error = vnode_setattr(vp, &va, ctx);
+ }
+ /* Each file goes to the medium before the next: without it the copy piled up dirty pages until it stalled (int-220). */
+ if (!error) {
+ error = VNOP_FSYNC(vp, MNT_WAIT, ctx);
+ }
+ int close_error = vnode_close(vp, FWRITE, ctx);
+ if (!error) {
+ error = close_error;
+ }
+ }
+ VNOP_CLOSE(source, FREAD, ctx);
+ return error;
+}
+/* Directories still to copy, as paths relative to the staged directory: the walk is iterative, because APFS operations need much of the
+ * kernel stack and a recursion as deep as the app bundles overflowed it (int-221). */
+struct native_staged_apps_pending {
+ char *pool;
+ uint32_t *at;
+ uint32_t used, count;
+};
+#define NATIVE_STAGED_APPS_POOL (2 * 1024 * 1024)
+#define NATIVE_STAGED_APPS_DIRS 16384
+/* Copies the entries of DIR into the Data directory PATH (appending each name to PATH and cutting it off again) and queues its
+ * subdirectories, whose source-relative paths start at REL inside PATH. */
+static void
+native_staged_apps_dir(proc_t p, user_addr_t scratch, vnode_t dir, char *path, size_t path_size, size_t rel,
+ struct native_staged_apps_pending *pending, uint8_t *entries, uint32_t entries_size, uint8_t *buffer, uint32_t chunk, vfs_context_t ctx)
+{
+ size_t base = strlen(path);
+ int error = VNOP_OPEN(dir, FREAD, ctx);
+ if (error) {
+ native_staged_apps_fail(error);
+ return;
+ }
+ off_t offset = 0;
+ for (int eof = 0; !error && !eof;) {
+ uio_t uio = uio_create(1, offset, UIO_SYSSPACE, UIO_READ);
+ if (!uio) {
+ error = ENOMEM;
+ break;
+ }
+ uio_addiov(uio, CAST_USER_ADDR_T(entries), entries_size);
+ int count = 0;
+ error = vnode_readdir64(dir, uio, VNODE_READDIR_EXTENDED, &eof, &count, ctx);
+ uint32_t filled = entries_size - (uint32_t)uio_resid(uio);
+ offset = uio_offset(uio);
+ uio_free(uio);
+ if (error || !filled) {
+ break;
+ }
+ for (uint32_t at = 0; at + offsetof(struct direntry, d_name) < filled;) {
+ struct direntry *entry = (struct direntry *)(entries + at);
+ if (!entry->d_reclen || at + entry->d_reclen > filled) {
+ error = EINVAL;
+ break;
+ }
+ at += entry->d_reclen;
+ const char *name = entry->d_name;
+ if (!entry->d_ino || !strcmp(name, ".") || !strcmp(name, "..")) {
+ continue;
+ }
+ if (base + 1 + strlen(name) >= path_size) {
+ native_staged_apps_fail(ENAMETOOLONG);
+ continue;
+ }
+ path[base] = '/';
+ strlcpy(path + base + 1, name, path_size - base - 1);
+ vnode_t child = NULLVP;
+ int child_error = vnode_lookupat(name, VNODE_LOOKUP_NOFOLLOW | VNODE_LOOKUP_NOCROSSMOUNT, &child, ctx, dir);
+ if (!child_error) {
+ struct vnode_attr va;
+ VATTR_INIT(&va);
+ VATTR_WANTED(&va, va_mode);
+ child_error = vnode_getattr(child, &va, ctx);
+ int mode = VATTR_IS_SUPPORTED(&va, va_mode) ? (int)(va.va_mode & 0777) : 0644;
+ if (child_error) {
+ } else if (vnode_vtype(child) == VDIR) {
+ child_error = native_staged_apps_mkdir(p, scratch, path);
+ size_t length = strlen(path + rel) + 1;
+ if (!child_error && (pending->count == NATIVE_STAGED_APPS_DIRS || NATIVE_STAGED_APPS_POOL - pending->used < length)) {
+ child_error = ENOSPC;
+ }
+ if (!child_error) {
+ memcpy(pending->pool + pending->used, path + rel, length);
+ pending->at[pending->count++] = pending->used;
+ pending->used += (uint32_t)length;
+ native_staged_apps_dirs++;
+ }
+ } else if (vnode_vtype(child) == VREG) {
+ child_error = native_staged_apps_file(child, path, mode, buffer, chunk, ctx);
+ if (!child_error) {
+ native_staged_apps_files++;
+ native_staged_apps_mib = (int)(native_staged_apps_bytes >> 20);
+ }
+ } else {
+ child_error = ENOTSUP;
+ }
+ vnode_put(child);
+ }
+ if (child_error) {
+ native_staged_apps_fail(child_error);
+ }
+ path[base] = '\0';
+ }
+ }
+ if (error) {
+ native_staged_apps_fail(error);
+ }
+ VNOP_CLOSE(dir, FREAD, ctx);
+}
+/* Copies the tree below SOURCE into the Data directory PATH, which exists. */
+static void
+native_staged_apps_walk(proc_t p, user_addr_t scratch, vnode_t source, char *path, size_t path_size, uint8_t *buffer, uint32_t chunk,
+ vfs_context_t ctx)
+{
+ const uint32_t entries_size = 8192;
+ struct native_staged_apps_pending pending = {
+ .pool = kalloc_data(NATIVE_STAGED_APPS_POOL, Z_WAITOK),
+ .at = kalloc_data(NATIVE_STAGED_APPS_DIRS * sizeof(uint32_t), Z_WAITOK),
+ };
+ uint8_t *entries = kalloc_data(entries_size, Z_WAITOK);
+ size_t root = strlen(path);
+ if (pending.pool && pending.at && entries) {
+ native_staged_apps_dir(p, scratch, source, path, path_size, root + 1, &pending, entries, entries_size, buffer, chunk, ctx);
+ while (pending.count) {
+ const char *rel = pending.pool + pending.at[--pending.count];
+ if (root + 1 + strlen(rel) >= path_size) {
+ native_staged_apps_fail(ENAMETOOLONG);
+ continue;
+ }
+ path[root] = '/';
+ strlcpy(path + root + 1, rel, path_size - root - 1);
+ vnode_t dir = NULLVP;
+ int error = vnode_lookupat(rel, VNODE_LOOKUP_NOFOLLOW | VNODE_LOOKUP_NOCROSSMOUNT, &dir, ctx, source);
+ if (!error) {
+ native_staged_apps_dir(p, scratch, dir, path, path_size, root + 1, &pending, entries, entries_size, buffer, chunk, ctx);
+ vnode_put(dir);
+ } else {
+ native_staged_apps_fail(error);
+ }
+ path[root] = '\0';
+ }
+ } else {
+ native_staged_apps_fail(ENOMEM);
+ }
+ if (entries) {
+ kfree_data(entries, entries_size);
+ }
+ if (pending.at) {
+ kfree_data(pending.at, NATIVE_STAGED_APPS_DIRS * sizeof(uint32_t));
+ }
+ if (pending.pool) {
+ kfree_data(pending.pool, NATIVE_STAGED_APPS_POOL);
+ }
+}
+static void
+native_staged_apps_prepare(proc_t p, user_addr_t scratch, mount_t data_mount)
+{
+ uint32_t enabled = 0;
+ if (!PE_parse_boot_argn("sheng-staged-apps", &enabled, sizeof(enabled)) || enabled != 1) {
+ return;
+ }
+ vfs_context_t ctx = vfs_context_current();
+ vnode_t vp = NULLVP, covered = data_mount->mnt_vnodecovered;
+ if (!vnode_lookup(native_staged_apps_marker, VNODE_LOOKUP_NOFOLLOW, &vp, ctx)) {
+ vnode_put(vp);
+ native_staged_apps_stage = 3;
+ return;
+ }
+ int error = covered ? vnode_getwithref(covered) : ENOENT;
+ if (!error) {
+ error = vnode_lookupat("staged_system_apps", VNODE_LOOKUP_NOFOLLOW | VNODE_LOOKUP_NOCROSSMOUNT, &vp, ctx, covered);
+ vnode_put(covered);
+ }
+ if (!error && vnode_vtype(vp) != VDIR) {
+ vnode_put(vp);
+ error = ENOTDIR;
+ }
+ if (error) {
+ native_staged_apps_error = error;
+ native_staged_apps_stage = -1;
+ return;
+ }
+ const uint32_t chunk = 1024 * 1024;
+ uint8_t *buffer = kalloc_data(chunk, Z_WAITOK);
+ char *path = kalloc_data(MAXPATHLEN, Z_WAITOK);
+ if (buffer && path) {
+ native_staged_apps_stage = 1;
+ strlcpy(path, "/private/var/staged_system_apps", MAXPATHLEN);
+ error = native_staged_apps_mkdir(p, scratch, path);
+ if (error) {
+ native_staged_apps_fail(error);
+ } else {
+ native_staged_apps_walk(p, scratch, vp, path, MAXPATHLEN, buffer, chunk, ctx);
+ }
+ if (!native_staged_apps_failed) {
+ vnode_t marker = NULLVP;
+ error = vnode_open(native_staged_apps_marker, O_CREAT | O_TRUNC | FWRITE | O_NOFOLLOW, 0644, 0, &marker, ctx);
+ if (!error) {
+ error = vnode_close(marker, FWRITE, ctx);
+ }
+ if (error) {
+ native_staged_apps_fail(error);
+ } else {
+ native_staged_apps_stage = 2;
+ }
+ }
+ } else {
+ native_staged_apps_stage = -2;
+ }
+ if (buffer) {
+ kfree_data(buffer, chunk);
+ }
+ if (path) {
+ kfree_data(path, MAXPATHLEN);
+ }
+ vnode_put(vp);
+}
+
static int
native_owner_data_prepare(proc_t p, user_addr_t scratch)
{
@@ -8497,6 +8804,7 @@ native_owner_data_prepare(proc_t p, user_addr_t scratch)
strcmp(vfs_statfs(data_mount)->f_fstypename, "apfs")) error = EINVAL;
if (!error) native_owner_data_stage = 4;
if (!error) native_gpu_addon_prepare(p, scratch);
+ if (!error) native_staged_apps_prepare(p, scratch, data_mount);
done:
if (data_root) vnode_put(data_root);
native_owner_data_status = error;
@@ -0,0 +1,176 @@
diff --git a/bsd/kern/kern_exec.c b/bsd/kern/kern_exec.c
index 6d7777b9..0daa1a2e 100644
--- a/bsd/kern/kern_exec.c
+++ b/bsd/kern/kern_exec.c
@@ -8409,6 +8409,161 @@ native_gpu_addon_install(proc_t p, user_addr_t scratch)
at += (packed + 7) & ~7ULL;
}
}
+/* The OS cryptex where iPadOS looks for it. The root volume's entries for cryptex content are links into /System/Cryptexes/OS, which
+ * leads to /private/preboot/Cryptexes/OS; WebKit's processes are ExtensionKit extensions reached that way
+ * (/System/Library/ExtensionKit/Extensions/WebContentExtension.appex and its Networking and GPU siblings), so with the path missing
+ * Safari could not start them and its pages never loaded. The SystemOS volume is already the upper layer of the root union: its
+ * /System and /usr are bound read-only over /private/preboot/Cryptexes/OS/{System,usr}, not as unions, so a lookup there stays on the
+ * SystemOS volume and never meets the root's links again. The two mount points are owner Data directories under
+ * /private/var/ngpu/preboot, shown over the root's empty /private/preboot as a read-only union. Never fatal; stage and status go to the
+ * boot log: 1 mount points made, 2 preboot shown, 3 System bound, 4 usr bound, 5 an extension of WebKit visible through the path. */
+volatile int native_cryptex_os_stage = 0;
+volatile int native_cryptex_os_status = -1;
+static int
+native_cryptex_os_bind(proc_t p, user_addr_t scratch, const char *source, const char *point, int flags)
+{
+ struct mount_args request = {.type = scratch + 0xa00, .path = scratch + 0xa20, .flags = MNT_RDONLY | flags,
+ .data = scratch + 0xb00};
+ int32_t result = 0;
+ int error;
+ if ((error = copyout("bindfs", request.type, 7)) || (error = copyout(point, request.path, strlen(point) + 1)) ||
+ (error = copyout(source, request.data, strlen(source) + 1))) {
+ return error;
+ }
+ native_bindfs_boot_mount = 1;
+ error = mount(p, &request, &result);
+ native_bindfs_boot_mount = 0;
+ return error;
+}
+static void
+native_cryptex_os_view(proc_t p, user_addr_t scratch)
+{
+ static const char *const points[] = {"/private/var/ngpu/preboot/Cryptexes/OS/System/", "/private/var/ngpu/preboot/Cryptexes/OS/usr/"};
+ vnode_t probe = NULLVP;
+ int error = 0;
+ for (size_t i = 0; i < sizeof(points) / sizeof(points[0]) && !error; ++i) {
+ error = native_gpu_addon_parents(p, scratch, points[i]);
+ }
+ if (error) {
+ native_cryptex_os_status = error;
+ return;
+ }
+ native_cryptex_os_stage = 1;
+ if ((error = native_cryptex_os_bind(p, scratch, "/private/var/ngpu/preboot", "/private/preboot", MNT_UNION))) {
+ native_cryptex_os_status = error;
+ return;
+ }
+ native_cryptex_os_stage = 2;
+ if ((error = native_cryptex_os_bind(p, scratch, "/System", "/private/preboot/Cryptexes/OS/System", 0))) {
+ native_cryptex_os_status = error;
+ return;
+ }
+ native_cryptex_os_stage = 3;
+ if ((error = native_cryptex_os_bind(p, scratch, "/usr", "/private/preboot/Cryptexes/OS/usr", 0))) {
+ native_cryptex_os_status = error;
+ return;
+ }
+ native_cryptex_os_stage = 4;
+ error = vnode_lookup("/System/Cryptexes/OS/System/Library/ExtensionKit/Extensions/WebContentExtension.appex/Info.plist", 0, &probe,
+ vfs_context_current());
+ if (!error) {
+ vnode_put(probe);
+ native_cryptex_os_stage = 5;
+ }
+ native_cryptex_os_status = error;
+}
+/* The device answers MobileGestalt cached while the display gamut was P3: removed so they are worked out again from the device tree
+ * (now sRGB). 0 removed, ENOENT none there (already regenerated or never made). */
+volatile int native_gestalt_cache_status = -1;
+static void
+native_gestalt_cache_drop(proc_t p, user_addr_t scratch)
+{
+ static const char cache[] = "/private/var/containers/Shared/SystemGroup/systemgroup.com.apple.mobilegestaltcache/Library/Caches/"
+ "com.apple.MobileGestalt.plist";
+ struct unlink_args request = {.path = scratch + 0xc00};
+ int32_t result = 0;
+ int error = copyout(cache, request.path, sizeof(cache));
+ if (!error) {
+ error = unlink(p, &request, &result);
+ }
+ native_gestalt_cache_status = error;
+}
+/* LaunchServices' database of this Data store was built while /System/Cryptexes/OS led nowhere, so it never registered what lies behind
+ * it (WebKit's extensions among them) and lsd does not register system content again on a later boot of the same OS build. Its files
+ * are removed once, so lsd builds the database again with the path in place; the marker file keeps later boots from repeating it.
+ * Status for the boot log: files removed, or -2 done before, or -errno. */
+#include <sys/dirent.h>
+volatile int native_ls_store_status = -1;
+static void
+native_ls_store_drop(proc_t p, user_addr_t scratch)
+{
+ static const char marker[] = "/private/var/ngpu/ls-store-rebuilt-1";
+ static const char caches[] = "/private/var/mobile/Containers/Data/InternalDaemon/5C583048-0C53-456F-B110-BB12068B787F/Library/Caches";
+ static const char prefix[] = "com.apple.LaunchServices-";
+ vfs_context_t ctx = vfs_context_current();
+ vnode_t vp = NULLVP;
+ if (!vnode_lookup(marker, 0, &vp, ctx)) {
+ vnode_put(vp);
+ native_ls_store_status = -2;
+ return;
+ }
+ int error = vnode_lookup(caches, 0, &vp, ctx);
+ if (error) {
+ native_ls_store_status = -error;
+ return;
+ }
+ char names[8][64];
+ unsigned found = 0;
+ char *buffer = kalloc_data(4096, Z_WAITOK);
+ uio_t uio = buffer ? uio_create(1, 0, UIO_SYSSPACE, UIO_READ) : NULL;
+ int eof = 0;
+ while (uio && !eof && found < 8) {
+ uio_reset(uio, uio_offset(uio), UIO_SYSSPACE, UIO_READ);
+ uio_addiov(uio, CAST_USER_ADDR_T(buffer), 4096);
+ int entries = 0;
+ if ((error = VNOP_READDIR(vp, uio, 0, &eof, &entries, ctx))) {
+ break;
+ }
+ const size_t used = 4096 - (size_t)uio_resid(uio);
+ if (!used) {
+ break;
+ }
+ for (size_t at = 0; at + sizeof(struct dirent) - sizeof(((struct dirent *)0)->d_name) <= used && found < 8;) {
+ const struct dirent *entry = (const struct dirent *)(buffer + at);
+ if (!entry->d_reclen) {
+ break;
+ }
+ if (entry->d_namlen < sizeof(names[0]) && !strncmp(entry->d_name, prefix, sizeof(prefix) - 1)) {
+ memcpy(names[found], entry->d_name, entry->d_namlen);
+ names[found++][entry->d_namlen] = 0;
+ }
+ at += entry->d_reclen;
+ }
+ }
+ if (uio) {
+ uio_free(uio);
+ }
+ if (buffer) {
+ kfree_data(buffer, 4096);
+ }
+ vnode_put(vp);
+ if (error) {
+ native_ls_store_status = -error;
+ return;
+ }
+ int removed = 0;
+ char path[192];
+ for (unsigned i = 0; i < found; ++i) {
+ snprintf(path, sizeof(path), "%s/%s", caches, names[i]);
+ struct unlink_args request = {.path = scratch + 0xc00};
+ int32_t result = 0;
+ if (!copyout(path, request.path, strlen(path) + 1) && !unlink(p, &request, &result)) {
+ removed++;
+ }
+ }
+ (void)native_gpu_addon_write(marker, (const uint8_t *)"1\n", 2, 0644, ctx);
+ native_ls_store_status = removed;
+}
static void
native_gpu_addon_prepare(proc_t p, user_addr_t scratch)
{
@@ -8436,6 +8591,9 @@ native_gpu_addon_prepare(proc_t p, user_addr_t scratch)
"7570.iPadOS_17_Flat_Background-1024w-1366h@2x~ipad.ca/assets/J120.HEIC",
"/Library/Wallpaper/Collections/iPadOS_17~ipad.wallpaperCollection/Wallpapers/7570.iPadOS_17_Flat-1024w-1366h@2x~ipad.wallpaper/"
"7570.iPadOS_17_Flat_Background-1024w-1366h@2x~ipad.ca/J120.png", &native_gpu_addon3_stage, &native_gpu_addon3_status);
+ native_cryptex_os_view(p, scratch);
+ native_gestalt_cache_drop(p, scratch);
+ native_ls_store_drop(p, scratch);
}
/* The deletable system apps (Weather, Notes, Calendar, ...) ship in the OS volume's /private/var/staged_system_apps. A restore copies
@@ -0,0 +1,31 @@
diff --git a/security/mac_process.c b/security/mac_process.c
index 51bcf33e..ae7c70bd 100644
--- a/security/mac_process.c
+++ b/security/mac_process.c
@@ -77,6 +77,7 @@
#include <mach/mach_types.h>
#include <kern/task.h>
#include <kern/zalloc.h>
+#include <pexpert/pexpert.h>
#include <os/hash.h>
@@ -909,9 +910,18 @@ mac_proc_check_settid(proc_t curp, uid_t uid, gid_t gid)
return error;
}
+// Board bring-up: a system service the original AMFI gates on a platform binary is refused with "Launch Constraint
+// Violation" when the binary is an App cryptex daemon or an owner-trusted one, although its signature and trust-cache
+// entry are valid: the constraint category of an owner or cryptex binary is not the platform one. The original release
+// AMFI ignores its own amfi_* boot arguments, so the check is cleared here when the board asks for it. An option of the
+// instrumented board only: the boot argument, not the default, decides.
int
mac_proc_check_launch_constraints(proc_t curp, struct image_params *imgp, os_reason_t *reasonp)
{
+ uint32_t skip = 0;
+ if (PE_parse_boot_argn("sheng-no-lc", &skip, sizeof(skip)) && skip != 0) {
+ return 0;
+ }
char *fatal_failure_desc = NULL;
size_t fatal_failure_desc_len = 0;
+380
View File
@@ -0,0 +1,380 @@
diff --git a/bsd/kern/bsd_init.c b/bsd/kern/bsd_init.c
index 7febaca6..5bc65a1e 100644
--- a/bsd/kern/bsd_init.c
+++ b/bsd/kern/bsd_init.c
@@ -1,3 +1,4 @@
+#include <kern/boot_progress.h>
/*
* Copyright (c) 2000-2021 Apple Inc. All rights reserved.
*
@@ -569,6 +570,7 @@ native_mobile_graphics_load_probe(void)
void
bsd_init(void)
{
+ boot_progress_stage("bsd_init enter");
struct uthread *ut;
vnode_t init_rootvnode = NULLVP;
struct proc_ro_data kernproc_ro_data = {
@@ -588,7 +590,7 @@ bsd_init(void)
#if DEBUG_BSDINIT
#define bsd_init_kprintf(x, ...) kprintf("bsd_init: " x, ## __VA_ARGS__)
#else
-#define bsd_init_kprintf(x, ...)
+#define bsd_init_kprintf(x, ...) boot_progress_stage("bsd_init: " x)
#endif
throttle_init();
@@ -778,7 +780,9 @@ bsd_init(void)
#if defined(XNU_SHENG_PMGR) && XNU_SHENG_PMGR
extern void sheng_pmgr_run(void);
+ boot_progress_stage("original PMGR nub start");
sheng_pmgr_run();
+ boot_progress_stage("original PMGR nub returned");
#endif
/*
@@ -1017,7 +1021,9 @@ bsd_init(void)
#endif
bsd_init_kprintf("vfs_mountroot\n");
+ boot_progress_stage("vfs_mountroot enter");
if (0 == (err = vfs_mountroot())) {
+ boot_progress_stage("vfs_mountroot mounted; authentication pending");
break;
}
rootdevice[0] = '\0';
@@ -1104,6 +1110,7 @@ bsd_init(void)
extern void native_owner_root_accepted_checkpoint(int, int);
native_owner_root_accepted = owner_authorized;
native_owner_root_accepted_checkpoint(autherr, owner_authorized);
+ boot_progress_stage("root authentication decision recorded");
}
#endif
}
diff --git a/bsd/kern/kern_exec.c b/bsd/kern/kern_exec.c
index 0daa1a2e..b823dc0d 100644
--- a/bsd/kern/kern_exec.c
+++ b/bsd/kern/kern_exec.c
@@ -1,3 +1,4 @@
+#include <kern/boot_progress.h>
/*
* Copyright (c) 2000-2020 Apple Inc. All rights reserved.
*
@@ -2075,7 +2076,9 @@ grade:
/*
* Actually load the image file we previously decided to load.
*/
+ boot_progress_process_stage(proc_getpid(p),p->p_name,"exec load_machfile enter");
lret = load_machfile(imgp, mach_header, thread, &map, &load_result);
+ boot_progress_process_stage(proc_getpid(p),p->p_name,"exec load_machfile returned");
if (lret != LOAD_SUCCESS) {
error = load_return_to_errno(lret);
@@ -9098,6 +9101,7 @@ static const char * init_programs[] = {
void
load_init_program(proc_t p)
{
+ boot_progress_stage("load_init_program enter");
uint32_t i;
int error;
vm_map_t map = current_map();
@@ -9113,12 +9117,23 @@ load_init_program(proc_t p)
VM_MAP_KERNEL_FLAGS_ANYWHERE());
user_addr_t init_envp = USER_ADDR_NULL;
#if defined(XNU_OWNER_CACHE_MOUNT) && XNU_OWNER_CACHE_MOUNT
+ boot_progress_stage("SystemOS mount enter");
int cache_error = native_owner_cache_prepare(p, (user_addr_t)scratch_addr, &init_envp);
+ boot_progress_stage("SystemOS mount returned");
if (cache_error) panic("offline owner SystemOS mount failed: %d", cache_error);
#endif
#if defined(XNU_OWNER_DATA_MOUNT) && XNU_OWNER_DATA_MOUNT
+ boot_progress_stage("Data mount enter");
int data_error = native_owner_data_prepare(p, (user_addr_t)scratch_addr);
+ boot_progress_stage("Data mount returned");
if (data_error) panic("offline scratch Data mount failed: %d", data_error);
+#if defined(XNU_BOOT_PROGRESS) && XNU_BOOT_PROGRESS
+ extern int sheng_boot_watchdog_policy(void *, uint64_t);
+ boot_progress_stage("temporary watchdog launch policy enter");
+ int policy_error=sheng_boot_watchdog_policy(p, (uint64_t)scratch_addr);
+ if (policy_error) panic("sheng watchdog launch policy failed: %d", policy_error);
+ boot_progress_stage("temporary watchdog launch policy ready");
+#endif
#endif
#if CONFIG_MEMORYSTATUS
(void) memorystatus_init_at_boot_snapshot();
@@ -9647,6 +9662,7 @@ process_signature(proc_t p, struct image_params *imgp)
* approve of exec, kill and return immediately.
*/
if (imgp->ip_mac_return != 0) {
+ boot_progress_process_stage(proc_getpid(p),p->p_name,"exec MAC policy REFUSED");
KERNEL_DEBUG_CONSTANT(BSDDBG_CODE(DBG_BSD_PROC, BSD_PROC_EXITREASON_CREATE) | DBG_FUNC_NONE,
proc_getpid(p), OS_REASON_EXEC, EXEC_EXIT_REASON_SECURITY_POLICY, 0, 0);
signature_failure_reason = os_reason_create(OS_REASON_EXEC, EXEC_EXIT_REASON_SECURITY_POLICY);
@@ -9823,7 +9839,9 @@ done:
* state.
*/
+ boot_progress_process_stage(proc_getpid(p),p->p_name,"exec process signature enter");
error = proc_process_signature(p, &signature_failure_reason);
+ boot_progress_process_stage(proc_getpid(p),p->p_name,error ? "exec process signature REFUSED" : "exec process signature accepted");
}
if (0 == error) {
diff --git a/bsd/kern/kern_exit.c b/bsd/kern/kern_exit.c
index b5633e45..b1ac3ad3 100644
--- a/bsd/kern/kern_exit.c
+++ b/bsd/kern/kern_exit.c
@@ -1,3 +1,4 @@
+#include <kern/boot_progress.h>
/*
* Copyright (c) 2000-2016 Apple Inc. All rights reserved.
*
@@ -1989,7 +1990,11 @@ proc_handle_critical_exit(proc_t p, int rv)
}
const char *prefix_str;
+#if defined(XNU_BOOT_PROGRESS) && XNU_BOOT_PROGRESS
+ char prefix_str_buf[1024];
+#else
char prefix_str_buf[128];
+#endif
if (p == initproc) {
if (strnstr(p->p_name, "preinit", sizeof(p->p_name))) {
@@ -2002,6 +2007,7 @@ proc_handle_critical_exit(proc_t p, int rv)
} else {
/* For processes that aren't launchd, just use the process name and pid */
snprintf(prefix_str_buf, sizeof(prefix_str_buf), "%s[%d] exited", p->p_name, proc_getpid(p));
+ boot_progress_critical_exit(proc_getpid(p), prefix_str_buf, sizeof(prefix_str_buf));
prefix_str = prefix_str_buf;
}
@@ -2037,6 +2043,10 @@ proc_prepareexit(proc_t p, int rv, boolean_t perf_notify)
task_t task = proc_task(p);
+ boot_progress_exit(proc_getpid(p),p->p_name,rv,
+ p->p_exit_reason ? p->p_exit_reason->osr_namespace : 0,
+ p->p_exit_reason ? p->p_exit_reason->osr_code : 0);
+
if (p->p_crash_behavior != 0 || p == initproc) {
proc_handle_critical_exit(p, rv);
}
diff --git a/osfmk/arm/arm_init.c b/osfmk/arm/arm_init.c
index d1324571..72d35614 100644
--- a/osfmk/arm/arm_init.c
+++ b/osfmk/arm/arm_init.c
@@ -1,3 +1,4 @@
+#include <kern/boot_progress.h>
/*
* Copyright (c) 2007-2024 Apple Inc. All rights reserved.
*
@@ -376,6 +377,7 @@ arm_init(
#endif /* defined(HAS_APPLE_PAC) */
PE_init_platform(FALSE, args); /* Get platform expert set up */
+ boot_progress_init();
#if __arm64__
configure_timer_apple_regs();
@@ -579,7 +581,9 @@ arm_init(
gDramSize = *dram_size;
pmap_first_pnum = (ppnum_t)atop(gDramBase);
+ boot_progress_stage("arm_vm_init enter");
arm_vm_init(xmaxmem, args);
+ boot_progress_stage("arm_vm_init returned");
if (debug_boot_arg) {
patch_low_glo();
diff --git a/osfmk/arm64/sleh.c b/osfmk/arm64/sleh.c
index 0554aa80..8bf3a043 100644
--- a/osfmk/arm64/sleh.c
+++ b/osfmk/arm64/sleh.c
@@ -1,3 +1,4 @@
+#include <kern/boot_progress.h>
/*
* Copyright (c) 2012-2023 Apple Inc. All rights reserved.
*
@@ -1269,6 +1270,7 @@ handle_uncategorized(arm_saved_state_t *state)
__builtin_unreachable();
}
+ boot_progress_user_breakpoint(state, esr);
#if __has_feature(ptrauth_calls)
static inline const char *
ptrauth_key_to_string(ptrauth_key key)
@@ -2079,6 +2081,7 @@ handle_user_abort(arm_saved_state_t *state, uint64_t esr, vm_offset_t fault_addr
}
#endif /* __has_feature(ptrauth_calls) */
+ boot_progress_user_abort(state, esr, fault_addr);
const self_restrict_mode_t self_restrict_mode = user_fault_in_self_restrict_mode(thread);
if ((self_restrict_mode != SELF_RESTRICT_NONE) &&
task_is_jit_exception_fatal(get_threadtask(thread))) {
@@ -3009,6 +3012,7 @@ sleh_irq(arm_saved_state_t *state)
sleh_interrupt_handler_prologue(state, DBG_INTR_TYPE_OTHER);
+ boot_progress_irq(get_saved_state_pc(state), get_saved_state_cpsr(state), get_saved_state_sp(state));
#if USE_APPLEARMSMP
PE_handle_ext_interrupt();
diff --git a/osfmk/console/serial_console.c b/osfmk/console/serial_console.c
index 7784e725..3a49734c 100644
--- a/osfmk/console/serial_console.c
+++ b/osfmk/console/serial_console.c
@@ -1,3 +1,4 @@
+#include <kern/boot_progress.h>
/*
* Copyright (c) 2000-2020 Apple, Inc. All rights reserved.
*
@@ -251,6 +252,7 @@ get_cons_ops_index(void)
static inline void
_cnputc(char c, bool poll)
{
+ if (boot_progress_active()) { boot_progress_putc(c); return; }
bool in_debugger = (kernel_debugger_entry_count > 0);
const uint32_t idx = get_cons_ops_index();
diff --git a/osfmk/console/video_console.c b/osfmk/console/video_console.c
index 648cb704..15b59643 100644
--- a/osfmk/console/video_console.c
+++ b/osfmk/console/video_console.c
@@ -1,3 +1,4 @@
+#include <kern/boot_progress.h>
/*
* Copyright (c) 2000-2020 Apple Inc. All rights reserved.
*
@@ -1230,6 +1231,7 @@ gc_update_color(int color, boolean_t fore)
void
vcputc_options(char c, __unused bool poll)
{
+ if (boot_progress_active()) { boot_progress_putc(c); return; }
if (gc_initialized && gc_enabled) {
VCPUTC_LOCK_LOCK();
if (gc_enabled) {
@@ -2896,6 +2898,9 @@ initialize_screen(PE_Video * boot_vinfo, unsigned int op)
vinfo = new_vinfo;
}
+ boot_progress_bind(vinfo.v_baseaddr, vinfo.v_width, vinfo.v_height,
+ vinfo.v_rowbytes, vinfo.v_depth);
+
// If we changed the virtual address, remove the old mapping
if (newVideoVirt != 0) {
if (lastVideoVirt && lastVideoMapSize) { /* Was the framebuffer mapped before? */
@@ -3048,6 +3053,10 @@ initialize_screen(PE_Video * boot_vinfo, unsigned int op)
}
#endif
}
+
+ /* Console initialization/acquisition may clear the existing mapping. */
+ boot_progress_bind(vinfo.v_baseaddr, vinfo.v_width, vinfo.v_height,
+ vinfo.v_rowbytes, vinfo.v_depth);
}
void vcattach(void); /* XXX gcc 4 warning cleanup */
diff --git a/osfmk/kern/Makefile b/osfmk/kern/Makefile
index 1c856dd0..f295db69 100644
--- a/osfmk/kern/Makefile
+++ b/osfmk/kern/Makefile
@@ -138,6 +138,7 @@ PRIVATE_EXPORT_FILES = \
XNU_ONLY_EXPORTS = \
+ boot_progress.h \
arcade.h \
btlog.h \
cpc.h \
diff --git a/osfmk/kern/boot_progress.h b/osfmk/kern/boot_progress.h
new file mode 100644
index 00000000..92f70b4a
--- /dev/null
+++ b/osfmk/kern/boot_progress.h
@@ -0,0 +1,34 @@
+// SPDX-License-Identifier: MIT
+#ifndef XNU_BOOT_PROGRESS_H
+#define XNU_BOOT_PROGRESS_H
+#include <stdint.h>
+#if defined(XNU_BOOT_PROGRESS) && XNU_BOOT_PROGRESS
+void boot_progress_init(void);
+void boot_progress_start(void);
+int boot_progress_active(void);
+void boot_progress_bind(uintptr_t, unsigned, unsigned, unsigned, unsigned);
+void boot_progress_putc(char);
+void boot_progress_stage(const char *);
+void boot_progress_irq(uint64_t, uint64_t, uint64_t);
+void boot_progress_poll(void);
+void boot_progress_user_breakpoint(void *, unsigned);
+void boot_progress_user_abort(void *, unsigned, uint64_t);
+void boot_progress_critical_exit(int, char *, unsigned long);
+void boot_progress_process_stage(int, const char *, const char *);
+void boot_progress_exit(int, const char *, int, unsigned, uint64_t);
+#else
+#define boot_progress_init() ((void)0)
+#define boot_progress_start() ((void)0)
+#define boot_progress_active() 0
+#define boot_progress_bind(...) ((void)0)
+static inline void boot_progress_putc(char c) { (void)c; }
+#define boot_progress_stage(s) ((void)0)
+#define boot_progress_irq(...) ((void)0)
+#define boot_progress_poll() ((void)0)
+#define boot_progress_user_breakpoint(...) ((void)0)
+#define boot_progress_user_abort(...) ((void)0)
+#define boot_progress_critical_exit(...) ((void)0)
+#define boot_progress_process_stage(...) ((void)0)
+#define boot_progress_exit(...) ((void)0)
+#endif
+#endif
diff --git a/osfmk/kern/startup.c b/osfmk/kern/startup.c
index c977c983..0125a33c 100644
--- a/osfmk/kern/startup.c
+++ b/osfmk/kern/startup.c
@@ -1,3 +1,4 @@
+#include <kern/boot_progress.h>
/*
* Copyright (c) 2000-2020 Apple Inc. All rights reserved.
*
@@ -222,6 +223,7 @@ TUNABLE(int, serverperfmode, "serverperfmode", 0);
static inline void
kernel_bootstrap_log(const char *message)
{
+ boot_progress_stage(message);
if ((startup_debug & STARTUP_DEBUG_VERBOSE) &&
startup_phase >= STARTUP_SUB_KPRINTF) {
kprintf("kernel_bootstrap: %s\n", message);
@@ -232,6 +234,7 @@ kernel_bootstrap_log(const char *message)
static inline void
kernel_bootstrap_thread_log(const char *message)
{
+ boot_progress_stage(message);
if ((startup_debug & STARTUP_DEBUG_VERBOSE) &&
startup_phase >= STARTUP_SUB_KPRINTF) {
kprintf("kernel_bootstrap_thread: %s\n", message);
@@ -754,6 +757,7 @@ kernel_bootstrap_thread(void)
*/
kernel_bootstrap_thread_log("clock_service_create");
clock_service_create();
+ boot_progress_start();
/*
* Create the device service.
diff --git a/pexpert/arm/pe_kprintf.c b/pexpert/arm/pe_kprintf.c
index 7a4bbd39..fea9411c 100644
--- a/pexpert/arm/pe_kprintf.c
+++ b/pexpert/arm/pe_kprintf.c
@@ -1,3 +1,4 @@
+#include <kern/boot_progress.h>
/*
* Copyright (c) 2000-2019 Apple Inc. All rights reserved.
*/
@@ -61,6 +62,7 @@ __startup_func
static void
PE_init_kprintf(void)
{
+ if (boot_progress_active()) { PE_kputc = boot_progress_putc; return; }
if (serial_init()) {
PE_kputc = serial_putc_crlf;
} else {
+28
View File
@@ -28,3 +28,31 @@
../../../../../patches/xnu-12377/source-corecrypto14-bootstrap.patch
../../../../../patches/xnu-12377/source-pthread-bootstrap.patch
../../../../../patches/xnu-12377/native-arm-pan.patch
../../../../../patches/xnu-12377/mockfs-intermediate-lookup.patch
../../../../../patches/xnu-12377/mockfs-memory-device-width.patch
../../../../../patches/xnu-12377/mockfs-statfs.patch
../../../../../patches/xnu-12377/mockfs-test-file.patch
../../../../../patches/xnu-12377/mockfs-typed-allocation.patch
../../../../../patches/xnu-12377/native-lookup-trace.patch
../../../../../patches/xnu-12377/native-union-getattrlistbulk.patch
../../../../../patches/xnu-12377/native-union-lower-directory.patch
../../../../../patches/xnu-12377/native-work-interval-trace.patch
../../../../../patches/xnu-12377/offline-component-load-probes.patch
../../../../../patches/xnu-12377/offline-legacy-start.patch
../../../../../patches/xnu-12377/offline-native-gpu-broker.patch
../../../../../patches/xnu-12377/offline-owner-boot-manifest.patch
../../../../../patches/xnu-12377/offline-owner-cache-carrier.patch
../../../../../patches/xnu-12377/offline-owner-root-binding-audit.patch
../../../../../patches/xnu-12377/offline-owner-root-crypto-probe.patch
../../../../../patches/xnu-12377/offline-owner-z-air-init.patch
../../../../../patches/xnu-12377/zzw-native-adreno-sandbox-grant.patch
../../../../../patches/xnu-12377/zzwb-native-sandbox-grant-set-properties.patch
../../../../../patches/xnu-12377/zzyc-owner-skip-launch-constraints.patch
../../../../../patches/xnu-12377/native-4k-user-abi.patch
../../../../../patches/xnu-12377/native-4k-signature-hash-cache.patch
../../../../../patches/xnu-12377/native-text-fixup.patch
../../../../../patches/xnu-12377/non-apple-iommu-bootstrap.patch
../../../../../patches/xnu-12377/offline-owner-systemos-mount.patch
../../../../../patches/xnu-12377/zzy-native-gpu-addon-mount.patch
../../../../../patches/xnu-12377/zzy-staged-system-apps.patch
../../../../../patches/xnu-12377/zzyb-native-cryptex-os-view.patch
@@ -1 +1,2 @@
# OS patch paths for xnu-12377.121.6, one per line in application order.
arch:../../../patches/xnu-12377/zzz-boot-progress.patch
+3 -1
View File
@@ -15,6 +15,8 @@ OUT = ROOT / 'build/adreno740-accelerator'
OUT.mkdir(exist_ok=True)
sys.path.insert(0, str(ROOT / 'kits/XnuPrelink'))
from native_prelink import macho
sys.path.insert(0, str(Path(__file__).resolve().parent))
from kernel_target import TRIPLE
def run(cmd, **kw):
@@ -79,7 +81,7 @@ if extra or not undefined & set(bindings):
asm = OUT / 'original-bindings.S'
asm.write_text(''.join(f'.globl {n}\n.set {n}, {a:#x}\n' for n, a in sorted(bindings.items()) if n in undefined))
sdk = run(['xcrun', '--sdk', 'iphoneos', '--show-sdk-path'], text=True).strip()
run(['xcrun', 'clang', '-target', 'arm64-apple-ios17.0', '-isysroot', sdk, '-c', asm, '-o', OUT / 'original-bindings.o'])
run(['xcrun', 'clang', '-target', TRIPLE, '-isysroot', sdk, '-c', asm, '-o', OUT / 'original-bindings.o'])
llvm = Path(run(['brew', '--prefix', 'llvm'], text=True).strip()) / 'bin'
late = OUT / 'NativeAdrenoAccelerator.late.o'
+3 -13
View File
@@ -2,9 +2,10 @@
"""Compile XNU's minimal executable-root filesystem for offline tests."""
import json
from pathlib import Path
import struct
import subprocess
from kernel_target import check_object
ROOT = Path(__file__).resolve().parents[1]
recipe = ROOT / 'build/xnu/ios-obj/DEVELOPMENT_ARM64_BCM2837/bsd/DEVELOPMENT/vfs_conf.o.json'
record = json.loads(recipe.read_text().strip().rstrip(','))
@@ -34,18 +35,7 @@ for name in ('mockfs_fsnode', 'mockfs_vfsops', 'mockfs_vnops'):
(out / (name + '.log')).write_text(result.stdout + result.stderr)
if result.returncode:
raise SystemExit(result.stderr[-4500:])
data = destination.read_bytes()
if struct.unpack_from('<4I', data) != (0xfeedfacf, 0x100000c, 0, 1):
raise SystemExit('Mockfs requires ordinary ARM64 output')
offset = 32
deployment = None
for _ in range(struct.unpack_from('<I', data, 16)[0]):
cmd, length = struct.unpack_from('<II', data, offset)
if cmd == 0x32:
deployment = struct.unpack_from('<II', data, offset + 8)
offset += length
if deployment != (2, 0x110000):
raise SystemExit(f'Unexpected mockfs deployment: {deployment}')
check_object(destination.read_bytes(), destination.name)
objects.append(destination)
archive = out / 'libmockfs_fixture.a'
archive.unlink(missing_ok=True)
+3 -1
View File
@@ -5,6 +5,8 @@ from pathlib import Path
ROOT=Path(__file__).resolve().parents[1];OUT=ROOT/'build/sheng-hid-input';OUT.mkdir(exist_ok=True)
sys.path.insert(0,str(ROOT/'kits/XnuPrelink'))
from native_prelink import macho,backed_offset,exports
sys.path.insert(0, str(Path(__file__).resolve().parent))
from kernel_target import TRIPLE
def run(cmd,**kw):return subprocess.check_output([str(x) for x in cmd],timeout=90,**kw)
def require(value,why):
@@ -102,7 +104,7 @@ undefined={n for n,_,k in symbols if k==1 and '11IOHIDDevice' in n}
require(undefined==set(bindings),'All and only actual original HID imports bound')
asm=OUT/'original-bindings.S';asm.write_text(''.join(f'.globl {n}\n.set {n}, {a:#x}\n' for n,a in sorted(bindings.items())))
sdk=run(['xcrun','--sdk','iphoneos','--show-sdk-path'],text=True).strip()
run(['xcrun','clang','-target','arm64-apple-ios17.0','-isysroot',sdk,'-c',asm,'-o',OUT/'hid-original-bindings.o'])
run(['xcrun','clang','-target',TRIPLE,'-isysroot',sdk,'-c',asm,'-o',OUT/'hid-original-bindings.o'])
llvm=Path(run(['brew','--prefix','llvm'],text=True).strip())/'bin';late=OUT/'NativeHidInput.late.o'
run([llvm/'llvm-objcopy','--remove-section=__DATA,__mod_init_func','--globalize-symbol=__GLOBAL__sub_I_NativeHidInput.cpp',obj,late])
def code(data,image):
+4 -2
View File
@@ -11,6 +11,8 @@ import sys
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT / 'kits/XnuPrelink'))
import native_prelink as np
sys.path.insert(0, str(Path(__file__).resolve().parent))
from kernel_target import TRIPLE
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--objects-dir', type=Path, default=ROOT / 'build/software-aes')
@@ -22,7 +24,7 @@ if not audit_path.is_file() and recorded.is_file():
# Without the qualification evidence of the original kernelcache (kernel candidate, KDK annotations, live metadata),
# bind with the recorded result of that audit. The addresses belong to the kernelcache pinned in board.toml.
out = args.objects_dir
subprocess.run(['xcrun', 'clang', '-target', 'arm64-apple-ios17.0', '-Werror', '-Wno-incompatible-sysroot', '-c', recorded,
subprocess.run(['xcrun', 'clang', '-target', TRIPLE, '-Werror', '-Wno-incompatible-sysroot', '-c', recorded,
'-o', out / 'original-bindings.o'], check=True)
archive = out / 'libSoftwareAES.a'
archive.unlink(missing_ok=True)
@@ -124,7 +126,7 @@ out = args.objects_dir
out.mkdir(parents=True, exist_ok=True)
source = out / 'original-bindings.S'
source.write_text(''.join(f'.globl {name}\n.set {name}, {address:#x}\n' for name, address in sorted(bindings.items())))
subprocess.run(['xcrun', 'clang', '-target', 'arm64-apple-ios17.0', '-Werror', '-Wno-incompatible-sysroot', '-c', source,
subprocess.run(['xcrun', 'clang', '-target', TRIPLE, '-Werror', '-Wno-incompatible-sysroot', '-c', source,
'-o', out / 'original-bindings.o'], check=True)
archive = out / 'libSoftwareAES.a'
archive.unlink(missing_ok=True)
+3 -1
View File
@@ -12,6 +12,8 @@ import sys
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT / 'kits/XnuPrelink'))
import native_prelink as np
sys.path.insert(0, str(Path(__file__).resolve().parent))
from kernel_target import TRIPLE
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--component', choices=['VirtioMmioBlock', 'RamBlock', 'NativeVirtioBlock', 'NativeRamBlock'], default='VirtioMmioBlock')
parser.add_argument('--directory', type=Path, default=ROOT / 'build/virtio-mmio-block')
@@ -78,7 +80,7 @@ if any(re.search(r'IOBlockStorageDeviceD[01]Ev$', n) for n in undefined):
bindings = {name: possible[name] for name in undefined}
assembly = out / 'original-bindings.S'
assembly.write_text(''.join(f'.globl {name}\n.set {name}, {value:#x}\n' for name, value in sorted(bindings.items())))
subprocess.run(['xcrun', 'clang', '-target', 'arm64-apple-ios17.0', '-Werror', '-Wno-incompatible-sysroot',
subprocess.run(['xcrun', 'clang', '-target', TRIPLE, '-Werror', '-Wno-incompatible-sysroot',
'-c', assembly, '-o', out / 'original-bindings.o'], check=True)
archive = out / ('lib' + canonical + '.a')
archive.unlink(missing_ok=True)