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