That does indeed answer the question, although I feel like the problem is unsolved and there’s room for innovation here.
It’s not entirely critical that reading or writing in a freed block actually fault, as long as it’s not receiving data from or overwriting pieces of some other object that was allocated into reused address space. If stray pointers lead into phantom backless memory instead of into an immediate error trap, that’s not as good, but it’s still a massive improvement.
With the Mill as it currently is, if I wanted to write a paranoid version of malloc and free, then free would first instruct the cache to discard or zero the object’s lines, then check whether it was freeing the last entry in its page; if not, it’d either shrink or destroy that page table entry and replace it with two new ones on either side. causing a proliferation of page-table entries. This would be cheaper than every object having its own page table entry, but not by very much.
Cache-line granularity is fine, unless the cache lines are wider than I think.
I have two more ideas for dealing with this, which I’m going to continue in email (and maybe post here later) since it may bear on things that are NYF.