xref: /xnu-11215.81.4/doc/allocators/read-only.md (revision d4514f0bc1d3f944c22d92e68b646ac3fb40d452)
1*d4514f0bSApple OSS Distributions# The Read-Only Allocator
2*d4514f0bSApple OSS Distributions
3*d4514f0bSApple OSS DistributionsAllocating read-only data in xnu.
4*d4514f0bSApple OSS Distributions
5*d4514f0bSApple OSS Distributions## Introduction
6*d4514f0bSApple OSS Distributions
7*d4514f0bSApple OSS DistributionsThe Read-Only Allocator is an extension of the zone allocator that facilitates
8*d4514f0bSApple OSS Distributions"read-only" allocations.  Data allocated from a read-only zone can only be
9*d4514f0bSApple OSS Distributionsmodified programmatically through the `zalloc_ro_mut` function.
10*d4514f0bSApple OSS Distributions
11*d4514f0bSApple OSS DistributionsRead-only zones are intended for very specific use cases where the data being
12*d4514f0bSApple OSS Distributionsmanaged directly affects runtime security decisions.
13*d4514f0bSApple OSS Distributions
14*d4514f0bSApple OSS Distributions## Discussion
15*d4514f0bSApple OSS Distributions
16*d4514f0bSApple OSS DistributionsThe purpose of the Read-Only Allocator is to protect security-
17*d4514f0bSApple OSS Distributionssensitive data from being targeted by memory corruption vulnerabilities.
18*d4514f0bSApple OSS Distributions
19*d4514f0bSApple OSS DistributionsWhile, historically, the modus operandi for an advanced attacker is to seize
20*d4514f0bSApple OSS Distributionscontrol of kernel execution, advances in control flow integrity defenses, such
21*d4514f0bSApple OSS Distributionsas PAC, means that today's attacker favors data-only attacks to achieve
22*d4514f0bSApple OSS Distributionscompromise.  Typically this involves using a controlled write primitive to
23*d4514f0bSApple OSS Distributionstarget data structures in the kernel's memory that effectively disables or
24*d4514f0bSApple OSS Distributionsbypasses obstacles standing in the way of the desired data.
25*d4514f0bSApple OSS Distributions
26*d4514f0bSApple OSS DistributionsBy necessity, we store lots of data on the heap that informs the various
27*d4514f0bSApple OSS Distributionssecurity mechanisms on our platforms.  The heap traditionally dispenses
28*d4514f0bSApple OSS Distributionsdirectly mutable allocations because this fits what we need the memory for:
29*d4514f0bSApple OSS Distributionsfrequent, fast and easy read/write access to memory.  Unfortunately, these are
30*d4514f0bSApple OSS Distributionsalso the requirements for an attacker looking to exploit a controllable write
31*d4514f0bSApple OSS Distributionsinto kernel memory.
32*d4514f0bSApple OSS Distributions
33*d4514f0bSApple OSS DistributionsFor globals, `SECURITY_READ_ONLY_(EARLY|LATE)` provides an elegant protection
34*d4514f0bSApple OSS Distributionsmechanism, but unfortunately that doesn't cater for dynamic runtime
35*d4514f0bSApple OSS Distributionsallocations.
36*d4514f0bSApple OSS Distributions
37*d4514f0bSApple OSS DistributionsThis is where the Read-Only Allocator provides its defense: we observe that
38*d4514f0bSApple OSS Distributionsthe majority of security-sensitive data that we allocate on the heap tends to
39*d4514f0bSApple OSS Distributionsbe written into memory once and seldom changed thereafter.  We can therefore
40*d4514f0bSApple OSS Distributionstrade some of this ease of access in exchange for stronger guarantees on the
41*d4514f0bSApple OSS Distributionsintegrity of the data.
42*d4514f0bSApple OSS Distributions
43*d4514f0bSApple OSS DistributionsData under the control of the Read-Only Allocator can be read from just as
44*d4514f0bSApple OSS Distributionscheaply and easily as other data, but writing to it must be done through the
45*d4514f0bSApple OSS Distributionsrelatively expensive `zalloc_ro_mut` function.  By insisting that data be
46*d4514f0bSApple OSS Distributionswritten programmatically (i.e. through calling a function), we raise the cost
47*d4514f0bSApple OSS Distributionsof targeting that data towards the cost of seizing control of kernel
48*d4514f0bSApple OSS Distributionsexecution.
49*d4514f0bSApple OSS Distributions
50*d4514f0bSApple OSS Distributions
51*d4514f0bSApple OSS Distributions## Data Structure Strategies
52*d4514f0bSApple OSS Distributions
53*d4514f0bSApple OSS DistributionsTo make best use of the Read-Only Allocator, some simple advice should be
54*d4514f0bSApple OSS Distributionsfollowed:
55*d4514f0bSApple OSS Distributions
56*d4514f0bSApple OSS Distributions1. Pointers to read-only elements should either reside in read-only memory
57*d4514f0bSApple OSS Distributions   themselves, or be protected by PAC.
58*d4514f0bSApple OSS Distributions2. Where there is a 1:1 mapping between read/write and read-only elements, the
59*d4514f0bSApple OSS Distributions   read-only element should include a pointer back to the read/write side (a
60*d4514f0bSApple OSS Distributions   "back reference") that is validated when traversing from read/write to
61*d4514f0bSApple OSS Distributions   read-only.
62*d4514f0bSApple OSS Distributions
63*d4514f0bSApple OSS DistributionsOn Point 1: data structures are typically stored through chains of pointers --
64*d4514f0bSApple OSS Distributionse.g. a thread points to its task, which points to its proc, which points to
65*d4514f0bSApple OSS Distributionsits credential.  The principle here is to ensure the integrity of the entire
66*d4514f0bSApple OSS Distributionschain from source pointer (e.g. thread) to destination data (e.g. credential).
67*d4514f0bSApple OSS Distributions
68*d4514f0bSApple OSS DistributionsOn Point 2: by storing a back reference on the read-only side of 1:1
69*d4514f0bSApple OSS Distributionsrelationships, we can validate the ownership invariant that we expect to hold.
70*d4514f0bSApple OSS DistributionsIf this is violated, it suggests that a use-after-free has happened -- perhaps
71*d4514f0bSApple OSS Distributionsthrough a genuine bug, or perhaps by an attacker targeting the zone allocator
72*d4514f0bSApple OSS Distributionsitself.
73*d4514f0bSApple OSS Distributions
74*d4514f0bSApple OSS Distributions## Should I Use the Read-Only Allocator?
75*d4514f0bSApple OSS Distributions
76*d4514f0bSApple OSS DistributionsThe Read-Only Allocator is intended to protect data from very specific
77*d4514f0bSApple OSS Distributionsthreats.  This means that for most data, it simply doesn't make sense to use
78*d4514f0bSApple OSS Distributionsit.  Its use is primarily geared toward allocations supporting security
79*d4514f0bSApple OSS Distributionsboundaries such as labels, sandboxing, audit tokens, etc.
80*d4514f0bSApple OSS Distributions
81*d4514f0bSApple OSS Distributions
82*d4514f0bSApple OSS Distributions## API
83*d4514f0bSApple OSS Distributions
84*d4514f0bSApple OSS DistributionsRead-only zones cannot be created after lockdown.  To create a new read-only
85*d4514f0bSApple OSS Distributionszone, a new identifier must be added to the `zone_reserved_id_t` enumeration
86*d4514f0bSApple OSS Distributionsand it must be created by passing `ZC_READONLY` through either `ZONE_INIT` or
87*d4514f0bSApple OSS Distributions`zone_create_ext`.
88*d4514f0bSApple OSS Distributions
89*d4514f0bSApple OSS DistributionsWe require identifiers for read-only zones for two reasons: firstly to ensure
90*d4514f0bSApple OSS Distributionsthat we're making conscious, considered choices over which zones are made
91*d4514f0bSApple OSS Distributionsread-only, and secondly to allow for more stringent validation at the API
92*d4514f0bSApple OSS Distributionsboundary.
93*d4514f0bSApple OSS Distributions
94*d4514f0bSApple OSS DistributionsOnce a read-only zone is created, the API for using it is small and simple.
95*d4514f0bSApple OSS DistributionsThe key functions are:
96*d4514f0bSApple OSS Distributions
97*d4514f0bSApple OSS Distributions- `zalloc_ro`: Allocate an element from a read-only zone.
98*d4514f0bSApple OSS Distributions- `zfree_ro`: Free an element back to a read-only zone.  Note that this is a
99*d4514f0bSApple OSS Distributions  macro that automatically zeroes the pointer after freeing.
100*d4514f0bSApple OSS Distributions- `zone_require_ro`: Verify that an element belongs to a given read-only zone
101*d4514f0bSApple OSS Distributions  and panic if it doesn't.
102*d4514f0bSApple OSS Distributions- `zalloc_ro_mut`: Modify part of an element allocated from a read-only zone.
103*d4514f0bSApple OSS Distributions  Think of this as a special `memcpy` to write into your elements.
104*d4514f0bSApple OSS Distributions- `zalloc_ro_update_elem`: A convenience function for calling `zalloc_ro_mut`
105*d4514f0bSApple OSS Distributions  over the entirety of an element: simply passes an offset of zero and size
106*d4514f0bSApple OSS Distributions  equal to the size of the elements in the zone.
107*d4514f0bSApple OSS Distributions
108*d4514f0bSApple OSS DistributionsNote that `zfree_ro`, `zalloc_ro_mut` and `zalloc_ro_update_elem` will
109*d4514f0bSApple OSS Distributionsperform a `zone_require_ro` on the element themselves; there's no need to do
110*d4514f0bSApple OSS Distributionsthis manually beforehand.
111