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