1*d4514f0bSApple OSS Distributions# VM API parameter sanitization 2*d4514f0bSApple OSS Distributions 3*d4514f0bSApple OSS DistributionsValidating parameter values passed to virtual memory APIs primarily from user 4*d4514f0bSApple OSS Distributionsspace. 5*d4514f0bSApple OSS Distributions 6*d4514f0bSApple OSS Distributions## Overview 7*d4514f0bSApple OSS Distributions 8*d4514f0bSApple OSS DistributionsVM parameter sanitization aims to eliminate shallow input validation 9*d4514f0bSApple OSS Distributionsbugs like overflows caused by rounding addresses to required page size, 10*d4514f0bSApple OSS Distributionsby providing a set of APIs that can be used to perform consistent, thorough 11*d4514f0bSApple OSS Distributionsmathematical checks on the input. This allows for the rest of the subsystem to 12*d4514f0bSApple OSS Distributionsfreely operate on the input without worrying that future computations may 13*d4514f0bSApple OSS Distributionsoverflow. Note that these APIs are meant to primarily catch issues with 14*d4514f0bSApple OSS Distributionsmathematical computation and are not responsible for checking if the input 15*d4514f0bSApple OSS Distributionsvalue is within certain expected bounds or valid in the context of a specific 16*d4514f0bSApple OSS DistributionsVM API. 17*d4514f0bSApple OSS Distributions 18*d4514f0bSApple OSS Distributions## Semantic types 19*d4514f0bSApple OSS Distributions 20*d4514f0bSApple OSS DistributionsTo enforce that sanitization is performed on input prior to use, 21*d4514f0bSApple OSS Distributionsunsafe input types are encapsulated as opaque types (i.e wrapped inside a 22*d4514f0bSApple OSS Distributionstransparent union) to the internal implementation of the VM APIs. Performing 23*d4514f0bSApple OSS Distributionsmathematical operations on these opaque values without calling the 24*d4514f0bSApple OSS Distributionsrespective sanitization functions (that validates and unwraps them) 25*d4514f0bSApple OSS Distributionswill generate a compiler error. 26*d4514f0bSApple OSS Distributions 27*d4514f0bSApple OSS DistributionsTypes that are typically considered unsafe (i.e require sanitization) include: 28*d4514f0bSApple OSS Distributions- Address/offset for example vm_offset_t and vm_address_t 29*d4514f0bSApple OSS Distributions- Size for example vm_size_t 30*d4514f0bSApple OSS Distributions- Various flags like vm_prot_t and vm_inherit_t 31*d4514f0bSApple OSS Distributions 32*d4514f0bSApple OSS Distributions## Sanitizer functions 33*d4514f0bSApple OSS Distributions 34*d4514f0bSApple OSS DistributionsThe functions that sanitize various types of input values are implemented 35*d4514f0bSApple OSS Distributionsin `vm_sanitize.c` and documented in their corresponding header 36*d4514f0bSApple OSS Distributions`vm_sanitize_internal.h`. 37*d4514f0bSApple OSS Distributions 38*d4514f0bSApple OSS Distributions## VM API boundary 39*d4514f0bSApple OSS Distributions 40*d4514f0bSApple OSS DistributionsVM functions can be called from three places: userspace, kexts, and xnu itself. 41*d4514f0bSApple OSS DistributionsFunctions callable from userspace should be fully sanitized. Functions 42*d4514f0bSApple OSS Distributionscallable from kexts and xnu are less thoroughly covered today. 43*d4514f0bSApple OSS Distributions 44*d4514f0bSApple OSS Distributions## Telemetry and error code compatibility 45*d4514f0bSApple OSS Distributions 46*d4514f0bSApple OSS DistributionsWhen VM parameter sanitization finds a problem, it does the following: 47*d4514f0bSApple OSS Distributions- returns an error to the API's caller 48*d4514f0bSApple OSS Distributions- optionally *rewrites* that error first, either to a different 49*d4514f0bSApple OSS Distributions error code or to `KERN_SUCCESS`. 50*d4514f0bSApple OSS Distributions- optionally *telemeters* that error, sending it to CoreAnalytics and ktriage. 51*d4514f0bSApple OSS Distributions 52*d4514f0bSApple OSS DistributionsThe option to rewrite and/or telemeter is chosen based on the sanitizer 53*d4514f0bSApple OSS Distributionstype and on the identity of the VM API that called the sanitizer. 54*d4514f0bSApple OSS DistributionsThe VM API identity is the `vm_sanitize_caller_t` passed to the sanitizer 55*d4514f0bSApple OSS Distributionsfunction. This identity contains function pointers that override the 56*d4514f0bSApple OSS Distributionsdefault behavior (i.e. no rewrite, no telemetry). The overrides, if any, are set 57*d4514f0bSApple OSS Distributionsby `VM_SANITIZE_DEFINE_CALLER` in `vm_sanitize_error_compat.c`. 58*d4514f0bSApple OSS Distributions 59*d4514f0bSApple OSS DistributionsError code rewrites change the error code to better match historical 60*d4514f0bSApple OSS Distributionsbehavior for binary compatibility purposes. There are two possible rewrites: 61*d4514f0bSApple OSS Distributions1. rewrite an error code to be a different error code. 62*d4514f0bSApple OSS Distributions2. rewrite an error code to be `KERN_SUCCESS`. The VM API returns success 63*d4514f0bSApple OSS Distributions immediately without executing the rest of its implementation. 64*d4514f0bSApple OSS DistributionsNot all changed error codes are (or could be) rewritten. 65*d4514f0bSApple OSS Distributions 66*d4514f0bSApple OSS DistributionsTelemetry similarly may record two cases: 67*d4514f0bSApple OSS Distributions1. The error code being returned differs from its historical value. 68*d4514f0bSApple OSS Distributions2. The error code being returned would be different from its historical 69*d4514f0bSApple OSS Distributions value, but a rewrite has changed it to match the historical value instead. 70*d4514f0bSApple OSS DistributionsNot all changed error codes are (or could be) telemetered. Currently all 71*d4514f0bSApple OSS Distributionsrewrites performed are telemetered. 72*d4514f0bSApple OSS Distributions 73*d4514f0bSApple OSS DistributionsAn outline of the sequence: 74*d4514f0bSApple OSS Distributions1. VM API calls a sanitizer function, passing its own identity in `vms_caller`. 75*d4514f0bSApple OSS Distributions2. `vm_sanitize_<kind>` looks for invalid parameters. 76*d4514f0bSApple OSS Distributions3. If an invalid parameter is found, the sanitizer calls 77*d4514f0bSApple OSS Distributions `vm_sanitize_err_compat_<kind>` to handle any rewrites or telemetry. 78*d4514f0bSApple OSS Distributions4. `vm_sanitize_err_compat_<kind>` looks for an override handler 79*d4514f0bSApple OSS Distributions for that type in the caller's identity, and calls it if present. 80*d4514f0bSApple OSS Distributions5. `vm_sanitize_err_compat_<kind>_<caller>`, the override handler, examines the 81*d4514f0bSApple OSS Distributions parameters and chooses whether to rewrite and/or telemeter this error. 82*d4514f0bSApple OSS Distributions It returns a `vm_sanitize_compat_rewrite_t` containing its decision. 83*d4514f0bSApple OSS Distributions6. `vm_sanitize_err_compat_<kind>` applies any requested error code rewrite 84*d4514f0bSApple OSS Distributions and sends any requested telemetry. 85*d4514f0bSApple OSS Distributions7. The VM API receives the error from the sanitizer and returns it. 86*d4514f0bSApple OSS Distributions 87*d4514f0bSApple OSS DistributionsThere is a complication in step #7: how do the error compat and 88*d4514f0bSApple OSS Distributionsthe sanitizer tell the VM API that it should halt and return `KERN_SUCCESS` 89*d4514f0bSApple OSS Distributionsimmediately, distinct from the sanitizer telling the VM API that 90*d4514f0bSApple OSS Distributionssanitization succeeded and the VM API should proceed normally? 91*d4514f0bSApple OSS DistributionsThe scheme looks like this: 92*d4514f0bSApple OSS Distributions- sanitizer returns `KERN_SUCCESS`: VM API may proceed normally 93*d4514f0bSApple OSS Distributions- sanitizer returns not-`KERN_SUCCESS`: VM API shall return immediately 94*d4514f0bSApple OSS Distributions - sanitizer returns `VM_ERR_RETURN_NOW`: VM API shall return `KERN_SUCCESS` now 95*d4514f0bSApple OSS Distributions - sanitizer returns any other error code: VM API shall return that error now 96*d4514f0bSApple OSS DistributionsThe mapping of `VM_ERR_RETURN_NOW` to `KERN_SUCCESS` is performed by 97*d4514f0bSApple OSS Distributions`vm_sanitize_get_kern_return`. 98*d4514f0bSApple OSS Distributions 99*d4514f0bSApple OSS Distributions## How to: add a new sanitizer or sanitized type 100*d4514f0bSApple OSS Distributions 101*d4514f0bSApple OSS DistributionsWhen a new type needs sanitization, use one of the following macros to declare 102*d4514f0bSApple OSS Distributionsand define the encapsulated opaque version: 103*d4514f0bSApple OSS Distributions- `VM_GENERATE_UNSAFE_ADDR`: Should be used for a new variant that represents 104*d4514f0bSApple OSS Distributions address or offset 105*d4514f0bSApple OSS Distributions- `VM_GENERATE_UNSAFE_SIZE`: Should be used for a new variant that represents 106*d4514f0bSApple OSS Distributions size 107*d4514f0bSApple OSS Distributions- `VM_GENERATE_UNSAFE_TYPE`: Should be used for other types that are not 108*d4514f0bSApple OSS Distributions address or size. For example, this macro is currently used to define the 109*d4514f0bSApple OSS Distributions opaque protections type `vm_prot_ut`. 110*d4514f0bSApple OSS Distributions 111*d4514f0bSApple OSS DistributionsThese opaque types are declared in `vm_types_unsafe.h`. There are also some 112*d4514f0bSApple OSS Distributionsvariants of these macros for specific purposes: 113*d4514f0bSApple OSS Distributions- 32 bit variants like `VM_GENERATE_UNSAFE_ADDR32` should be used for 32bit 114*d4514f0bSApple OSS Distributions variants of address, offset and size. 115*d4514f0bSApple OSS Distributions- BSD variants like `VM_GENERATE_UNSAFE_BSD_ADDR` for types that are 116*d4514f0bSApple OSS Distributions specifically used in the BSD subsystem and not in mach (for example: 117*d4514f0bSApple OSS Distributions caddr_t). 118*d4514f0bSApple OSS Distributions- EXT variants like `VM_GENERATE_UNSAFE_EXT` should not be used directly. They 119*d4514f0bSApple OSS Distributions are intermediate implementation macros. 120*d4514f0bSApple OSS Distributions- `VM_GENERATE_UNSAFE_WRAPPER` is a special macro that is needed to avoid 121*d4514f0bSApple OSS Distributions compiler errors when pointers of opaque types of a specific kind are 122*d4514f0bSApple OSS Distributions interchangeably used as pointer of another opaque type of the same kind for 123*d4514f0bSApple OSS Distributions example: 124*d4514f0bSApple OSS Distributions ``` 125*d4514f0bSApple OSS Distributions mach_vm_offset_ut *offset; 126*d4514f0bSApple OSS Distributions ... 127*d4514f0bSApple OSS Distributions mach_vm_address_ut *ptr = offset; 128*d4514f0bSApple OSS Distributions ``` 129*d4514f0bSApple OSS Distributions These macros define a common opaque type for the entire kind that other 130*d4514f0bSApple OSS Distributions `_ADDR`/`_SIZE` macros redirect to. 131*d4514f0bSApple OSS Distributions ``` 132*d4514f0bSApple OSS Distributions VM_GENERATE_UNSAFE_WRAPPER(uint64_t, vm_addr_struct_t); 133*d4514f0bSApple OSS Distributions ``` 134*d4514f0bSApple OSS Distributions generates the common opaque type for address and offset. All the `_ADDR` 135*d4514f0bSApple OSS Distributions macros define respective opaque types as a typedef of 136*d4514f0bSApple OSS Distributions `vm_addr_struct_t`. 137*d4514f0bSApple OSS Distributions ``` 138*d4514f0bSApple OSS Distributions VM_GENERATE_UNSAFE_ADDR(mach_vm_address_t, mach_vm_address_ut); 139*d4514f0bSApple OSS Distributions ``` 140*d4514f0bSApple OSS Distributions typedefs `mach_vm_address_ut` as a `vm_addr_struct_t`. 141*d4514f0bSApple OSS Distributions 142*d4514f0bSApple OSS Distributions## How to: add sanitization to new VM API 143*d4514f0bSApple OSS Distributions 144*d4514f0bSApple OSS DistributionsOnce the opaque type is available to use, modify the respective 145*d4514f0bSApple OSS Distributionsdeclaration/definition of the entry point to use the opaque types. 146*d4514f0bSApple OSS Distributions 147*d4514f0bSApple OSS Distributions### Opaque types in function prototype 148*d4514f0bSApple OSS Distributions 149*d4514f0bSApple OSS Distributions#### Adoption in MIG 150*d4514f0bSApple OSS Distributions 151*d4514f0bSApple OSS DistributionsFor APIs that are exposed via MIG, adopting the new opaque type in the 152*d4514f0bSApple OSS DistributionsAPI requires some additional steps as we want the opaque types to only appear 153*d4514f0bSApple OSS Distributionsin the kernel headers, leaving the userspace headers unchanged. 154*d4514f0bSApple OSS Distributions- Associate the safe type with its unsafe type using `VM_UNSAFE_TYPE` or 155*d4514f0bSApple OSS Distributions `VM_TYPE_SAFE_UNSAFE` macros. For example: 156*d4514f0bSApple OSS Distributions ``` 157*d4514f0bSApple OSS Distributions type mach_vm_address_t = uint64_t VM_UNSAFE_TYPE(mach_vm_address_ut); 158*d4514f0bSApple OSS Distributions ``` 159*d4514f0bSApple OSS Distributions will cause MIG to use the original type `mach_vm_address_t` in the userspace 160*d4514f0bSApple OSS Distributions headers that are generated by MIG, but overload with the unsafe type 161*d4514f0bSApple OSS Distributions `mach_vm_address_ut` for kernel headers. 162*d4514f0bSApple OSS Distributions Similarly, 163*d4514f0bSApple OSS Distributions ``` 164*d4514f0bSApple OSS Distributions type pointer_t = ^array[] of MACH_MSG_TYPE_BYTE 165*d4514f0bSApple OSS Distributions VM_TYPE_SAFE_UNSAFE(vm_offset_t, pointer_ut); 166*d4514f0bSApple OSS Distributions ``` 167*d4514f0bSApple OSS Distributions replaces `pointer_t` with `vm_offset_t` in userspace headers 168*d4514f0bSApple OSS Distributions and `pointer_ut` in kernel headers. 169*d4514f0bSApple OSS Distributions- Ensure that `VM_KERNEL_SERVER` is defined at the top of the defs file before 170*d4514f0bSApple OSS Distributions any includes. 171*d4514f0bSApple OSS Distributions- Adopt the opaque types in the function definition present in the `.c` file. 172*d4514f0bSApple OSS Distributions ``` 173*d4514f0bSApple OSS Distributions kern_return_t 174*d4514f0bSApple OSS Distributions mach_vm_read( 175*d4514f0bSApple OSS Distributions vm_map_t map, 176*d4514f0bSApple OSS Distributions mach_vm_address_ut addr, 177*d4514f0bSApple OSS Distributions mach_vm_size_ut size, 178*d4514f0bSApple OSS Distributions pointer_ut *data_u, 179*d4514f0bSApple OSS Distributions mach_msg_type_number_t *data_size) 180*d4514f0bSApple OSS Distributions ``` 181*d4514f0bSApple OSS Distributions 182*d4514f0bSApple OSS Distributions#### Adoption in syscalls 183*d4514f0bSApple OSS Distributions 184*d4514f0bSApple OSS Distributions- Ensure that you have created the opaque types needed by the BSD subsystem 185*d4514f0bSApple OSS Distributions using `VM_GENERATE_UNSAFE_BSD_*` in `osfmk/mach/vm_types_unsafe.h`. 186*d4514f0bSApple OSS Distributions- Add the new opaque type to `sys/_types/*` or `bsd/<arm or i386>/types.h`. 187*d4514f0bSApple OSS Distributions `caddr_ut` was added to `bsd/sys/_types/_caddr_t.h` and `user_addr_ut` was 188*d4514f0bSApple OSS Distributions added to `bsd/arm/types.h` and `bsd/i386/types.h`. When adding an opaque for 189*d4514f0bSApple OSS Distributions `caddr_t` you may also need to add opaque types for corresponding types like 190*d4514f0bSApple OSS Distributions `user_addr_t` as the syscall generated use those types. 191*d4514f0bSApple OSS Distributions- Also add the types to `libsyscall/xcodescripts/create-syscalls.pl`. 192*d4514f0bSApple OSS Distributions- Adopt the opaque type in the API in `syscalls.master`. 193*d4514f0bSApple OSS Distributions ``` 194*d4514f0bSApple OSS Distributions 203 AUE_MLOCK ALL { int mlock(caddr_ut addr, size_ut len); } 195*d4514f0bSApple OSS Distributions ``` 196*d4514f0bSApple OSS Distributions `mlock` uses opaque type `caddr_ut` for its address and `size_ut` for its 197*d4514f0bSApple OSS Distributions size. 198*d4514f0bSApple OSS Distributions- Modify `bsd/kern/makesyscalls.sh` to handle the new types added. 199*d4514f0bSApple OSS Distributions 200*d4514f0bSApple OSS Distributions#### Adoption in mach traps 201*d4514f0bSApple OSS Distributions 202*d4514f0bSApple OSS DistributionsFunction prototypes aren't generated automatically for mach traps as is the 203*d4514f0bSApple OSS Distributionscase for syscalls. Therefore we need to modify the mach trap manually to use 204*d4514f0bSApple OSS Distributionsthe opaque type in `osfmk/mach/mach_traps.h`. 205*d4514f0bSApple OSS Distributions``` 206*d4514f0bSApple OSS Distributionsstruct _kernelrpc_mach_vm_deallocate_args { 207*d4514f0bSApple OSS Distributions PAD_ARG_(mach_port_name_t, target); /* 1 word */ 208*d4514f0bSApple OSS Distributions PAD_ARG_(mach_vm_address_ut, address); /* 2 words */ 209*d4514f0bSApple OSS Distributions PAD_ARG_(mach_vm_size_ut, size); /* 2 words */ 210*d4514f0bSApple OSS Distributions}; /* Total: 5 */ 211*d4514f0bSApple OSS Distributionsextern kern_return_t _kernelrpc_mach_vm_deallocate_trap( 212*d4514f0bSApple OSS Distributions struct _kernelrpc_mach_vm_deallocate_args *args); 213*d4514f0bSApple OSS Distributions``` 214*d4514f0bSApple OSS Distributions### Perform sanitization 215*d4514f0bSApple OSS Distributions 216*d4514f0bSApple OSS DistributionsNow that the internal function definitions see the opaque types, we need to 217*d4514f0bSApple OSS Distributionsperform the required sanitization. If multiple entry points call the same 218*d4514f0bSApple OSS Distributionsinternal function, pass along the unsafe value and perform the check at the 219*d4514f0bSApple OSS Distributionsbest choke point further down. For example the best choke point for the 220*d4514f0bSApple OSS Distributionsfollowing APIs was `vm_map_copyin_internal`: 221*d4514f0bSApple OSS Distributions- `mach_vm_read` 222*d4514f0bSApple OSS Distributions- `vm_read` 223*d4514f0bSApple OSS Distributions- `mach_vm_read_list` 224*d4514f0bSApple OSS Distributions- `vm_read_list` 225*d4514f0bSApple OSS Distributions- `vm_map_copyin` 226*d4514f0bSApple OSS Distributions- `mach_vm_read_overwrite` 227*d4514f0bSApple OSS Distributions- `mach_vm_copy` 228*d4514f0bSApple OSS Distributions 229*d4514f0bSApple OSS DistributionsOnce you have determined the right choke point create a 230*d4514f0bSApple OSS Distributions`<function name>_sanitize` function that will sanitize all opaque types and 231*d4514f0bSApple OSS Distributionsreturn their unwrapped safe values. In this function you should call the 232*d4514f0bSApple OSS Distributionssanitization functions provided in `vm_sanitize.c` to validate all opaque 233*d4514f0bSApple OSS Distributionstypes adopted by the API. If you added a new type that doesn't have a 234*d4514f0bSApple OSS Distributionscorresponding sanitization function in `vm_sanitize.c`, please add one. 235*d4514f0bSApple OSS DistributionsFor existing types, try to reuse the functions provided instead of 236*d4514f0bSApple OSS Distributionswriting new ones with specific purposes. `vm_sanitize.c` is meant to 237*d4514f0bSApple OSS Distributionscontain the basic blocks that could be chained to meet your specific 238*d4514f0bSApple OSS Distributionsrequirements. 239*d4514f0bSApple OSS Distributions 240*d4514f0bSApple OSS Distributions#### Adding new functions to `vm_sanitize.c` 241*d4514f0bSApple OSS Distributions 242*d4514f0bSApple OSS Distributions- Mark function with `__attribute__((always_inline, 243*d4514f0bSApple OSS Distributions warn_unused_result))`. 244*d4514f0bSApple OSS Distributions- Ensure that you return safe values on failure for all opaque types that 245*d4514f0bSApple OSS Distributions were supposed to be sanitized by the function. 246*d4514f0bSApple OSS Distributions 247*d4514f0bSApple OSS Distributions### Enforcement 248*d4514f0bSApple OSS Distributions 249*d4514f0bSApple OSS DistributionsFor files outside `osfmk/vm` and `bsd/vm` that need to see the opaque type 250*d4514f0bSApple OSS Distributionsadd the following to their `conf/Makefile.template`: 251*d4514f0bSApple OSS Distributions``` 252*d4514f0bSApple OSS Distributionskern_mman.o_CFLAGS_ADD += -DVM_UNSAFE_TYPES 253*d4514f0bSApple OSS Distributions``` 254*d4514f0bSApple OSS Distributions 255*d4514f0bSApple OSS Distributions## Tests 256*d4514f0bSApple OSS Distributions 257*d4514f0bSApple OSS DistributionsMost VM API callable from userspace or kexts have tests that pass correct and 258*d4514f0bSApple OSS Distributionsincorrect input values, to verify that the functions return the expected error 259*d4514f0bSApple OSS Distributionscodes. These tests run every VM function that has sanitized parameters dozens 260*d4514f0bSApple OSS Distributionsor hundreds or thousands of times. 261*d4514f0bSApple OSS Distributions 262*d4514f0bSApple OSS DistributionsThe code for these tests is: 263*d4514f0bSApple OSS Distributions- `tests/vm/vm_parameter_validation.c` (test `vm_parameter_validation_user` 264*d4514f0bSApple OSS Distributionsfor userspace call sites) 265*d4514f0bSApple OSS Distributions- `osfmk/tests/vm_parameter_validation_kern.c` (test 266*d4514f0bSApple OSS Distributions`vm_parameter_validation_kern` for kernel or kext call sites) 267*d4514f0bSApple OSS Distributions 268*d4514f0bSApple OSS DistributionsThe expected error codes returned by these calls are stored in "golden" result 269*d4514f0bSApple OSS Distributionsfiles. If you change the error code of a VM API, or define a new flag bit that 270*d4514f0bSApple OSS Distributionswas previously unused, you may need to update the golden results. 271*d4514f0bSApple OSS DistributionsSee `tests/vm/vm_parameter_validation.c` for instructions. 272*d4514f0bSApple OSS Distributions 273*d4514f0bSApple OSS DistributionsYou can run these tests locally. See `tests/vm/vm_parameter_validation.c` 274*d4514f0bSApple OSS Distributionsfor instructions. 275*d4514f0bSApple OSS Distributions 276*d4514f0bSApple OSS DistributionsA *trial* is a single VM function called with a single set of argument values. 277*d4514f0bSApple OSS DistributionsFor example, `mach_vm_protect(VM_PROT_READ)` with address=0 and size=0 is a 278*d4514f0bSApple OSS Distributionssingle trial. 279*d4514f0bSApple OSS Distributions 280*d4514f0bSApple OSS DistributionsA *test* is made up of multiple trials: a single VM function called many 281*d4514f0bSApple OSS Distributionstimes with many values for one sanitized parameter (or group of related 282*d4514f0bSApple OSS Distributionsparameters). For example, `mach_vm_protect(VM_PROT_READ)` with many different 283*d4514f0bSApple OSS Distributionspairs of address and size is a single test. `mach_vm_protect` with a single 284*d4514f0bSApple OSS Distributionsvalid address+size and many different `vm_prot_t` values is another test. 285*d4514f0bSApple OSS Distributions 286*d4514f0bSApple OSS DistributionsThe trial values in these tests are generally intended to provoke bugs 287*d4514f0bSApple OSS Distributionsthat the sanitizers are supposed to catch. The list of trial values for 288*d4514f0bSApple OSS Distributionsaddress+size provokes various integer overflows if they are added and/or 289*d4514f0bSApple OSS Distributionsrounded. The list of trial values for flags like `vm_prot_t` includes at 290*d4514f0bSApple OSS Distributionsleast one trial for every possible set bit. The list of trial values for 291*d4514f0bSApple OSS Distributionsa sanitized type or group of types is produced by a "generator". Each 292*d4514f0bSApple OSS Distributionstrial generator is in `osfmk/tests/vm_parameter_validation.h`. 293*d4514f0bSApple OSS Distributions 294*d4514f0bSApple OSS DistributionsA test `harness` or `runner` is the loop that runs a VM function with 295*d4514f0bSApple OSS Distributionsevery trial value, performing any setup necessary and collecting the results. 296*d4514f0bSApple OSS DistributionsThese function names start with `test_`. For example, 297*d4514f0bSApple OSS Distributions`test_mach_with_allocated_vm_prot_t` runs `vm_prot_t` trials of a VM API, 298*d4514f0bSApple OSS Distributionseach time passing it the address and size of a valid allocation and a 299*d4514f0bSApple OSS Distributionsdifferent `vm_prot_t` value. This particular runner is used by some tests of 300*d4514f0bSApple OSS Distributions`mach_vm_protect`, `mach_vm_wire`, and others. 301*d4514f0bSApple OSS Distributions 302*d4514f0bSApple OSS DistributionsThe output of all trials in one test is collected as `results_t`, storing the 303*d4514f0bSApple OSS Distributionsname of the test, the name of each trial, and the error code from each trial. 304*d4514f0bSApple OSS DistributionsThe "error code" is also used for trial outcomes that are not return values 305*d4514f0bSApple OSS Distributionsfrom the VM API. For example, value `PANIC` means the trial was deliberately 306*d4514f0bSApple OSS Distributionsnot executed because if it were it would have panicked and the test machinery 307*d4514f0bSApple OSS Distributionscan't handle that. 308*d4514f0bSApple OSS Distributions 309*d4514f0bSApple OSS DistributionsAfter each test the collected results are processed. Normally this means 310*d4514f0bSApple OSS Distributionscomparing them to the expected results from the golden files. Test results 311*d4514f0bSApple OSS Distributionsmay also be used to generate new golden files. Test results may also be 312*d4514f0bSApple OSS Distributionsdumped to console in their entirety. You can pipe dumped output to 313*d4514f0bSApple OSS Distributions`tools/format_vm_parameter_validation.py`, which knows how to pretty-print 314*d4514f0bSApple OSS Distributionssome things. 315*d4514f0bSApple OSS Distributions 316*d4514f0bSApple OSS DistributionsThese tests are intended to exercise every kernel entry point from userspace 317*d4514f0bSApple OSS Distributionsdirectly, both MIG and syscall, even for functions that have no access via 318*d4514f0bSApple OSS DistributionsLibsystem or that Libsystem intercepts. For MIG entry points we generate our 319*d4514f0bSApple OSS Distributionsown MIG call sites; see `tests/Makefile` for details. For syscall entry points 320*d4514f0bSApple OSS Distributionswe sometimes call a `__function_name` entry point exported by Libsystem that 321*d4514f0bSApple OSS Distributionsis more direct than `function_name` would be. Examples: `__mmap`, `__msync`, 322*d4514f0bSApple OSS Distributions`__msync_nocancel`. 323*d4514f0bSApple OSS Distributions 324*d4514f0bSApple OSS DistributionsThere are two sets of kernel entrypoints that are not exercised by these tests 325*d4514f0bSApple OSS Distributionstoday: 326*d4514f0bSApple OSS Distributions1. the MIG entrypoints that use 32-bit addresses, on platforms other than 327*d4514f0bSApple OSS DistributionswatchOS. These kernels respond to these MIG messages but Libsystem never sends 328*d4514f0bSApple OSS Distributionsthem. We reviewed the vm32 implementations and decided they were safe and 329*d4514f0bSApple OSS Distributionsunlikely to do unsanitary things with the input values before passing them 330*d4514f0bSApple OSS Distributionsto VM API that perform sanitizations. These entrypoints should be disabled 331*d4514f0bSApple OSS Distributions(rdar://124030574). 332*d4514f0bSApple OSS Distributions2. the `kernelrpc` trap alternatives to some MIG entrypoints. We reviewed 333*d4514f0bSApple OSS Distributionsthe trap implementations and decided they were safe and unlikely to do 334*d4514f0bSApple OSS Distributionsunsanitary things with the input values before passing them to VM API that 335*d4514f0bSApple OSS Distributionsperform sanitizations. 336*d4514f0bSApple OSS Distributions 337*d4514f0bSApple OSS Distributions## How to: add a new test 338*d4514f0bSApple OSS Distributions 339*d4514f0bSApple OSS DistributionsYou may need to write new tests in `vm_parameter_validation` if you do 340*d4514f0bSApple OSS Distributionsone of the following: 341*d4514f0bSApple OSS Distributions- write a new VM API function (for userspace or kexts) that has parameters of 342*d4514f0bSApple OSS Distributionssanitized types 343*d4514f0bSApple OSS Distributions- implement sanitization in an existing VM API function for a parameter that 344*d4514f0bSApple OSS Distributionswas not previously sanitized 345*d4514f0bSApple OSS Distributions 346*d4514f0bSApple OSS DistributionsStep 1: are you testing userspace callers (`tests/vm/vm_parameter_validation.c`), 347*d4514f0bSApple OSS Distributionskernel/kext callers (`osfmk/tests/vm_parameter_validation_kern.c`), or both? 348*d4514f0bSApple OSS DistributionsIf you are testing both kernel and userspace you may be able to share much of 349*d4514f0bSApple OSS Distributionsthe implementation in the common file `osfmk/tests/vm_parameter_validation.h`. 350*d4514f0bSApple OSS Distributions 351*d4514f0bSApple OSS DistributionsStep 2: decide what functions you are testing. Each API function with sanitized 352*d4514f0bSApple OSS Distributionsparameters get at least one test. Some functions are divided into multiple 353*d4514f0bSApple OSS Distributionsindependent tests because the function has multiple modes of operation that 354*d4514f0bSApple OSS Distributionsuse different parameter validation paths internally. For example, 355*d4514f0bSApple OSS Distributions`mach_vm_allocate(VM_FLAGS_FIXED)` and `mach_vm_allocate(VM_FLAGS_ANYWHERE)` 356*d4514f0bSApple OSS Distributionseach get their own set of tests as if they were two different functions, 357*d4514f0bSApple OSS Distributionsbecause each handles their `addr/size` parameters differently. 358*d4514f0bSApple OSS Distributions 359*d4514f0bSApple OSS DistributionsStep 3: decide what parameters you are testing. Each sanitized parameter or 360*d4514f0bSApple OSS Distributionsgroup of related parameters gets its own test. For example, `mach_vm_protect` 361*d4514f0bSApple OSS Distributionshas two parameter tests to perform, one for the protection parameter and one 362*d4514f0bSApple OSS Distributionsfor the address and size parameters together. The sanitization of address and 363*d4514f0bSApple OSS Distributionssize are intertwined (we check for overflow of address+size), so they are 364*d4514f0bSApple OSS Distributionstested together. The sanitization of the protection parameter is independent 365*d4514f0bSApple OSS Distributionsof the address and size, so it is tested separately. 366*d4514f0bSApple OSS Distributions 367*d4514f0bSApple OSS DistributionsStep 4: for each parameter or group of parameters, decide what trial values 368*d4514f0bSApple OSS Distributionsshould be tested. The trials should be exhaustive for small values, and 369*d4514f0bSApple OSS Distributionsexercise edge cases and invalid state for large values and interconnected 370*d4514f0bSApple OSS Distributionsvalues. `vm_prot_t` is exhaustive at the bit level (each bit is set in at 371*d4514f0bSApple OSS Distributionsleast one trial) and probes edge cases like `rwx`. Address and size trials 372*d4514f0bSApple OSS Distributionsprobe for overflows when the values are added and/or rounded to page sizes. 373*d4514f0bSApple OSS DistributionsChoose existing trial value generators for your parameters, or write new 374*d4514f0bSApple OSS Distributionsgenerators if you want a new type or different values for an existing type. 375*d4514f0bSApple OSS DistributionsNote that the trial name strings produced by the generator are used by 376*d4514f0bSApple OSS Distributions`tools/format_vm_parameter_validation.py` to pretty-print your output; 377*d4514f0bSApple OSS Distributionsyou may even want to edit that script to recognize new things from your 378*d4514f0bSApple OSS Distributionscode. The trial names are also used in the golden files; each trial 379*d4514f0bSApple OSS Distributionsname must be unique within a single test. 380*d4514f0bSApple OSS Distributions 381*d4514f0bSApple OSS DistributionsStep 5: for each test, decide what setup is necessary for the test or for 382*d4514f0bSApple OSS Distributionseach trial in the test. Choose an existing test running or write a new 383*d4514f0bSApple OSS Distributionsrunner with that setup and those trials. The test runner loops through 384*d4514f0bSApple OSS Distributionsthe trial values produced by the trial generators above, performs the 385*d4514f0bSApple OSS Distributionsrequired setup for the test or for each trial, and calls the function 386*d4514f0bSApple OSS Distributionsto be tested. If there is an existing VM API with similar setup or 387*d4514f0bSApple OSS Distributionssimilar parameters to yours then you can use the same runner or implement 388*d4514f0bSApple OSS Distributionsa variation on that runner. 389*d4514f0bSApple OSS Distributions 390*d4514f0bSApple OSS DistributionsStep 6: if your VM API function has out parameters, test that they are 391*d4514f0bSApple OSS Distributionsmodified or not modified as expected. This is not strictly related to 392*d4514f0bSApple OSS Distributionsparameter sanitization, but the sanitization error paths often have 393*d4514f0bSApple OSS Distributionsinconsistent out parameter handling so these tests are a convenient 394*d4514f0bSApple OSS Distributionsplace to verify the desired behavior. 395*d4514f0bSApple OSS Distributions 396*d4514f0bSApple OSS DistributionsStep 7: call all of your new tests from the top-level test functions 397*d4514f0bSApple OSS Distributions`vm_parameter_validation_kern_test` and `vm_parameter_validation_user`. 398*d4514f0bSApple OSS DistributionsWrap your calls in the same processing and deallocation functions as the 399*d4514f0bSApple OSS Distributionsother tests. You should not need to modify either of them. Note that string 400*d4514f0bSApple OSS Distributionsused to label the test (with the function and parameters being tested) is 401*d4514f0bSApple OSS Distributionsused by the pretty-printing in `tools/format_vm_parameter_validation.py` 402*d4514f0bSApple OSS Distributionsso choose it wisely; you may even want to edit that script to recognize 403*d4514f0bSApple OSS Distributionsnew things from your code. The test name is also recorded in the golden 404*d4514f0bSApple OSS Distributionsfiles; each test name must be unique. 405*d4514f0bSApple OSS Distributions 406*d4514f0bSApple OSS DistributionsStep 8: run your new tests and verify that the patterns of success and 407*d4514f0bSApple OSS Distributionserror are what you want. `tools/format_vm_parameter_validation.py` can 408*d4514f0bSApple OSS Distributionspretty-print some of these outputs which makes them easier to examine. 409*d4514f0bSApple OSS DistributionsMake sure you test the platforms with unusual behavior, such as Intel 410*d4514f0bSApple OSS Distributionsand Rosetta where page sizes are different. See 411*d4514f0bSApple OSS Distributions`tests/vm/vm_parameter_validation.c` for instructions on how to run your 412*d4514f0bSApple OSS Distributionstests in BATS or locally. 413*d4514f0bSApple OSS Distributions 414*d4514f0bSApple OSS DistributionsStep 9: if you are adding sanitization to an existing VM API, decide if 415*d4514f0bSApple OSS Distributionsyou need error code compatibility handling. Run your new test before and 416*d4514f0bSApple OSS Distributionsafter your new sanitization code is in place and compare the output from 417*d4514f0bSApple OSS Distributions`DUMP_RESULTS=1`. If your new sanitization has changed the function's 418*d4514f0bSApple OSS Distributionserror code behavior then you may want to write error code compatibility 419*d4514f0bSApple OSS Distributionsrewrites and/or telemetry for binary compatibility. 420*d4514f0bSApple OSS Distributions 421*d4514f0bSApple OSS DistributionsStep 10: update the "golden" files of expected results. This is done last 422*d4514f0bSApple OSS Distributionswhen you are confident that your sanitization and tests are complete and 423*d4514f0bSApple OSS Distributionsstable. See `tests/vm/vm_parameter_validation.c` for instructions. 424