Cute trick to mark parts of a C structure read-only

Recently, I’ve been trying to improve the sorry state of PHP’s heap
implementation, small step by
small step since my free time significantly shrunk this year. Anyway,
one of the low-hanging fruits is
to makes parts of the `_zend_mm_heap` read-only, since it contains function
pointers that are often overwritten in public exploits to transform a (limited)
read/write primitive into an arbitrary code execution.

Changing memory’s mode is done via `mprotect`, but it can only be done on a
per-page page-aligned granularity. The easiest way that came to mind
is to use C11’s anonymous `struct` and `union` along with the `aligned`
attribute to make the structure fit neatly on pages:

“`
struct { union { struct { void* my_important_ptr; size_t my_important_size; }; char padding[PAGE_SIZE]; } ro_data void *my_unimportant_ptr; size_t my_unimportant_size; } my_struct __attribute__((aligned(PAGE_SIZE))); my_struct* init_my_struct() { assert(sizeof(my_struct) > PAGE_SIZE); my_struct* s = (my_struct*)malloc(sizeof(my_struct)); if (!s) { return NULL;; } s->my_unimportant_ptr = NULL; s->my_unimportant_size = 0; s->my_important_ptr = NULL; s->my_important_size = 0; mprotect(s, PAGE_SIZE, PROT_READ); return s; } void set_size(my_struct* s, size_t size) { mprotect(s, PAGE_SIZE, PROT_WRITE); s->my_important_size = size; mprotect(s, PAGE_SIZE, PROT_READ); }
“`

Unfortunately, this isn’t really portable, as `PAGE_SIZE` can’t be known at
compilation-time. The recommended way to get its value is to call `long sz =
sysconf(_SC_PAGESIZE);`.

So what I went for, on the good advice of Arnaud Le Blanc was to go the dynamic route, with two separate structures:

“`
static size_t get_page_size(void) { static size_t page_size = 0; if (!page_size) { page_size = sysconf(_SC_PAGESIZE); if (!page_size) { page_size = 4096; // return a sane-ish default } } return page_size; } #define GET_RO(s) ((my_struct*)((char*)(s) + get_page_size())) struct { void *my_unimportant_ptr; size_t my_unimportant_size; } my_struct; struct { void* my_important_ptr; size_t my_important_size; } ro_data my_struct* init_my_struct() { assert(sizeof(my_struct) my_unimportant_ptr = NULL; s->my_important_size = 0; GET_RO(s)->my_important_ptr = NULL; GET_RO(s)->my_important_size = 0; mprotect(GET_RO(s), get_page_size(), PROT_READ); return s; } void set_size(my_struct* s, size_t size) { mprotect(GET_RO(s), get_page_size(), PROT_WRITE); GET_RO(s)->my_important_size = size; mprotect(GET_RO(s), get_page_size(), PROT_READ); }
“`

The pull-request to implement this in php has a bit of fluff to integrate it with PHP’s memory-management lifecycle, but should still be fairly readable. Unfortunately, it was rejected on the basis of lowering performances by 0.6% on my local benchmark.

Leave a Reply

Your email address will not be published. Required fields are marked *