Open jvoisin opened 6 months ago
Thank you for creating this issue! I though a bit more about this since https://github.com/php/php-src/pull/13943#issuecomment-2062730055:
Longer term, we should check if replacing refcounting+cycle GC by a full tracing GC is practicable, because it would help. Although refcounting can not be entirely removed because CoW semantics rely on it.
SIZE_MAX
won't fix in a GigaCage, sigh.
- mimalloc is pretty neat and performant, and I'd recommend looking at isoalloc as well. I spent some time last year trying to produce some easily digestible mitigation/design comparison between userland allocators which might be relevant here, as well as benchmarking the performances of the different allocators, even gave a small talk on the topic
Great, thank you!
- Unfortunately, having a different base means re-executing the process after the fork, which might significantly impact performances wrt. CoW. It's one of the reasons Android's Zygote doesn't do it. Moreover, I think that the threat model here is "an attacker with (limited) PHP code execution", meaning that ASLR can usually be inferred/ignored in some ways. Randomization applied to freelist would/could help though.
Agreed with changing the base entirely. What I had in mind was to use a random mmap hint in zend alloc, and allocate contiguously from that hint (to avoid splitting the address space too much). After that we can randomize bin placement inside chunks (but I feel this can be easily defeated with heap feng shui) and freelists inside bins indeed.
Regarding the threat model, I'm focusing more on the remote attacker model for now, as I feel this is the most critical.
Agreed with changing the base entirely. What I had in mind was to use a random mmap hint in zend alloc, and allocate contiguously from that hint (to avoid splitting the address space too much). After that we can randomize bin placement inside chunks (but I feel this can be easily defeated with heap feng shui) and freelists inside bins indeed.
Oh, I see. Yes, having a randomized per-child base would help a bit, as an attacker wouldn't be able to use forks to bruteforce the randomization, albeit memory allocated before the fork would still be at the same offset across processes. As for periodic rebasing, I guess having the master process re-executing itself once in a while would be an acceptable hack tradeoff.
Remote PHP exploitation is pretty exotic, to my knowledge, to my knowledge, the only person to do it (publicly) is @cfreal. Local exploitation is much more common, usually to bypass open_basedir
and disable_functions
.
...
- [ ] surround large allocations with guard-pages, as done in partitionAlloc, scudo, … ...
@jvoisin, just curious ; would you recommend using the userfaultfd api in that case ?
@jvoisin, just curious ; would you recommend using the userfaultfd api in that case ?
I'd rather keep things simple and portable: map two pages PROT_NONE
and let the process violently crash in case of violation. I'm under the impression that userfaultfd
adds a lot of complexity, which is never a good thing for security-related features.
Oh not so much complexity it allows to handle the violation more smoothly than the usual technique you re referring to. But ... that s just linux :)
Description
Currently, PHP's heap implementation is ~trivial to exploit:
There are several hardening techniques that could/should be implemented, listed here in order of difficulty:
13943
14339
Disable ZEND_MM_CUSTOM by default: #14570SLAB_FREELIST_RANDOM
allocate strings and array buckets in GigaCages so that a corrupt length doesn't allow to access anything else than other strings/array buckets. This will significantly increase the virtual-memory usage though.this isn't doable since those structures have a maximum size ofSIZE_MAX
cc @arnaud-lb @cfreal @therealcoiffeur