The idea is that both HeapCellTypes and ThreadLocalCaches will be able to support a security token for allocation. The LocalAllocator will only select a MarkedBlock for allocation of that MarkedBlock's security tokens match the ones requested by both HeapCellType and ThreadLocalCache.
Then we will move MarkedBlock's header to the end. Note that MarkedBlock has a split header: part of the header is inline at the start of the MarkedBlock (these are the fields of MarkedBlock itself) and part of it is out-of-line (these are the fields of MarkedBlock::Handle). This is deliberate. We can move any amount of MarkedBlock::Handle to the end of MarkedBlock. We can move any amount of MarkedBlock itself to the end of the MarkedBlock allocation. This creates between 128 and ~400 bytes of padding at the end.
Then we can make sure that LargeAllocation also leaves a similar amount of bytes of padding at the end.
This means that every bounds check is allowed to overflow by the MarkedBlock "footer" size.
So, in this world, you'll be able to:
1. Specify that your data structs that you're using to hold all of your dangerous ints and unpoisoned pointers has a HeapCellType with a security token that differs from the security tokens of objects directly reachable by the user.
2. Give each JSGlobalObject its own ThreadLocalCache, give each one its own security token, and then context-switch ThreadLocalCaches when switching origins.