1*043036a2SApple OSS Distributions /* 2*043036a2SApple OSS Distributions * Copyright (c) 2024 Apple Inc. All rights reserved. 3*043036a2SApple OSS Distributions * 4*043036a2SApple OSS Distributions * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5*043036a2SApple OSS Distributions * 6*043036a2SApple OSS Distributions * This file contains Original Code and/or Modifications of Original Code 7*043036a2SApple OSS Distributions * as defined in and that are subject to the Apple Public Source License 8*043036a2SApple OSS Distributions * Version 2.0 (the 'License'). You may not use this file except in 9*043036a2SApple OSS Distributions * compliance with the License. The rights granted to you under the License 10*043036a2SApple OSS Distributions * may not be used to create, or enable the creation or redistribution of, 11*043036a2SApple OSS Distributions * unlawful or unlicensed copies of an Apple operating system, or to 12*043036a2SApple OSS Distributions * circumvent, violate, or enable the circumvention or violation of, any 13*043036a2SApple OSS Distributions * terms of an Apple operating system software license agreement. 14*043036a2SApple OSS Distributions * 15*043036a2SApple OSS Distributions * Please obtain a copy of the License at 16*043036a2SApple OSS Distributions * http://www.opensource.apple.com/apsl/ and read it before using this file. 17*043036a2SApple OSS Distributions * 18*043036a2SApple OSS Distributions * The Original Code and all software distributed under the License are 19*043036a2SApple OSS Distributions * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20*043036a2SApple OSS Distributions * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21*043036a2SApple OSS Distributions * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22*043036a2SApple OSS Distributions * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23*043036a2SApple OSS Distributions * Please see the License for the specific language governing rights and 24*043036a2SApple OSS Distributions * limitations under the License. 25*043036a2SApple OSS Distributions * 26*043036a2SApple OSS Distributions * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27*043036a2SApple OSS Distributions * 28*043036a2SApple OSS Distributions */ 29*043036a2SApple OSS Distributions 30*043036a2SApple OSS Distributions #include <darwintest.h> 31*043036a2SApple OSS Distributions #include <darwintest_utils.h> 32*043036a2SApple OSS Distributions 33*043036a2SApple OSS Distributions #include <os/thread_self_restrict.h> 34*043036a2SApple OSS Distributions 35*043036a2SApple OSS Distributions #include <stdlib.h> 36*043036a2SApple OSS Distributions #include <sys/mman.h> 37*043036a2SApple OSS Distributions 38*043036a2SApple OSS Distributions #include <mach/mach.h> 39*043036a2SApple OSS Distributions #include <mach/mach_vm.h> 40*043036a2SApple OSS Distributions 41*043036a2SApple OSS Distributions #include <System/machine/cpu_capabilities.h> 42*043036a2SApple OSS Distributions 43*043036a2SApple OSS Distributions #include "exc_guard_helper.h" 44*043036a2SApple OSS Distributions #include "test_utils.h" 45*043036a2SApple OSS Distributions 46*043036a2SApple OSS Distributions T_GLOBAL_META( 47*043036a2SApple OSS Distributions T_META_NAMESPACE("xnu.vm"), 48*043036a2SApple OSS Distributions T_META_RADAR_COMPONENT_NAME("xnu"), 49*043036a2SApple OSS Distributions T_META_RADAR_COMPONENT_VERSION("VM"), 50*043036a2SApple OSS Distributions T_META_OWNER("jharmening"), 51*043036a2SApple OSS Distributions T_META_CHECK_LEAKS(false), 52*043036a2SApple OSS Distributions T_META_RUN_CONCURRENTLY(true), 53*043036a2SApple OSS Distributions T_META_ALL_VALID_ARCHS(true)); 54*043036a2SApple OSS Distributions 55*043036a2SApple OSS Distributions typedef struct { 56*043036a2SApple OSS Distributions uint64_t ptr; 57*043036a2SApple OSS Distributions uint32_t size; 58*043036a2SApple OSS Distributions char test_pattern; 59*043036a2SApple OSS Distributions bool copy_expected; 60*043036a2SApple OSS Distributions bool should_fail; 61*043036a2SApple OSS Distributions bool upl_rw; 62*043036a2SApple OSS Distributions } upl_test_args; 63*043036a2SApple OSS Distributions 64*043036a2SApple OSS Distributions T_DECL(vm_upl_ro_on_rw, 65*043036a2SApple OSS Distributions "Generate RO UPL against RW memory region") 66*043036a2SApple OSS Distributions { 67*043036a2SApple OSS Distributions const size_t buf_size = 10 * PAGE_SIZE; 68*043036a2SApple OSS Distributions unsigned int *buf = mmap(NULL, buf_size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); 69*043036a2SApple OSS Distributions T_QUIET; T_ASSERT_NE_PTR(buf, MAP_FAILED, "map buffer"); 70*043036a2SApple OSS Distributions 71*043036a2SApple OSS Distributions for (unsigned int i = 0; i < (buf_size / sizeof(*buf)); i++) { 72*043036a2SApple OSS Distributions buf[i] = (unsigned int)'a' + i; 73*043036a2SApple OSS Distributions } 74*043036a2SApple OSS Distributions 75*043036a2SApple OSS Distributions upl_test_args args = { .ptr = (uint64_t)buf, .size = buf_size, .test_pattern = 'a', 76*043036a2SApple OSS Distributions .copy_expected = false, .should_fail = false, .upl_rw = false }; 77*043036a2SApple OSS Distributions 78*043036a2SApple OSS Distributions int64_t addr = (int64_t)&args; 79*043036a2SApple OSS Distributions int64_t result = 0; 80*043036a2SApple OSS Distributions size_t s = sizeof(result); 81*043036a2SApple OSS Distributions T_ASSERT_POSIX_SUCCESS(sysctlbyname("debug.test.vm_upl", &result, &s, &addr, sizeof(addr)), 82*043036a2SApple OSS Distributions "sysctlbyname(debug.test.vm_upl)"); 83*043036a2SApple OSS Distributions 84*043036a2SApple OSS Distributions args.ptr = (uint64_t)buf + 0x800; 85*043036a2SApple OSS Distributions args.size -= 0x1000; 86*043036a2SApple OSS Distributions 87*043036a2SApple OSS Distributions T_ASSERT_POSIX_SUCCESS(sysctlbyname("debug.test.vm_upl", &result, &s, &addr, sizeof(addr)), 88*043036a2SApple OSS Distributions "sysctlbyname(debug.test.vm_upl)"); 89*043036a2SApple OSS Distributions 90*043036a2SApple OSS Distributions args.ptr = (uint64_t)buf + 0x1000; 91*043036a2SApple OSS Distributions args.size -= 0x1000; 92*043036a2SApple OSS Distributions 93*043036a2SApple OSS Distributions T_ASSERT_POSIX_SUCCESS(sysctlbyname("debug.test.vm_upl", &result, &s, &addr, sizeof(addr)), 94*043036a2SApple OSS Distributions "sysctlbyname(debug.test.vm_upl)"); 95*043036a2SApple OSS Distributions 96*043036a2SApple OSS Distributions munmap(buf, buf_size); 97*043036a2SApple OSS Distributions } 98*043036a2SApple OSS Distributions 99*043036a2SApple OSS Distributions T_DECL(vm_upl_ro_on_ro, 100*043036a2SApple OSS Distributions "Generate RO UPL against RO memory region") 101*043036a2SApple OSS Distributions { 102*043036a2SApple OSS Distributions const size_t buf_size = 10 * PAGE_SIZE; 103*043036a2SApple OSS Distributions unsigned int *buf = mmap(NULL, buf_size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); 104*043036a2SApple OSS Distributions T_QUIET; T_ASSERT_NE_PTR(buf, MAP_FAILED, "map buffer"); 105*043036a2SApple OSS Distributions 106*043036a2SApple OSS Distributions for (unsigned int i = 0; i < (buf_size / sizeof(*buf)); i++) { 107*043036a2SApple OSS Distributions buf[i] = (unsigned int)'a' + i; 108*043036a2SApple OSS Distributions } 109*043036a2SApple OSS Distributions 110*043036a2SApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(mprotect(buf, buf_size, PROT_READ), "mprotect"); 111*043036a2SApple OSS Distributions 112*043036a2SApple OSS Distributions upl_test_args args = { .ptr = (uint64_t)buf, .size = buf_size, .test_pattern = 'a', 113*043036a2SApple OSS Distributions .copy_expected = false, .should_fail = false, .upl_rw = false }; 114*043036a2SApple OSS Distributions 115*043036a2SApple OSS Distributions int64_t addr = (int64_t)&args; 116*043036a2SApple OSS Distributions int64_t result = 0; 117*043036a2SApple OSS Distributions size_t s = sizeof(result); 118*043036a2SApple OSS Distributions T_ASSERT_POSIX_SUCCESS(sysctlbyname("debug.test.vm_upl", &result, &s, &addr, sizeof(addr)), 119*043036a2SApple OSS Distributions "sysctlbyname(debug.test.vm_upl)"); 120*043036a2SApple OSS Distributions 121*043036a2SApple OSS Distributions args.ptr = (uint64_t)buf + 0x800; 122*043036a2SApple OSS Distributions args.size -= 0x1000; 123*043036a2SApple OSS Distributions 124*043036a2SApple OSS Distributions T_ASSERT_POSIX_SUCCESS(sysctlbyname("debug.test.vm_upl", &result, &s, &addr, sizeof(addr)), 125*043036a2SApple OSS Distributions "sysctlbyname(debug.test.vm_upl)"); 126*043036a2SApple OSS Distributions 127*043036a2SApple OSS Distributions args.ptr = (uint64_t)buf + 0x1000; 128*043036a2SApple OSS Distributions args.size -= 0x1000; 129*043036a2SApple OSS Distributions 130*043036a2SApple OSS Distributions T_ASSERT_POSIX_SUCCESS(sysctlbyname("debug.test.vm_upl", &result, &s, &addr, sizeof(addr)), 131*043036a2SApple OSS Distributions "sysctlbyname(debug.test.vm_upl)"); 132*043036a2SApple OSS Distributions 133*043036a2SApple OSS Distributions munmap(buf, buf_size); 134*043036a2SApple OSS Distributions } 135*043036a2SApple OSS Distributions 136*043036a2SApple OSS Distributions T_DECL(vm_upl_rw_on_rw, 137*043036a2SApple OSS Distributions "Generate RW UPL against RW memory region") 138*043036a2SApple OSS Distributions { 139*043036a2SApple OSS Distributions const size_t buf_size = 10 * PAGE_SIZE; 140*043036a2SApple OSS Distributions unsigned int *buf = mmap(NULL, buf_size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); 141*043036a2SApple OSS Distributions T_QUIET; T_ASSERT_NE_PTR(buf, MAP_FAILED, "map buffer"); 142*043036a2SApple OSS Distributions 143*043036a2SApple OSS Distributions for (unsigned int i = 0; i < (buf_size / sizeof(*buf)); i++) { 144*043036a2SApple OSS Distributions buf[i] = (unsigned int)'a' + i; 145*043036a2SApple OSS Distributions } 146*043036a2SApple OSS Distributions 147*043036a2SApple OSS Distributions upl_test_args args = { .ptr = (uint64_t)buf, .size = buf_size, .test_pattern = 'b', 148*043036a2SApple OSS Distributions .copy_expected = false, .should_fail = false, .upl_rw = true }; 149*043036a2SApple OSS Distributions 150*043036a2SApple OSS Distributions int64_t addr = (int64_t)&args; 151*043036a2SApple OSS Distributions int64_t result = 0; 152*043036a2SApple OSS Distributions size_t s = sizeof(result); 153*043036a2SApple OSS Distributions T_ASSERT_POSIX_SUCCESS(sysctlbyname("debug.test.vm_upl", &result, &s, &addr, sizeof(addr)), 154*043036a2SApple OSS Distributions "sysctlbyname(debug.test.vm_upl)"); 155*043036a2SApple OSS Distributions 156*043036a2SApple OSS Distributions for (unsigned int i = 0; i < (buf_size / sizeof(*buf)); i++) { 157*043036a2SApple OSS Distributions T_QUIET; T_ASSERT_EQ(buf[i], (unsigned int)'b' + i, 158*043036a2SApple OSS Distributions "buf[%u]='%u' == '%u'", 159*043036a2SApple OSS Distributions i, buf[i], (unsigned int)'b' + i); 160*043036a2SApple OSS Distributions } 161*043036a2SApple OSS Distributions bzero(buf, buf_size); 162*043036a2SApple OSS Distributions args.ptr = (uint64_t)buf + 0x800; 163*043036a2SApple OSS Distributions args.size -= 0x1000; 164*043036a2SApple OSS Distributions 165*043036a2SApple OSS Distributions T_ASSERT_POSIX_SUCCESS(sysctlbyname("debug.test.vm_upl", &result, &s, &addr, sizeof(addr)), 166*043036a2SApple OSS Distributions "sysctlbyname(debug.test.vm_upl)"); 167*043036a2SApple OSS Distributions 168*043036a2SApple OSS Distributions for (unsigned int i = 0; i < (buf_size / sizeof(*buf)); i++) { 169*043036a2SApple OSS Distributions if ((i < (0x800 / sizeof(*buf))) || (i >= ((0x800 + args.size) / sizeof(*buf)))) { 170*043036a2SApple OSS Distributions T_QUIET; T_ASSERT_EQ(buf[i], 0, 171*043036a2SApple OSS Distributions "buf[%u]='%u' == 0", i, buf[i]); 172*043036a2SApple OSS Distributions } else { 173*043036a2SApple OSS Distributions T_QUIET; T_ASSERT_EQ(buf[i], (unsigned int)'b' + i - (unsigned int)(0x800 / sizeof(*buf)), 174*043036a2SApple OSS Distributions "buf[%u]='%u' == '%u'", 175*043036a2SApple OSS Distributions i, buf[i], (unsigned int)'b' + i - (unsigned int)(0x800 / sizeof(*buf))); 176*043036a2SApple OSS Distributions } 177*043036a2SApple OSS Distributions } 178*043036a2SApple OSS Distributions 179*043036a2SApple OSS Distributions bzero(buf, buf_size); 180*043036a2SApple OSS Distributions args.ptr = (uint64_t)buf + 0x1000; 181*043036a2SApple OSS Distributions args.size -= 0x1000; 182*043036a2SApple OSS Distributions 183*043036a2SApple OSS Distributions T_ASSERT_POSIX_SUCCESS(sysctlbyname("debug.test.vm_upl", &result, &s, &addr, sizeof(addr)), 184*043036a2SApple OSS Distributions "sysctlbyname(debug.test.vm_upl)"); 185*043036a2SApple OSS Distributions 186*043036a2SApple OSS Distributions for (unsigned int i = 0; i < (buf_size / sizeof(*buf)); i++) { 187*043036a2SApple OSS Distributions if ((i < (0x1000 / sizeof(*buf))) || (i >= ((0x1000 + args.size) / sizeof(*buf)))) { 188*043036a2SApple OSS Distributions T_QUIET; T_ASSERT_EQ(buf[i], 0, 189*043036a2SApple OSS Distributions "buf[%u]='%u' == 0", i, buf[i]); 190*043036a2SApple OSS Distributions } else { 191*043036a2SApple OSS Distributions T_QUIET; T_ASSERT_EQ(buf[i], (unsigned int)'b' + i - (unsigned int)(0x1000 / sizeof(*buf)), 192*043036a2SApple OSS Distributions "buf[%u]='%u' == '%u'", 193*043036a2SApple OSS Distributions i, buf[i], (unsigned int)'b' + i - (unsigned int)(0x1000 / sizeof(*buf))); 194*043036a2SApple OSS Distributions } 195*043036a2SApple OSS Distributions } 196*043036a2SApple OSS Distributions 197*043036a2SApple OSS Distributions munmap(buf, buf_size); 198*043036a2SApple OSS Distributions } 199*043036a2SApple OSS Distributions 200*043036a2SApple OSS Distributions T_DECL(vm_upl_rw_on_ro, 201*043036a2SApple OSS Distributions "Generate RW UPL against RO memory region") 202*043036a2SApple OSS Distributions { 203*043036a2SApple OSS Distributions const size_t buf_size = 10 * PAGE_SIZE; 204*043036a2SApple OSS Distributions unsigned int *buf = mmap(NULL, buf_size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); 205*043036a2SApple OSS Distributions T_QUIET; T_ASSERT_NE_PTR(buf, MAP_FAILED, "map buffer"); 206*043036a2SApple OSS Distributions 207*043036a2SApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(mprotect(buf, buf_size, PROT_READ), "mprotect"); 208*043036a2SApple OSS Distributions 209*043036a2SApple OSS Distributions upl_test_args args = { .ptr = (uint64_t)buf, .size = buf_size, .test_pattern = 'b', 210*043036a2SApple OSS Distributions .copy_expected = false, .should_fail = true, .upl_rw = true }; 211*043036a2SApple OSS Distributions 212*043036a2SApple OSS Distributions int64_t addr = (int64_t)&args; 213*043036a2SApple OSS Distributions int64_t result = 0; 214*043036a2SApple OSS Distributions size_t s = sizeof(result); 215*043036a2SApple OSS Distributions T_ASSERT_POSIX_SUCCESS(sysctlbyname("debug.test.vm_upl", &result, &s, &addr, sizeof(addr)), 216*043036a2SApple OSS Distributions "sysctlbyname(debug.test.vm_upl)"); 217*043036a2SApple OSS Distributions 218*043036a2SApple OSS Distributions args.ptr = (uint64_t)buf + 0x800; 219*043036a2SApple OSS Distributions args.size -= 0x1000; 220*043036a2SApple OSS Distributions 221*043036a2SApple OSS Distributions T_ASSERT_POSIX_SUCCESS(sysctlbyname("debug.test.vm_upl", &result, &s, &addr, sizeof(addr)), 222*043036a2SApple OSS Distributions "sysctlbyname(debug.test.vm_upl)"); 223*043036a2SApple OSS Distributions 224*043036a2SApple OSS Distributions args.ptr = (uint64_t)buf + 0x1000; 225*043036a2SApple OSS Distributions args.size -= 0x1000; 226*043036a2SApple OSS Distributions 227*043036a2SApple OSS Distributions T_ASSERT_POSIX_SUCCESS(sysctlbyname("debug.test.vm_upl", &result, &s, &addr, sizeof(addr)), 228*043036a2SApple OSS Distributions "sysctlbyname(debug.test.vm_upl)"); 229*043036a2SApple OSS Distributions 230*043036a2SApple OSS Distributions munmap(buf, buf_size); 231*043036a2SApple OSS Distributions } 232*043036a2SApple OSS Distributions 233*043036a2SApple OSS Distributions T_DECL(vm_upl_ro_on_rx, 234*043036a2SApple OSS Distributions "Generate RO UPL against RX memory region") 235*043036a2SApple OSS Distributions { 236*043036a2SApple OSS Distributions bool copy_expected = true; 237*043036a2SApple OSS Distributions #if TARGET_OS_OSX 238*043036a2SApple OSS Distributions /** 239*043036a2SApple OSS Distributions * For embedded targets, UPL creation against RX mappings should always produce a copy due to codesigning. 240*043036a2SApple OSS Distributions * For MacOS, a copy should only be produced if the SPTM is enabled, due to the SPTM's stricter requirements 241*043036a2SApple OSS Distributions * for DMA mappings of executable frame types. 242*043036a2SApple OSS Distributions */ 243*043036a2SApple OSS Distributions if (!is_sptm_enabled()) { 244*043036a2SApple OSS Distributions copy_expected = false; 245*043036a2SApple OSS Distributions } 246*043036a2SApple OSS Distributions #endif /* TARGET_OS_OSX */ 247*043036a2SApple OSS Distributions 248*043036a2SApple OSS Distributions upl_test_args args = { .ptr = (uint64_t)__builtin_return_address(0), .size = PAGE_SIZE, .test_pattern = 'a', 249*043036a2SApple OSS Distributions .copy_expected = copy_expected, .should_fail = false, .upl_rw = false }; 250*043036a2SApple OSS Distributions 251*043036a2SApple OSS Distributions int64_t addr = (int64_t)&args; 252*043036a2SApple OSS Distributions int64_t result = 0; 253*043036a2SApple OSS Distributions size_t s = sizeof(result); 254*043036a2SApple OSS Distributions T_ASSERT_POSIX_SUCCESS(sysctlbyname("debug.test.vm_upl", &result, &s, &addr, sizeof(addr)), 255*043036a2SApple OSS Distributions "sysctlbyname(debug.test.vm_upl)"); 256*043036a2SApple OSS Distributions 257*043036a2SApple OSS Distributions args.ptr += 0x100; 258*043036a2SApple OSS Distributions args.size -= 0x200; 259*043036a2SApple OSS Distributions 260*043036a2SApple OSS Distributions T_ASSERT_POSIX_SUCCESS(sysctlbyname("debug.test.vm_upl", &result, &s, &addr, sizeof(addr)), 261*043036a2SApple OSS Distributions "sysctlbyname(debug.test.vm_upl)"); 262*043036a2SApple OSS Distributions } 263*043036a2SApple OSS Distributions 264*043036a2SApple OSS Distributions T_DECL(vm_upl_rw_on_rx, 265*043036a2SApple OSS Distributions "Generate RW UPL against RX memory region") 266*043036a2SApple OSS Distributions { 267*043036a2SApple OSS Distributions upl_test_args args = { .ptr = (uint64_t)__builtin_return_address(0), .size = PAGE_SIZE, .test_pattern = 'a', 268*043036a2SApple OSS Distributions .copy_expected = true, .should_fail = true, .upl_rw = true }; 269*043036a2SApple OSS Distributions 270*043036a2SApple OSS Distributions int64_t addr = (int64_t)&args; 271*043036a2SApple OSS Distributions int64_t result = 0; 272*043036a2SApple OSS Distributions size_t s = sizeof(result); 273*043036a2SApple OSS Distributions T_ASSERT_POSIX_SUCCESS(sysctlbyname("debug.test.vm_upl", &result, &s, &addr, sizeof(addr)), 274*043036a2SApple OSS Distributions "sysctlbyname(debug.test.vm_upl)"); 275*043036a2SApple OSS Distributions 276*043036a2SApple OSS Distributions args.ptr += 0x100; 277*043036a2SApple OSS Distributions args.size -= 0x200; 278*043036a2SApple OSS Distributions 279*043036a2SApple OSS Distributions T_ASSERT_POSIX_SUCCESS(sysctlbyname("debug.test.vm_upl", &result, &s, &addr, sizeof(addr)), 280*043036a2SApple OSS Distributions "sysctlbyname(debug.test.vm_upl)"); 281*043036a2SApple OSS Distributions } 282*043036a2SApple OSS Distributions 283*043036a2SApple OSS Distributions T_DECL(vm_upl_ro_on_jit, 284*043036a2SApple OSS Distributions "Generate RO UPL against JIT memory region") 285*043036a2SApple OSS Distributions { 286*043036a2SApple OSS Distributions if (!is_map_jit_allowed()) { 287*043036a2SApple OSS Distributions T_SKIP("MAP_JIT not allowed for this system configuration"); 288*043036a2SApple OSS Distributions } 289*043036a2SApple OSS Distributions /** 290*043036a2SApple OSS Distributions * Direct RO UPLs against JIT pages should be allowed for non-SPTM targets. 291*043036a2SApple OSS Distributions * For SPTM targets, a copy is expected due to the SPTM's stricter requirements for DMA 292*043036a2SApple OSS Distributions * mappings of executable frame types. 293*043036a2SApple OSS Distributions */ 294*043036a2SApple OSS Distributions bool copy_expected = is_sptm_enabled(); 295*043036a2SApple OSS Distributions const size_t buf_size = 10 * PAGE_SIZE; 296*043036a2SApple OSS Distributions unsigned int *buf = mmap(NULL, buf_size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_PRIVATE | MAP_JIT, -1, 0); 297*043036a2SApple OSS Distributions T_QUIET; T_ASSERT_NE_PTR(buf, MAP_FAILED, "map buffer"); 298*043036a2SApple OSS Distributions 299*043036a2SApple OSS Distributions if (os_thread_self_restrict_rwx_is_supported()) { 300*043036a2SApple OSS Distributions os_thread_self_restrict_rwx_to_rw(); 301*043036a2SApple OSS Distributions } 302*043036a2SApple OSS Distributions 303*043036a2SApple OSS Distributions for (unsigned int i = 0; i < (buf_size / sizeof(*buf)); i++) { 304*043036a2SApple OSS Distributions buf[i] = (unsigned int)'a' + i; 305*043036a2SApple OSS Distributions } 306*043036a2SApple OSS Distributions 307*043036a2SApple OSS Distributions upl_test_args args = { .ptr = (uint64_t)buf, .size = buf_size, .test_pattern = 'b', 308*043036a2SApple OSS Distributions .copy_expected = copy_expected, .should_fail = false, .upl_rw = false }; 309*043036a2SApple OSS Distributions 310*043036a2SApple OSS Distributions int64_t addr = (int64_t)&args; 311*043036a2SApple OSS Distributions int64_t result = 0; 312*043036a2SApple OSS Distributions size_t s = sizeof(result); 313*043036a2SApple OSS Distributions T_ASSERT_POSIX_SUCCESS(sysctlbyname("debug.test.vm_upl", &result, &s, &addr, sizeof(addr)), 314*043036a2SApple OSS Distributions "sysctlbyname(debug.test.vm_upl)"); 315*043036a2SApple OSS Distributions 316*043036a2SApple OSS Distributions args.ptr = (uint64_t)buf + 0x800; 317*043036a2SApple OSS Distributions args.size -= 0x1000; 318*043036a2SApple OSS Distributions 319*043036a2SApple OSS Distributions T_ASSERT_POSIX_SUCCESS(sysctlbyname("debug.test.vm_upl", &result, &s, &addr, sizeof(addr)), 320*043036a2SApple OSS Distributions "sysctlbyname(debug.test.vm_upl)"); 321*043036a2SApple OSS Distributions 322*043036a2SApple OSS Distributions args.ptr = (uint64_t)buf + 0x1000; 323*043036a2SApple OSS Distributions args.size -= 0x1000; 324*043036a2SApple OSS Distributions 325*043036a2SApple OSS Distributions T_ASSERT_POSIX_SUCCESS(sysctlbyname("debug.test.vm_upl", &result, &s, &addr, sizeof(addr)), 326*043036a2SApple OSS Distributions "sysctlbyname(debug.test.vm_upl)"); 327*043036a2SApple OSS Distributions 328*043036a2SApple OSS Distributions munmap(buf, buf_size); 329*043036a2SApple OSS Distributions } 330*043036a2SApple OSS Distributions 331*043036a2SApple OSS Distributions T_DECL(vm_upl_rw_on_jit, 332*043036a2SApple OSS Distributions "Generate RW UPL against JIT memory region") 333*043036a2SApple OSS Distributions { 334*043036a2SApple OSS Distributions if (process_is_translated()) { 335*043036a2SApple OSS Distributions /* TODO: Remove this once rdar://142438840 is fixed. */ 336*043036a2SApple OSS Distributions T_SKIP("Guard exception handling does not work correctly with Rosetta (rdar://142438840), skipping..."); 337*043036a2SApple OSS Distributions } 338*043036a2SApple OSS Distributions if (!is_map_jit_allowed()) { 339*043036a2SApple OSS Distributions T_SKIP("MAP_JIT not allowed for this system configuration"); 340*043036a2SApple OSS Distributions } 341*043036a2SApple OSS Distributions const size_t buf_size = 10 * PAGE_SIZE; 342*043036a2SApple OSS Distributions /** 343*043036a2SApple OSS Distributions * Direct RW UPLs against JIT pages should be allowed for non-SPTM targets. 344*043036a2SApple OSS Distributions * For SPTM targets, UPL creation should fail due to the SPTM's stricter requirements for DMA 345*043036a2SApple OSS Distributions * mappings of executable frame types. 346*043036a2SApple OSS Distributions */ 347*043036a2SApple OSS Distributions bool should_fail = is_sptm_enabled(); 348*043036a2SApple OSS Distributions unsigned int *buf = mmap(NULL, buf_size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_PRIVATE | MAP_JIT, -1, 0); 349*043036a2SApple OSS Distributions T_QUIET; T_ASSERT_NE_PTR(buf, MAP_FAILED, "map buffer"); 350*043036a2SApple OSS Distributions 351*043036a2SApple OSS Distributions upl_test_args args = { .ptr = (uint64_t)buf, .size = buf_size, .test_pattern = 'b', 352*043036a2SApple OSS Distributions .copy_expected = false, .should_fail = should_fail, .upl_rw = true }; 353*043036a2SApple OSS Distributions 354*043036a2SApple OSS Distributions __block int64_t addr = (int64_t)&args; 355*043036a2SApple OSS Distributions __block int64_t result = 0; 356*043036a2SApple OSS Distributions __block size_t s = sizeof(result); 357*043036a2SApple OSS Distributions 358*043036a2SApple OSS Distributions /* Ensure that guard exceptions will not be fatal to the test process. */ 359*043036a2SApple OSS Distributions enable_exc_guard_of_type(GUARD_TYPE_VIRT_MEMORY); 360*043036a2SApple OSS Distributions 361*043036a2SApple OSS Distributions /** 362*043036a2SApple OSS Distributions * Iterate 3 times to guarantee buffer offsets that are neither 4K nor 16K aligned, 363*043036a2SApple OSS Distributions * and 4K but not necessarily 16K aligned. 364*043036a2SApple OSS Distributions */ 365*043036a2SApple OSS Distributions for (int i = 0; i < 2; i++) { 366*043036a2SApple OSS Distributions exc_guard_helper_info_t exc_info; 367*043036a2SApple OSS Distributions bool caught_exception = 368*043036a2SApple OSS Distributions block_raised_exc_guard_of_type(GUARD_TYPE_VIRT_MEMORY, &exc_info, ^{ 369*043036a2SApple OSS Distributions T_ASSERT_POSIX_SUCCESS(sysctlbyname("debug.test.vm_upl", &result, &s, &addr, sizeof(addr)), 370*043036a2SApple OSS Distributions "sysctlbyname(debug.test.vm_upl)"); 371*043036a2SApple OSS Distributions }); 372*043036a2SApple OSS Distributions if (args.should_fail) { 373*043036a2SApple OSS Distributions T_ASSERT_TRUE(caught_exception, "Failing test should also throw guard exception"); 374*043036a2SApple OSS Distributions T_ASSERT_EQ(exc_info.guard_flavor, kGUARD_EXC_SEC_UPL_WRITE_ON_EXEC_REGION, 375*043036a2SApple OSS Distributions "Failing test throws the expected guard exception flavor"); 376*043036a2SApple OSS Distributions T_ASSERT_EQ(exc_info.catch_count, 1, "Failing test should throw exactly one guard exception"); 377*043036a2SApple OSS Distributions } else { 378*043036a2SApple OSS Distributions T_ASSERT_FALSE(caught_exception, "Passing test should not throw guard exception"); 379*043036a2SApple OSS Distributions } 380*043036a2SApple OSS Distributions 381*043036a2SApple OSS Distributions args.ptr += 0x800; 382*043036a2SApple OSS Distributions args.size -= 0x1000; 383*043036a2SApple OSS Distributions } 384*043036a2SApple OSS Distributions 385*043036a2SApple OSS Distributions munmap(buf, buf_size); 386*043036a2SApple OSS Distributions } 387*043036a2SApple OSS Distributions 388*043036a2SApple OSS Distributions T_DECL(vm_upl_ro_on_commpage, 389*043036a2SApple OSS Distributions "Generate RO UPL against comm page") 390*043036a2SApple OSS Distributions { 391*043036a2SApple OSS Distributions #if !TARGET_OS_OSX 392*043036a2SApple OSS Distributions T_SKIP("Comm page only guaranteed to be within user address range on MacOS, skipping..."); 393*043036a2SApple OSS Distributions #else 394*043036a2SApple OSS Distributions #ifndef __arm64__ 395*043036a2SApple OSS Distributions T_SKIP("Comm page only has UPL-incompatible mapping on arm64, skipping..."); 396*043036a2SApple OSS Distributions #else 397*043036a2SApple OSS Distributions upl_test_args args = { .ptr = (uint64_t)_COMM_PAGE_START_ADDRESS, .size = 0x1000, .test_pattern = 'b', 398*043036a2SApple OSS Distributions .copy_expected = false, .should_fail = true, .upl_rw = false }; 399*043036a2SApple OSS Distributions 400*043036a2SApple OSS Distributions int64_t addr = (int64_t)&args; 401*043036a2SApple OSS Distributions int64_t result = 0; 402*043036a2SApple OSS Distributions size_t s = sizeof(result); 403*043036a2SApple OSS Distributions T_ASSERT_POSIX_SUCCESS(sysctlbyname("debug.test.vm_upl", &result, &s, &addr, sizeof(addr)), 404*043036a2SApple OSS Distributions "sysctlbyname(debug.test.vm_upl)"); 405*043036a2SApple OSS Distributions #endif /* !defined(__arm64__) */ 406*043036a2SApple OSS Distributions #endif /* !TARGET_OS_OSX */ 407*043036a2SApple OSS Distributions } 408*043036a2SApple OSS Distributions 409*043036a2SApple OSS Distributions T_DECL(vm_upl_partial_cow, 410*043036a2SApple OSS Distributions "Generate a UPL that requires CoW setup for part of an object") 411*043036a2SApple OSS Distributions { 412*043036a2SApple OSS Distributions const size_t buf_size = 10 * PAGE_SIZE; 413*043036a2SApple OSS Distributions unsigned int *buf = mmap(NULL, buf_size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); 414*043036a2SApple OSS Distributions T_QUIET; T_ASSERT_NE_PTR(buf, MAP_FAILED, "map buffer"); 415*043036a2SApple OSS Distributions 416*043036a2SApple OSS Distributions for (unsigned int i = 0; i < (buf_size / sizeof(*buf)); i++) { 417*043036a2SApple OSS Distributions buf[i] = (unsigned int)'a' + i; 418*043036a2SApple OSS Distributions } 419*043036a2SApple OSS Distributions 420*043036a2SApple OSS Distributions /* 421*043036a2SApple OSS Distributions * Mark a portion of the buffer RO, which will split off a separate vm_map_entry backed by the same 422*043036a2SApple OSS Distributions * vm_object. This will produce an internal COPY_SYMMETRIC object with refcount > 1, which is the 423*043036a2SApple OSS Distributions * baseline requirement for partial CoW setup by vm_map_create_upl(). 424*043036a2SApple OSS Distributions */ 425*043036a2SApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(mprotect((char*)buf + (8 * PAGE_SIZE), 2 * PAGE_SIZE, PROT_READ), "mprotect"); 426*043036a2SApple OSS Distributions 427*043036a2SApple OSS Distributions /* 428*043036a2SApple OSS Distributions * Request a non-page-aligned UPL against the RW region of the buffer, to ensure that partial CoW 429*043036a2SApple OSS Distributions * setup still ultimately uses a page-aligned buffer as required for vm_map_entry clipping. 430*043036a2SApple OSS Distributions */ 431*043036a2SApple OSS Distributions upl_test_args args = { .ptr = (uint64_t)buf + 0x800, .size = 2 * PAGE_SIZE, .test_pattern = 'b', 432*043036a2SApple OSS Distributions .copy_expected = false, .should_fail = false, .upl_rw = true }; 433*043036a2SApple OSS Distributions 434*043036a2SApple OSS Distributions int64_t addr = (int64_t)&args; 435*043036a2SApple OSS Distributions int64_t result = 0; 436*043036a2SApple OSS Distributions size_t s = sizeof(result); 437*043036a2SApple OSS Distributions T_ASSERT_POSIX_SUCCESS(sysctlbyname("debug.test.vm_upl", &result, &s, &addr, sizeof(addr)), 438*043036a2SApple OSS Distributions "sysctlbyname(debug.test.vm_upl)"); 439*043036a2SApple OSS Distributions 440*043036a2SApple OSS Distributions for (unsigned int i = 0; i < (buf_size / sizeof(*buf)); i++) { 441*043036a2SApple OSS Distributions if ((i < (0x800 / sizeof(*buf))) || (i >= ((0x800 + args.size) / sizeof(*buf)))) { 442*043036a2SApple OSS Distributions T_QUIET; T_ASSERT_EQ(buf[i], (unsigned int)'a' + i, 443*043036a2SApple OSS Distributions "buf[%u]='%u' == '%u'", i, buf[i], (unsigned int)'a' + i); 444*043036a2SApple OSS Distributions } else { 445*043036a2SApple OSS Distributions T_QUIET; T_ASSERT_EQ(buf[i], (unsigned int)'b' + i - (unsigned int)(0x800 / sizeof(*buf)), 446*043036a2SApple OSS Distributions "buf[%u]='%u' == '%u'", 447*043036a2SApple OSS Distributions i, buf[i], (unsigned int)'b' + i - (unsigned int)(0x800 / sizeof(*buf))); 448*043036a2SApple OSS Distributions } 449*043036a2SApple OSS Distributions } 450*043036a2SApple OSS Distributions 451*043036a2SApple OSS Distributions munmap(buf, buf_size); 452*043036a2SApple OSS Distributions } 453*043036a2SApple OSS Distributions 454*043036a2SApple OSS Distributions typedef struct { 455*043036a2SApple OSS Distributions uint64_t ptr; 456*043036a2SApple OSS Distributions uint32_t size; 457*043036a2SApple OSS Distributions bool upl_rw; 458*043036a2SApple OSS Distributions bool should_fail; 459*043036a2SApple OSS Distributions uint8_t fault_prot; 460*043036a2SApple OSS Distributions } upl_object_test_args; 461*043036a2SApple OSS Distributions 462*043036a2SApple OSS Distributions T_DECL(vm_upl_rw_on_exec_object, 463*043036a2SApple OSS Distributions "Attempt to create a writable UPL against an object containing executable pages") 464*043036a2SApple OSS Distributions { 465*043036a2SApple OSS Distributions /** 466*043036a2SApple OSS Distributions * This test is meant to exercise functionality that is currently SPTM-specific. 467*043036a2SApple OSS Distributions * It also relies on the assumption that JIT regions are faulted in an all-or-nothing 468*043036a2SApple OSS Distributions * manner, so that the write faults generated by our buffer fill below will also 469*043036a2SApple OSS Distributions * produce executable mappings of the underlying JIT pages. This happens to hold 470*043036a2SApple OSS Distributions * true on SPTM-enabled devices because all of them use xPRR, but may not hold true 471*043036a2SApple OSS Distributions * in general. 472*043036a2SApple OSS Distributions */ 473*043036a2SApple OSS Distributions if (!is_sptm_enabled()) { 474*043036a2SApple OSS Distributions T_SKIP("Exec object test only supported on SPTM-enabled devices, skipping..."); 475*043036a2SApple OSS Distributions } 476*043036a2SApple OSS Distributions if (process_is_translated()) { 477*043036a2SApple OSS Distributions /* TODO: Remove this once rdar://142438840 is fixed. */ 478*043036a2SApple OSS Distributions T_SKIP("Guard exception handling does not work correctly with Rosetta (rdar://142438840), skipping..."); 479*043036a2SApple OSS Distributions } 480*043036a2SApple OSS Distributions if (!is_map_jit_allowed()) { 481*043036a2SApple OSS Distributions T_SKIP("MAP_JIT not allowed for this system configuration"); 482*043036a2SApple OSS Distributions } 483*043036a2SApple OSS Distributions 484*043036a2SApple OSS Distributions const size_t buf_size = 10 * PAGE_SIZE; 485*043036a2SApple OSS Distributions unsigned int *buf = mmap(NULL, buf_size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_PRIVATE | MAP_JIT, -1, 0); 486*043036a2SApple OSS Distributions T_QUIET; T_ASSERT_NE_PTR(buf, MAP_FAILED, "map buffer"); 487*043036a2SApple OSS Distributions 488*043036a2SApple OSS Distributions if (os_thread_self_restrict_rwx_is_supported()) { 489*043036a2SApple OSS Distributions os_thread_self_restrict_rwx_to_rw(); 490*043036a2SApple OSS Distributions } 491*043036a2SApple OSS Distributions 492*043036a2SApple OSS Distributions for (unsigned int i = 0; i < (buf_size / sizeof(*buf)); i++) { 493*043036a2SApple OSS Distributions buf[i] = (unsigned int)'a' + i; 494*043036a2SApple OSS Distributions } 495*043036a2SApple OSS Distributions 496*043036a2SApple OSS Distributions /* Ensure that guard exceptions will not be fatal to the test process. */ 497*043036a2SApple OSS Distributions enable_exc_guard_of_type(GUARD_TYPE_VIRT_MEMORY); 498*043036a2SApple OSS Distributions 499*043036a2SApple OSS Distributions upl_object_test_args args = { .ptr = (uint64_t)buf, .size = buf_size, .upl_rw = true, .should_fail = true, .fault_prot = VM_PROT_NONE }; 500*043036a2SApple OSS Distributions 501*043036a2SApple OSS Distributions __block int64_t addr = (int64_t)&args; 502*043036a2SApple OSS Distributions __block int64_t result = 0; 503*043036a2SApple OSS Distributions __block size_t s = sizeof(result); 504*043036a2SApple OSS Distributions exc_guard_helper_info_t exc_info; 505*043036a2SApple OSS Distributions bool caught_exception = 506*043036a2SApple OSS Distributions block_raised_exc_guard_of_type(GUARD_TYPE_VIRT_MEMORY, &exc_info, ^{ 507*043036a2SApple OSS Distributions T_ASSERT_POSIX_SUCCESS(sysctlbyname("debug.test.vm_upl_object", &result, &s, &addr, sizeof(addr)), 508*043036a2SApple OSS Distributions "sysctlbyname(debug.test.vm_upl_object)"); 509*043036a2SApple OSS Distributions }); 510*043036a2SApple OSS Distributions if (args.should_fail) { 511*043036a2SApple OSS Distributions T_ASSERT_TRUE(caught_exception, "Failing test should also throw guard exception"); 512*043036a2SApple OSS Distributions T_ASSERT_EQ(exc_info.guard_flavor, kGUARD_EXC_SEC_IOPL_ON_EXEC_PAGE, 513*043036a2SApple OSS Distributions "Failing test throws the expected guard exception flavor"); 514*043036a2SApple OSS Distributions T_ASSERT_EQ(exc_info.catch_count, 1, "Failing test should throw exactly one guard exception"); 515*043036a2SApple OSS Distributions } else { 516*043036a2SApple OSS Distributions T_ASSERT_FALSE(caught_exception, "Passing test should not throw guard exception"); 517*043036a2SApple OSS Distributions } 518*043036a2SApple OSS Distributions 519*043036a2SApple OSS Distributions munmap(buf, buf_size); 520*043036a2SApple OSS Distributions } 521*043036a2SApple OSS Distributions 522*043036a2SApple OSS Distributions T_DECL(vm_upl_ro_with_exec_fault, 523*043036a2SApple OSS Distributions "Attempt to exec-fault a region while a UPL is in-flight for that region") 524*043036a2SApple OSS Distributions { 525*043036a2SApple OSS Distributions /** 526*043036a2SApple OSS Distributions * This test is meant to exercise functionality that is currently SPTM-specific. 527*043036a2SApple OSS Distributions */ 528*043036a2SApple OSS Distributions if (!is_sptm_enabled()) { 529*043036a2SApple OSS Distributions T_SKIP("Exec-fault test only supported on SPTM-enabled devices, skipping..."); 530*043036a2SApple OSS Distributions } 531*043036a2SApple OSS Distributions if (process_is_translated()) { 532*043036a2SApple OSS Distributions /* TODO: Remove this once rdar://142438840 is fixed. */ 533*043036a2SApple OSS Distributions T_SKIP("Guard exception handling does not work correctly with Rosetta (rdar://142438840), skipping..."); 534*043036a2SApple OSS Distributions } 535*043036a2SApple OSS Distributions if (!is_map_jit_allowed()) { 536*043036a2SApple OSS Distributions T_SKIP("MAP_JIT not allowed for this system configuration"); 537*043036a2SApple OSS Distributions } 538*043036a2SApple OSS Distributions 539*043036a2SApple OSS Distributions const size_t buf_size = 10 * PAGE_SIZE; 540*043036a2SApple OSS Distributions unsigned int *buf = mmap(NULL, buf_size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_PRIVATE | MAP_JIT, -1, 0); 541*043036a2SApple OSS Distributions T_QUIET; T_ASSERT_NE_PTR(buf, MAP_FAILED, "map buffer"); 542*043036a2SApple OSS Distributions 543*043036a2SApple OSS Distributions if (os_thread_self_restrict_rwx_is_supported()) { 544*043036a2SApple OSS Distributions os_thread_self_restrict_rwx_to_rw(); 545*043036a2SApple OSS Distributions } 546*043036a2SApple OSS Distributions for (unsigned int i = 0; i < (buf_size / sizeof(*buf)); i++) { 547*043036a2SApple OSS Distributions buf[i] = (unsigned int)'a' + i; 548*043036a2SApple OSS Distributions } 549*043036a2SApple OSS Distributions if (os_thread_self_restrict_rwx_is_supported()) { 550*043036a2SApple OSS Distributions os_thread_self_restrict_rwx_to_rx(); 551*043036a2SApple OSS Distributions } 552*043036a2SApple OSS Distributions 553*043036a2SApple OSS Distributions /* Ensure that guard exceptions will not be fatal to the test process. */ 554*043036a2SApple OSS Distributions enable_exc_guard_of_type(GUARD_TYPE_VIRT_MEMORY); 555*043036a2SApple OSS Distributions 556*043036a2SApple OSS Distributions upl_object_test_args args = { .ptr = (uint64_t)buf, .size = buf_size, .upl_rw = false, .should_fail = false, 557*043036a2SApple OSS Distributions .fault_prot = VM_PROT_EXECUTE | VM_PROT_READ }; 558*043036a2SApple OSS Distributions 559*043036a2SApple OSS Distributions __block int64_t addr = (int64_t)&args; 560*043036a2SApple OSS Distributions __block int64_t result = 0; 561*043036a2SApple OSS Distributions __block size_t s = sizeof(result); 562*043036a2SApple OSS Distributions exc_guard_helper_info_t exc_info; 563*043036a2SApple OSS Distributions bool caught_exception = 564*043036a2SApple OSS Distributions block_raised_exc_guard_of_type(GUARD_TYPE_VIRT_MEMORY, &exc_info, ^{ 565*043036a2SApple OSS Distributions T_ASSERT_POSIX_SUCCESS(sysctlbyname("debug.test.vm_upl_object", &result, &s, &addr, sizeof(addr)), 566*043036a2SApple OSS Distributions "sysctlbyname(debug.test.vm_upl_object)"); 567*043036a2SApple OSS Distributions }); 568*043036a2SApple OSS Distributions T_ASSERT_TRUE(caught_exception, "Exec fault should throw guard exception"); 569*043036a2SApple OSS Distributions T_ASSERT_EQ(exc_info.guard_flavor, kGUARD_EXC_SEC_EXEC_ON_IOPL_PAGE, 570*043036a2SApple OSS Distributions "Attempted exec fault throws the expected guard exception flavor"); 571*043036a2SApple OSS Distributions T_ASSERT_EQ(exc_info.catch_count, 1, "Attempted exec fault should throw exactly one guard exception"); 572*043036a2SApple OSS Distributions 573*043036a2SApple OSS Distributions munmap(buf, buf_size); 574*043036a2SApple OSS Distributions } 575*043036a2SApple OSS Distributions 576*043036a2SApple OSS Distributions T_DECL(vm_upl_ro_with_write_fault_on_exec_file, 577*043036a2SApple OSS Distributions "Attempt to write-fault a file-backed executable region while a UPL is in-flight for that region") 578*043036a2SApple OSS Distributions { 579*043036a2SApple OSS Distributions #if TARGET_OS_OSX 580*043036a2SApple OSS Distributions /** 581*043036a2SApple OSS Distributions * Test the bug uncovered in rdar://158063220. This requires an on-the-fly retype to an 582*043036a2SApple OSS Distributions * executable frame type in conjunction with a write fault on a file-backed page that is also 583*043036a2SApple OSS Distributions * being cleaned in place. This implies a "legacy JIT" mapping, since modern MAP_JIT mappings 584*043036a2SApple OSS Distributions * aren't allowed for file-backed memory. The existing vm_upl_object test hook can already 585*043036a2SApple OSS Distributions * simulate the concurrent retype and in-place clean, so this test is mostly a matter of 586*043036a2SApple OSS Distributions * setting up the file-backed memory correctly. 587*043036a2SApple OSS Distributions */ 588*043036a2SApple OSS Distributions /** 589*043036a2SApple OSS Distributions * This test is meant to exercise functionality that is currently SPTM-specific. 590*043036a2SApple OSS Distributions */ 591*043036a2SApple OSS Distributions if (!is_sptm_enabled()) { 592*043036a2SApple OSS Distributions T_SKIP("Write-fault-on-exec test only supported on SPTM-enabled devices, skipping..."); 593*043036a2SApple OSS Distributions } 594*043036a2SApple OSS Distributions 595*043036a2SApple OSS Distributions if (process_is_translated()) { 596*043036a2SApple OSS Distributions /* TODO: Remove this once rdar://142438840 is fixed. */ 597*043036a2SApple OSS Distributions T_SKIP("Guard exception handling does not work correctly with Rosetta (rdar://142438840), skipping..."); 598*043036a2SApple OSS Distributions } 599*043036a2SApple OSS Distributions 600*043036a2SApple OSS Distributions /* First, generate our backing file. */ 601*043036a2SApple OSS Distributions const size_t buf_size = 10 * PAGE_SIZE; 602*043036a2SApple OSS Distributions unsigned int *buf = mmap(NULL, buf_size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); 603*043036a2SApple OSS Distributions T_QUIET; T_ASSERT_NE_PTR(buf, MAP_FAILED, "map buffer"); 604*043036a2SApple OSS Distributions 605*043036a2SApple OSS Distributions for (unsigned int i = 0; i < (buf_size / sizeof(*buf)); i++) { 606*043036a2SApple OSS Distributions buf[i] = (unsigned int)'a' + i; 607*043036a2SApple OSS Distributions } 608*043036a2SApple OSS Distributions 609*043036a2SApple OSS Distributions ssize_t nbytes; 610*043036a2SApple OSS Distributions int fd; 611*043036a2SApple OSS Distributions char tmp_file_name[PATH_MAX] = "/tmp/vm_upl_data.XXXXXXXX"; 612*043036a2SApple OSS Distributions T_ASSERT_NOTNULL(mktemp(tmp_file_name), "create temporary file name"); 613*043036a2SApple OSS Distributions T_WITH_ERRNO; T_QUIET; T_ASSERT_GE(fd = open(tmp_file_name, O_CREAT | O_RDWR), 614*043036a2SApple OSS Distributions 0, "create temp file"); 615*043036a2SApple OSS Distributions T_WITH_ERRNO; T_QUIET; T_ASSERT_EQ(nbytes = write(fd, buf, buf_size), 616*043036a2SApple OSS Distributions (ssize_t)buf_size, "write %zu bytes", buf_size); 617*043036a2SApple OSS Distributions munmap(buf, buf_size); 618*043036a2SApple OSS Distributions 619*043036a2SApple OSS Distributions /* Map the backing file. */ 620*043036a2SApple OSS Distributions buf = mmap(NULL, buf_size, PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, fd, 0); 621*043036a2SApple OSS Distributions T_QUIET; T_ASSERT_NE_PTR(buf, MAP_FAILED, "map buffer"); 622*043036a2SApple OSS Distributions 623*043036a2SApple OSS Distributions /** 624*043036a2SApple OSS Distributions * Twiddle the mapping permissions between RX and RW. This will mark the mapping as "user debug", 625*043036a2SApple OSS Distributions * which will induce a retype when the backing pages are faulted in. 626*043036a2SApple OSS Distributions */ 627*043036a2SApple OSS Distributions kern_return_t kr = mach_vm_protect(mach_task_self(), (mach_vm_address_t)buf, buf_size, FALSE, VM_PROT_READ | VM_PROT_EXECUTE); 628*043036a2SApple OSS Distributions T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "mach_vm_protect(RX)"); 629*043036a2SApple OSS Distributions 630*043036a2SApple OSS Distributions kr = mach_vm_protect(mach_task_self(), (mach_vm_address_t)buf, buf_size, FALSE, VM_PROT_READ | VM_PROT_WRITE); 631*043036a2SApple OSS Distributions T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "mach_vm_protect(RW)"); 632*043036a2SApple OSS Distributions 633*043036a2SApple OSS Distributions /* Ensure that guard exceptions will not be fatal to the test process. */ 634*043036a2SApple OSS Distributions enable_exc_guard_of_type(GUARD_TYPE_VIRT_MEMORY); 635*043036a2SApple OSS Distributions 636*043036a2SApple OSS Distributions upl_object_test_args args = { .ptr = (uint64_t)buf, .size = buf_size, .upl_rw = false, .should_fail = false, 637*043036a2SApple OSS Distributions .fault_prot = VM_PROT_WRITE | VM_PROT_READ }; 638*043036a2SApple OSS Distributions 639*043036a2SApple OSS Distributions __block int64_t addr = (int64_t)&args; 640*043036a2SApple OSS Distributions __block int64_t result = 0; 641*043036a2SApple OSS Distributions __block size_t s = sizeof(result); 642*043036a2SApple OSS Distributions exc_guard_helper_info_t exc_info; 643*043036a2SApple OSS Distributions bool caught_exception = 644*043036a2SApple OSS Distributions block_raised_exc_guard_of_type(GUARD_TYPE_VIRT_MEMORY, &exc_info, ^{ 645*043036a2SApple OSS Distributions T_ASSERT_POSIX_SUCCESS(sysctlbyname("debug.test.vm_upl_object", &result, &s, &addr, sizeof(addr)), 646*043036a2SApple OSS Distributions "sysctlbyname(debug.test.vm_upl_object)"); 647*043036a2SApple OSS Distributions }); 648*043036a2SApple OSS Distributions if (!process_is_translated()) { 649*043036a2SApple OSS Distributions T_ASSERT_TRUE(caught_exception, "Exec fault should throw guard exception"); 650*043036a2SApple OSS Distributions T_ASSERT_EQ(exc_info.guard_flavor, kGUARD_EXC_SEC_EXEC_ON_IOPL_PAGE, 651*043036a2SApple OSS Distributions "Attempted exec fault throws the expected guard exception flavor"); 652*043036a2SApple OSS Distributions T_ASSERT_EQ(exc_info.catch_count, 1, "Attempted exec fault should throw exactly one guard exception"); 653*043036a2SApple OSS Distributions } 654*043036a2SApple OSS Distributions 655*043036a2SApple OSS Distributions munmap(buf, buf_size); 656*043036a2SApple OSS Distributions close(fd); 657*043036a2SApple OSS Distributions #else 658*043036a2SApple OSS Distributions T_SKIP("Write-fault on exec file requires non-MAP_JIT RWX support"); 659*043036a2SApple OSS Distributions #endif /* TARGET_OS_OSX */ 660*043036a2SApple OSS Distributions } 661*043036a2SApple OSS Distributions 662*043036a2SApple OSS Distributions typedef struct { 663*043036a2SApple OSS Distributions uint64_t ptr; 664*043036a2SApple OSS Distributions uint64_t upl_base; 665*043036a2SApple OSS Distributions uint32_t size; 666*043036a2SApple OSS Distributions uint32_t upl_size; 667*043036a2SApple OSS Distributions bool upl_rw; 668*043036a2SApple OSS Distributions } upl_submap_test_args; 669*043036a2SApple OSS Distributions 670*043036a2SApple OSS Distributions T_DECL(vm_upl_ro_on_submap, 671*043036a2SApple OSS Distributions "Generate RO UPL against a submap region") 672*043036a2SApple OSS Distributions { 673*043036a2SApple OSS Distributions const size_t buf_size = 10 * PAGE_SIZE; 674*043036a2SApple OSS Distributions unsigned int *buf = mmap(NULL, buf_size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); 675*043036a2SApple OSS Distributions T_QUIET; T_ASSERT_NE_PTR(buf, MAP_FAILED, "map buffer"); 676*043036a2SApple OSS Distributions 677*043036a2SApple OSS Distributions for (unsigned int i = 0; i < (buf_size / sizeof(*buf)); i++) { 678*043036a2SApple OSS Distributions buf[i] = (unsigned int)'a' + i; 679*043036a2SApple OSS Distributions } 680*043036a2SApple OSS Distributions 681*043036a2SApple OSS Distributions upl_submap_test_args args = { .ptr = (uint64_t)buf, .size = buf_size, .upl_base = 0x180000000ULL, 682*043036a2SApple OSS Distributions .upl_size = buf_size, .upl_rw = false }; 683*043036a2SApple OSS Distributions 684*043036a2SApple OSS Distributions int64_t addr = (int64_t)&args; 685*043036a2SApple OSS Distributions int64_t result = 0; 686*043036a2SApple OSS Distributions size_t s = sizeof(result); 687*043036a2SApple OSS Distributions T_ASSERT_POSIX_SUCCESS(sysctlbyname("debug.test.vm_upl_submap", &result, &s, &addr, sizeof(addr)), 688*043036a2SApple OSS Distributions "sysctlbyname(debug.test.vm_upl_submap)"); 689*043036a2SApple OSS Distributions 690*043036a2SApple OSS Distributions args.upl_base += 0x800; 691*043036a2SApple OSS Distributions args.upl_size -= 0x1000; 692*043036a2SApple OSS Distributions 693*043036a2SApple OSS Distributions T_ASSERT_POSIX_SUCCESS(sysctlbyname("debug.test.vm_upl_submap", &result, &s, &addr, sizeof(addr)), 694*043036a2SApple OSS Distributions "sysctlbyname(debug.test.vm_upl_submap)"); 695*043036a2SApple OSS Distributions 696*043036a2SApple OSS Distributions args.upl_base += 0x800; 697*043036a2SApple OSS Distributions args.upl_size -= 0x1000; 698*043036a2SApple OSS Distributions 699*043036a2SApple OSS Distributions T_ASSERT_POSIX_SUCCESS(sysctlbyname("debug.test.vm_upl_submap", &result, &s, &addr, sizeof(addr)), 700*043036a2SApple OSS Distributions "sysctlbyname(debug.test.vm_upl_submap)"); 701*043036a2SApple OSS Distributions 702*043036a2SApple OSS Distributions munmap(buf, buf_size); 703*043036a2SApple OSS Distributions } 704*043036a2SApple OSS Distributions 705*043036a2SApple OSS Distributions T_DECL(vm_upl_rw_on_submap, 706*043036a2SApple OSS Distributions "Generate RW UPL against a submap region") 707*043036a2SApple OSS Distributions { 708*043036a2SApple OSS Distributions const size_t buf_size = 10 * PAGE_SIZE; 709*043036a2SApple OSS Distributions unsigned int *buf = mmap(NULL, buf_size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); 710*043036a2SApple OSS Distributions T_QUIET; T_ASSERT_NE_PTR(buf, MAP_FAILED, "map buffer"); 711*043036a2SApple OSS Distributions 712*043036a2SApple OSS Distributions for (unsigned int i = 0; i < (buf_size / sizeof(*buf)); i++) { 713*043036a2SApple OSS Distributions buf[i] = (unsigned int)'a' + i; 714*043036a2SApple OSS Distributions } 715*043036a2SApple OSS Distributions 716*043036a2SApple OSS Distributions upl_submap_test_args args = { .ptr = (uint64_t)buf, .size = buf_size, .upl_base = 0x180000000ULL, 717*043036a2SApple OSS Distributions .upl_size = buf_size, .upl_rw = true }; 718*043036a2SApple OSS Distributions 719*043036a2SApple OSS Distributions int64_t addr = (int64_t)&args; 720*043036a2SApple OSS Distributions int64_t result = 0; 721*043036a2SApple OSS Distributions size_t s = sizeof(result); 722*043036a2SApple OSS Distributions T_ASSERT_POSIX_SUCCESS(sysctlbyname("debug.test.vm_upl_submap", &result, &s, &addr, sizeof(addr)), 723*043036a2SApple OSS Distributions "sysctlbyname(debug.test.vm_upl_submap)"); 724*043036a2SApple OSS Distributions 725*043036a2SApple OSS Distributions args.upl_base += 0x800; 726*043036a2SApple OSS Distributions args.upl_size -= 0x1000; 727*043036a2SApple OSS Distributions 728*043036a2SApple OSS Distributions T_ASSERT_POSIX_SUCCESS(sysctlbyname("debug.test.vm_upl_submap", &result, &s, &addr, sizeof(addr)), 729*043036a2SApple OSS Distributions "sysctlbyname(debug.test.vm_upl_submap"); 730*043036a2SApple OSS Distributions 731*043036a2SApple OSS Distributions args.upl_base += 0x800; 732*043036a2SApple OSS Distributions args.upl_size -= 0x1000; 733*043036a2SApple OSS Distributions 734*043036a2SApple OSS Distributions T_ASSERT_POSIX_SUCCESS(sysctlbyname("debug.test.vm_upl_submap", &result, &s, &addr, sizeof(addr)), 735*043036a2SApple OSS Distributions "sysctlbyname(debug.test.vm_upl_submap)"); 736*043036a2SApple OSS Distributions 737*043036a2SApple OSS Distributions munmap(buf, buf_size); 738*043036a2SApple OSS Distributions } 739