xref: /xnu-12377.41.6/doc/allocators/guard-objects.md (revision bbb1b6f9e71b8cdde6e5cd6f4841f207dee3d828)
1*bbb1b6f9SApple OSS Distributions# Guard-object allocation policy
2*bbb1b6f9SApple OSS Distributions
3*bbb1b6f9SApple OSS DistributionsThe contemporary techniques we use to group and protect smaller allocations,
4*bbb1b6f9SApple OSS Distributionssuch as zalloc and kalloc_type, are premised around type-isolation and VA
5*bbb1b6f9SApple OSS Distributionssequestering. These techniques are impractical for larger allocations because
6*bbb1b6f9SApple OSS Distributionsthey consume outsized chunks of virtual address space, and could lead to
7*bbb1b6f9SApple OSS Distributionsexhaustion. We therefore use a different strategy for larger allocations, which
8*bbb1b6f9SApple OSS Distributionswe call guard objects.
9*bbb1b6f9SApple OSS Distributions
10*bbb1b6f9SApple OSS Distributions
11*bbb1b6f9SApple OSS Distributions## Algorithm
12*bbb1b6f9SApple OSS Distributions
13*bbb1b6f9SApple OSS DistributionsAllocation policies are assigned to particular regions of kernel address space.
14*bbb1b6f9SApple OSS DistributionsThe strategy specified by this document is used by regions such as the various
15*bbb1b6f9SApple OSS Distributionspointer ranges of `kmem_alloc()`, which previously used a first-fit allocator.
16*bbb1b6f9SApple OSS DistributionsUnder the guard-objects policy, the virtual address space is divided in
17*bbb1b6f9SApple OSS Distributions*chunks*. A chunk has a size class of the form $2^k \times \mathtt{PAGE\_SIZE}$,
18*bbb1b6f9SApple OSS Distributionsand is made of $\mathcal{S}$ *slots* of that size. $\mathcal{S}$ varies with the
19*bbb1b6f9SApple OSS Distributionssize class: smaller size-classes will have more slots, and larger size classes
20*bbb1b6f9SApple OSS Distributionswill have fewer. Chunks are also configured to have $\mathcal{G}$ _guards_ and
21*bbb1b6f9SApple OSS Distributionsup to $\mathcal{Q}$ _quarantines_.
22*bbb1b6f9SApple OSS Distributions
23*bbb1b6f9SApple OSS DistributionsChunks maintain several other important pieces of information, such as:
24*bbb1b6f9SApple OSS Distributions
25*bbb1b6f9SApple OSS Distributions* which slots are allocated and which slots are free;
26*bbb1b6f9SApple OSS Distributions* a dynamic count of quarantined slots within $[0, \mathcal{Q})$;
27*bbb1b6f9SApple OSS Distributions* a count of *available* slots, which are the number of free slots in excess
28*bbb1b6f9SApple OSS Distributions  of guards $\mathcal{G}$ and the number of currently quarantined slots.
29*bbb1b6f9SApple OSS Distributions
30*bbb1b6f9SApple OSS DistributionsA chunk can be in three states: *empty* if all its slots are free, *partial* if
31*bbb1b6f9SApple OSS Distributionsit has at least one available slot, *full* if it has no available slots. Chunks
32*bbb1b6f9SApple OSS Distributionsare maintained in lists segregated by size class and state.
33*bbb1b6f9SApple OSS Distributions
34*bbb1b6f9SApple OSS Distributions### Allocating memory
35*bbb1b6f9SApple OSS Distributions
36*bbb1b6f9SApple OSS DistributionsMemory requests for a given size are rounded up to the next size class of the
37*bbb1b6f9SApple OSS Distributionsform $2^k$. The allocator must first select an appropriate chunk. Partial chunks
38*bbb1b6f9SApple OSS Distributionsare preferred, and if no partial chunk exists, an empty chunk is allocated from
39*bbb1b6f9SApple OSS Distributionsunused virtual address space.
40*bbb1b6f9SApple OSS Distributions
41*bbb1b6f9SApple OSS DistributionsA random slot is then chosen from any of the free slots in that chunk, and the
42*bbb1b6f9SApple OSS Distributionsavailable count of the chunk is decremented. If the chunk has now exhausted its
43*bbb1b6f9SApple OSS Distributionsavailable slots — only quarantined slots and guards are left — it's placed on
44*bbb1b6f9SApple OSS Distributionsits corresponding size class's full list.
45*bbb1b6f9SApple OSS Distributions
46*bbb1b6f9SApple OSS DistributionsVisually, let’s consider an example with two partial chunks A and B, with 8
47*bbb1b6f9SApple OSS Distributionsslots each, for $\mathcal{G} = \mathcal{Q} = 2$:
48*bbb1b6f9SApple OSS Distributions
49*bbb1b6f9SApple OSS Distributions```
50*bbb1b6f9SApple OSS Distributions                A───────────────┬─┬─┬─┬─┐     B───────────────┬─┬─┬─┬─┐
51*bbb1b6f9SApple OSS Distributions                │ Available:  1 │X│ │ │X│     │ Available:  4 │ │ │X│ │
52*bbb1b6f9SApple OSS Distributions             ┌─▶│ Quarantine: 0 ├─┼─┼─┼─┤────▶│ Quarantine: 0 ├─┼─┼─┼─┤
53*bbb1b6f9SApple OSS Distributions┌─────────┐  │  │ Guards:     2 │X│X│X│ │     │ Guards:     2 │ │X│ │ │
54*bbb1b6f9SApple OSS Distributions│ partial │──┘  └───────────────┴─┴─┴─┴─┘     └───────────────┴─┴─┴─┴─┘
55*bbb1b6f9SApple OSS Distributions├─────────┤
56*bbb1b6f9SApple OSS Distributions│  full   │
57*bbb1b6f9SApple OSS Distributions└─────────┘
58*bbb1b6f9SApple OSS Distributions
59*bbb1b6f9SApple OSS DistributionsLegend:
60*bbb1b6f9SApple OSS Distributions┌─┐                  ┌─┐
61*bbb1b6f9SApple OSS Distributions│ │ free slot        │X│ allocated slot
62*bbb1b6f9SApple OSS Distributions└─┘                  └─┘
63*bbb1b6f9SApple OSS Distributions```
64*bbb1b6f9SApple OSS Distributions
65*bbb1b6f9SApple OSS DistributionsThe first allocation will be performed from chunk A, using its last available
66*bbb1b6f9SApple OSS Distributionsslot and moving it to the full list:
67*bbb1b6f9SApple OSS Distributions
68*bbb1b6f9SApple OSS Distributions```
69*bbb1b6f9SApple OSS Distributions                B───────────────┬─┬─┬─┬─┐
70*bbb1b6f9SApple OSS Distributions                │ Available:  4 │ │ │X│ │
71*bbb1b6f9SApple OSS Distributions             ┌─▶│ Quarantine: 0 ├─┼─┼─┼─┤
72*bbb1b6f9SApple OSS Distributions┌─────────┐  │  │ Guards:     2 │ │X│ │ │
73*bbb1b6f9SApple OSS Distributions│ partial │──┘  └───────────────┴─┴─┴─┴─┘
74*bbb1b6f9SApple OSS Distributions├─────────┤
75*bbb1b6f9SApple OSS Distributions│  full   │──┐  A───────────────┬─┬─┬─┬─┐
76*bbb1b6f9SApple OSS Distributions└─────────┘  │  │ Available:  0 │X│ │X│X│
77*bbb1b6f9SApple OSS Distributions             └─▶│ Quarantine: 0 ├─┼─┼─┼─┤
78*bbb1b6f9SApple OSS Distributions                │ Guards:     2 │X│X│X│ │
79*bbb1b6f9SApple OSS Distributions                └───────────────┴─┴─┴─┴─┘
80*bbb1b6f9SApple OSS Distributions```
81*bbb1b6f9SApple OSS Distributions
82*bbb1b6f9SApple OSS DistributionsWhen the next allocation request in this size class arrives, the allocator will
83*bbb1b6f9SApple OSS Distributionsselect B because A is now full:
84*bbb1b6f9SApple OSS Distributions
85*bbb1b6f9SApple OSS Distributions```
86*bbb1b6f9SApple OSS Distributions                B───────────────┬─┬─┬─┬─┐
87*bbb1b6f9SApple OSS Distributions                │ Available:  3 │ │ │X│ │
88*bbb1b6f9SApple OSS Distributions             ┌─▶│ Quarantine: 0 ├─┼─┼─┼─┤
89*bbb1b6f9SApple OSS Distributions┌─────────┐  │  │ Guards:     2 │ │X│X│ │
90*bbb1b6f9SApple OSS Distributions│ partial │──┘  └───────────────┴─┴─┴─┴─┘
91*bbb1b6f9SApple OSS Distributions├─────────┤
92*bbb1b6f9SApple OSS Distributions│  full   │──┐  A───────────────┬─┬─┬─┬─┐
93*bbb1b6f9SApple OSS Distributions└─────────┘  │  │ Available:  0 │X│ │X│X│
94*bbb1b6f9SApple OSS Distributions             └─▶│ Quarantine: 0 ├─┼─┼─┼─┤
95*bbb1b6f9SApple OSS Distributions                │ Guards:     2 │X│X│X│ │
96*bbb1b6f9SApple OSS Distributions                └───────────────┴─┴─┴─┴─┘
97*bbb1b6f9SApple OSS Distributions```
98*bbb1b6f9SApple OSS Distributions
99*bbb1b6f9SApple OSS Distributions### Deallocating memory
100*bbb1b6f9SApple OSS Distributions
101*bbb1b6f9SApple OSS DistributionsDeallocating a virtual memory range works in reverse. First, we evaluate which
102*bbb1b6f9SApple OSS Distributionschunk and slot correspond to the range being freed. Since the semantics of the
103*bbb1b6f9SApple OSS Distributionsvirtual memory subsystem mandate that we must support partial deallocations, we
104*bbb1b6f9SApple OSS Distributionsnext consider whether the slot has become only partially free. If so, we have
105*bbb1b6f9SApple OSS Distributionsnothing more to do for now; the slot remains in use.
106*bbb1b6f9SApple OSS Distributions
107*bbb1b6f9SApple OSS DistributionsIf however the slot is now entirely free, then the quarantine count of the chunk
108*bbb1b6f9SApple OSS Distributionsis incremented. If at least $\mathcal{G} + \mathcal{Q}$ are free, then the
109*bbb1b6f9SApple OSS Distributionsquarantine is cleared. The idea behind this policy is that maintaining a good
110*bbb1b6f9SApple OSS Distributionsentropy requires enough free slots to choose from. As a result, once the free
111*bbb1b6f9SApple OSS Distributionsslot count dips below $\mathcal{G} + \mathcal{Q}$, freed slots are quarantined
112*bbb1b6f9SApple OSS Distributionsrather than made immediately available.
113*bbb1b6f9SApple OSS Distributions
114*bbb1b6f9SApple OSS DistributionsFinally, we evaluate whether the chunk needs to be moved:
115*bbb1b6f9SApple OSS Distributions
116*bbb1b6f9SApple OSS Distributions* if a chunk's slots are all fully free, the chunk is marked as empty, and is
117*bbb1b6f9SApple OSS Distributions  typically returned to the system as free space;
118*bbb1b6f9SApple OSS Distributions* if the chunk previously had no slots available, but has any available now,
119*bbb1b6f9SApple OSS Distributions  the chunk is moved to the partially-free chunks list.
120*bbb1b6f9SApple OSS Distributions
121*bbb1b6f9SApple OSS DistributionsVisually, let’s consider an example with a chunk using 16 slots,
122*bbb1b6f9SApple OSS Distributionsunder a configuration in which $\mathcal{G} = \mathcal{Q} = 4$:
123*bbb1b6f9SApple OSS Distributions
124*bbb1b6f9SApple OSS Distributions```
125*bbb1b6f9SApple OSS Distributions                 A───────────────┬─┬─┬─┬─┬─┬─┬─┬─┐
126*bbb1b6f9SApple OSS Distributions┌─────────┐      │ Available:  1 │ │X│X│X│ │ │X│ │
127*bbb1b6f9SApple OSS Distributions│ partial │─────▶│ Quarantine: 1 ├─┼─┼─┼─┼─┼─┼─┼─┤
128*bbb1b6f9SApple OSS Distributions├─────────┤      │ Guards:     4 │ │X│X│X│X│X│ │X│
129*bbb1b6f9SApple OSS Distributions│  full   │      └───────────────┴─┴─┴─┴─┴─┴─┴─┴─┘
130*bbb1b6f9SApple OSS Distributions└─────────┘
131*bbb1b6f9SApple OSS Distributions
132*bbb1b6f9SApple OSS DistributionsLegend:
133*bbb1b6f9SApple OSS Distributions┌─┐                  ┌─┐
134*bbb1b6f9SApple OSS Distributions│ │ free slot        │X│ allocated slot
135*bbb1b6f9SApple OSS Distributions└─┘                  └─┘
136*bbb1b6f9SApple OSS Distributions```
137*bbb1b6f9SApple OSS Distributions
138*bbb1b6f9SApple OSS DistributionsIf we now free an element, its slot is marked free, and the quarantine count
139*bbb1b6f9SApple OSS Distributionsis increased:
140*bbb1b6f9SApple OSS Distributions
141*bbb1b6f9SApple OSS Distributions```
142*bbb1b6f9SApple OSS Distributions                 A───────────────┬─┬─┬─┬─┬─┬─┬─┬─┐
143*bbb1b6f9SApple OSS Distributions┌─────────┐      │ Available:  1 │ │X│X│X│ │ │X│ │
144*bbb1b6f9SApple OSS Distributions│ partial │─────▶│ Quarantine: 2 ├─┼─┼─┼─┼─┼─┼─┼─┤
145*bbb1b6f9SApple OSS Distributions├─────────┤      │ Guards:     4 │ │X│X│ │X│X│ │X│
146*bbb1b6f9SApple OSS Distributions│  full   │      └───────────────┴─┴─┴─┴─┴─┴─┴─┴─┘
147*bbb1b6f9SApple OSS Distributions└─────────┘
148*bbb1b6f9SApple OSS Distributions```
149*bbb1b6f9SApple OSS Distributions
150*bbb1b6f9SApple OSS DistributionsIf we allocate the last available element, a slot is now marked used,
151*bbb1b6f9SApple OSS Distributionsthe available count drops to 0, and causes the chunk to now be full,
152*bbb1b6f9SApple OSS Distributionsand the quarantine stays untouched:
153*bbb1b6f9SApple OSS Distributions
154*bbb1b6f9SApple OSS Distributions```
155*bbb1b6f9SApple OSS Distributions┌─────────┐
156*bbb1b6f9SApple OSS Distributions│ partial │      A───────────────┬─┬─┬─┬─┬─┬─┬─┬─┐
157*bbb1b6f9SApple OSS Distributions├─────────┤      │ Available:  0 │X│X│X│X│ │ │X│ │
158*bbb1b6f9SApple OSS Distributions│  full   │─────▶│ Quarantine: 2 ├─┼─┼─┼─┼─┼─┼─┼─┤
159*bbb1b6f9SApple OSS Distributions└─────────┘      │ Guards:     4 │ │X│X│ │X│X│ │X│
160*bbb1b6f9SApple OSS Distributions                 └───────────────┴─┴─┴─┴─┴─┴─┴─┴─┘
161*bbb1b6f9SApple OSS Distributions```
162*bbb1b6f9SApple OSS Distributions
163*bbb1b6f9SApple OSS DistributionsFreeing just one element would return just one slot, and bump the quarantine
164*bbb1b6f9SApple OSS Distributionscount to 3. It takes freeing two elements for more than $\mathcal{G} +
165*bbb1b6f9SApple OSS Distributions\mathcal{Q}$ slots to be free, leading to clearing the quarantine:
166*bbb1b6f9SApple OSS Distributions
167*bbb1b6f9SApple OSS Distributions```
168*bbb1b6f9SApple OSS Distributions                 A───────────────┬─┬─┬─┬─┬─┬─┬─┬─┐
169*bbb1b6f9SApple OSS Distributions┌─────────┐      │ Available:  4 │X│X│X│X│ │ │X│ │
170*bbb1b6f9SApple OSS Distributions│ partial │─────▶│ Quarantine: 0 ├─┼─┼─┼─┼─┼─┼─┼─┤
171*bbb1b6f9SApple OSS Distributions├─────────┤      │ Guards:     4 │ │X│X│ │ │ │ │X│
172*bbb1b6f9SApple OSS Distributions│  full   │      └───────────────┴─┴─┴─┴─┴─┴─┴─┴─┘
173*bbb1b6f9SApple OSS Distributions└─────────┘
174*bbb1b6f9SApple OSS Distributions```
175*bbb1b6f9SApple OSS Distributions
176*bbb1b6f9SApple OSS Distributions### Operation clamping to a slot
177*bbb1b6f9SApple OSS Distributions
178*bbb1b6f9SApple OSS DistributionsAs long as a VM operation does not exceed slot bounds, any VM call is permitted,
179*bbb1b6f9SApple OSS Distributionswhether it is configuration altering calls such as `vm_protect()` or
180*bbb1b6f9SApple OSS Distributions`vm_inherit()`, taking copies of the range with `mach_make_memory_entry()`, or
181*bbb1b6f9SApple OSS Distributionsreplacing part of the mapping with `vm_allocate(..., VM_FLAGS_FIXED |
182*bbb1b6f9SApple OSS DistributionsVM_FLAGS_OVERWRITE)`.
183*bbb1b6f9SApple OSS Distributions
184*bbb1b6f9SApple OSS DistributionsHowever, operations that cross a slot boundary are not permitted, and lead to
185*bbb1b6f9SApple OSS Distributionstermination. When guard object policies are in effect, allocations are
186*bbb1b6f9SApple OSS Distributionsrandomized, and even in a single threaded context, clients can't assume that
187*bbb1b6f9SApple OSS Distributionsconsecutive allocations will be served in address order, and as a result,
188*bbb1b6f9SApple OSS Distributionsoperations crossing slot boundaries are always bugs.
189*bbb1b6f9SApple OSS Distributions
190*bbb1b6f9SApple OSS Distributions
191*bbb1b6f9SApple OSS Distributions## Security motivation
192*bbb1b6f9SApple OSS Distributions
193*bbb1b6f9SApple OSS DistributionsWith the algorithm explanation, the “guard object” moniker should start to make
194*bbb1b6f9SApple OSS Distributionssense: the goal is that unused free slots are object-sized guards — in the
195*bbb1b6f9SApple OSS Distributionsguard-page sense. Unlike usage of traditional guard pages, which only protect
196*bbb1b6f9SApple OSS Distributionsagainst linear buffer overflows, this scheme also adds a probabilistic
197*bbb1b6f9SApple OSS Distributionsmitigation against use-after-free or non-linear buffer overflows, forcing
198*bbb1b6f9SApple OSS Distributionsattackers to contend with a high probability of hitting these guards.
199*bbb1b6f9SApple OSS Distributions
200*bbb1b6f9SApple OSS DistributionsDue to visible timing differences, it is assumed that an attacker can observe
201*bbb1b6f9SApple OSS Distributionswhen a chunk is being allocated or freed. However, this doesn't give them an
202*bbb1b6f9SApple OSS Distributionsedge because the system maintains a guarantee that at least
203*bbb1b6f9SApple OSS Distributions$\mathcal{G}/\mathcal{S}$ of the address space causes a crash when accessed at
204*bbb1b6f9SApple OSS Distributionsany given time. This is why we can let go of any form of sequestering of the
205*bbb1b6f9SApple OSS Distributionsaddress space for ranges managed with the guard-object allocation policy.
206*bbb1b6f9SApple OSS Distributions
207*bbb1b6f9SApple OSS Distributions### Use-after-Free exploitation strategies and failure rates
208*bbb1b6f9SApple OSS Distributions
209*bbb1b6f9SApple OSS DistributionsAttackers attempting to exploit a use-after-free will be able to cause an
210*bbb1b6f9SApple OSS Distributionselement to be freed, knowing that a dangling pointer to it remains. They will
211*bbb1b6f9SApple OSS Distributionsthen try to replace this freed element with another one of a different type
212*bbb1b6f9SApple OSS Distributionsthat they control, creating a type confusion.
213*bbb1b6f9SApple OSS Distributions
214*bbb1b6f9SApple OSS DistributionsBecause allocating new chunks causes visible timing differences, we can assume
215*bbb1b6f9SApple OSS Distributionsthat attackers are able to fill chunks with all slots corresponding to elements
216*bbb1b6f9SApple OSS Distributionsthat they control. They also know which elements are in the same chunk, but
217*bbb1b6f9SApple OSS Distributionsdon't know which element corresponds to which slot.
218*bbb1b6f9SApple OSS Distributions
219*bbb1b6f9SApple OSS Distributions**In the absence of the quarantine**, the best strategy for attackers trying to
220*bbb1b6f9SApple OSS Distributionsexploit a use-after free is to perform $\mathcal{S} − \mathcal{G}$ rounds
221*bbb1b6f9SApple OSS Distributionsof freeing then reallocating each element in the chunk, where the first free is
222*bbb1b6f9SApple OSS Distributionsto the element they are trying to use-after-free, so that they retain a dangling
223*bbb1b6f9SApple OSS Distributionspointer to the original slot. Each round, the allocator will choose one slot
224*bbb1b6f9SApple OSS Distributionsamong $\mathcal{G} + 1$, when only one corresponds to the slot that was freed by
225*bbb1b6f9SApple OSS Distributionstriggering the bug. The failure rate the attackers face with this strategy is
226*bbb1b6f9SApple OSS Distributions
227*bbb1b6f9SApple OSS Distributions$$failure\_rate = \left(
228*bbb1b6f9SApple OSS Distributions\frac{\mathcal{G}}{\mathcal{G+1}}\right)^{\mathcal{S} - \mathcal{G}}$$
229*bbb1b6f9SApple OSS Distributions
230*bbb1b6f9SApple OSS Distributions* $\mathcal{S} = 8, \mathcal{G} = 2$ yields a 8.8% failure rate;
231*bbb1b6f9SApple OSS Distributions* $\mathcal{S} = 16, \mathcal{G} = 4$ yields a 6.9 failure rate.
232*bbb1b6f9SApple OSS Distributions
233*bbb1b6f9SApple OSS Distributions**Using the quarantine** further reduces an attacker's odds of success. Unlike
234*bbb1b6f9SApple OSS Distributionsbefore, they need to free at least $\mathcal{Q}$ elements before elements are
235*bbb1b6f9SApple OSS Distributionsmade available for allocations again. As a result, a round now needs to be
236*bbb1b6f9SApple OSS Distributions$\mathcal{Q}$ deallocations followed by $\mathcal{Q}$ allocations. As before,
237*bbb1b6f9SApple OSS Distributionsthe very first deallocation is to the element the attacker tries to
238*bbb1b6f9SApple OSS Distributionsuse-after-free. A round gives attackers $\frac{\mathcal{G}}{
239*bbb1b6f9SApple OSS Distributions\mathcal{G}+\mathcal{Q}}$ chances of failure. The aggregate failure rate for
240*bbb1b6f9SApple OSS Distributionsthis strategy is
241*bbb1b6f9SApple OSS Distributions
242*bbb1b6f9SApple OSS Distributions$$failure\_rate =\left( 1 - \frac{(\mathcal{S} - \mathcal{G)} \bmod \mathcal{Q}
243*bbb1b6f9SApple OSS Distributions}{\mathcal{G} + \mathcal{Q}} \right)
244*bbb1b6f9SApple OSS Distributions\left(\frac{\mathcal{G}}{\mathcal{G} + \mathcal{Q}}\right)^{
245*bbb1b6f9SApple OSS Distributions\left\lfloor \frac{\mathcal{S} -\mathcal{G}}{\mathcal{Q}} \right\rfloor}$$
246*bbb1b6f9SApple OSS Distributions
247*bbb1b6f9SApple OSS Distributions* $\mathcal{S}=8, \mathcal{G}=1, \mathcal{Q}=4$ yields a 8% failure rate;
248*bbb1b6f9SApple OSS Distributions* $\mathcal{S}=8, \mathcal{G}=2, \mathcal{Q}=2$ yields a 12.5% failure rate;
249*bbb1b6f9SApple OSS Distributions* $\mathcal{S}=16, \mathcal{G}=4, \mathcal{Q}=3$ yields a 10.7% failure rate;
250*bbb1b6f9SApple OSS Distributions* $\mathcal{S}=16, \mathcal{G}=4, \mathcal{Q}=4$ yields a 12.5% failure rate.
251*bbb1b6f9SApple OSS Distributions
252*bbb1b6f9SApple OSS Distributions### Out-of-bound exploitation strategies
253*bbb1b6f9SApple OSS Distributions
254*bbb1b6f9SApple OSS DistributionsExploiting out-of-bound bugs requires knowing the distance between allocated
255*bbb1b6f9SApple OSS Distributionsobjects, which an attacker a priori doesn’t know without some information leak.
256*bbb1b6f9SApple OSS DistributionsThe regions protected by guard objects are coarsely randomized in the address
257*bbb1b6f9SApple OSS Distributionsspace, so that attackers can’t predict how far an allocation is from other juicy
258*bbb1b6f9SApple OSS Distributionsexploitation targets in the address space such as the `__DATA` segment.  Lastly,
259*bbb1b6f9SApple OSS Distributionsguard objects are combined in XNU with type isolation and allocation fronts. It
260*bbb1b6f9SApple OSS Distributionsmakes cross-type attacks unreliable and unpredictable — as the various buckets
261*bbb1b6f9SApple OSS Distributionsof types are randomized per boot.
262*bbb1b6f9SApple OSS Distributions
263*bbb1b6f9SApple OSS DistributionsThe last remaining avenue of attack targets types that fall in the same
264*bbb1b6f9SApple OSS Distributionsallocation front. However, attackers still have to contend with the uniform
265*bbb1b6f9SApple OSS Distributions$\mathcal{G}/\mathcal{S}$ density of holes, making out-of-bound unreliable with
266*bbb1b6f9SApple OSS Distributionsa probability of failure close to $\mathcal{G}/\mathcal{S}$. This probability of
267*bbb1b6f9SApple OSS Distributionsfailure is typically worse than use-after-free for attackers, and intuitively,
268*bbb1b6f9SApple OSS Distributionsthis is precisely because they need to know more information — the distance
269*bbb1b6f9SApple OSS Distributionsbetween objects — unlike use-after-free where that distance is obviously known
270*bbb1b6f9SApple OSS Distributionsto be zero.
271*bbb1b6f9SApple OSS Distributions
272*bbb1b6f9SApple OSS Distributions### Choice of parameters
273*bbb1b6f9SApple OSS Distributions
274*bbb1b6f9SApple OSS DistributionsIn the actual implementation, $\mathcal{S}$ scales up with the size of the
275*bbb1b6f9SApple OSS Distributionsallocations going up — as a way to limit the amount of metadata needed.
276*bbb1b6f9SApple OSS DistributionsMaintaining the $\mathcal{G}/\mathcal{S}$ and $\mathcal{Q}/\mathcal{S}$ ratios
277*bbb1b6f9SApple OSS Distributionsconstant for any $\mathcal{S}$ allows for all probabilities to become
278*bbb1b6f9SApple OSS Distributionsindependent of $\mathcal{S}$.
279*bbb1b6f9SApple OSS Distributions
280*bbb1b6f9SApple OSS DistributionsOur goal is to make attackers face at least 10% chance of failure — in the
281*bbb1b6f9SApple OSS Distributionsabsence of information disclosure — which is why we chose numbers where
282*bbb1b6f9SApple OSS Distributions$\mathcal{G} = \mathcal{Q} = \mathcal{S}/4$, yielding:
283*bbb1b6f9SApple OSS Distributions
284*bbb1b6f9SApple OSS Distributions* 25% failure rates for out-of-bound exploitation;
285*bbb1b6f9SApple OSS Distributions* 12.5% failure rates for use-after-free exploitation.
286*bbb1b6f9SApple OSS Distributions
287