lager
lager is where a program running in ring 3 gets its memory from and where
it gives that memory back. every such program has an 8MB heap of its own,
reserved when it starts and backed by real pages only as it asks for them.
alloc(n) asks lager for n bytes and
dealloc(p) hands a block back, each of them one system call.
a table the program cannot reach
the kernel's own allocator in jenna keeps a small header in front of each block. here that would be a header the program can rewrite, and a forged size would let it write anywhere, kernel memory included. so lager keeps its table of blocks in kernel memory, one per program, and the program only ever sees the addresses of its blocks.
handing out and taking back
a request goes to the first free block that is big enough, split if it is bigger than needed. when nothing fits, the heap grows at the end. memory always comes back zeroed, reused or not. a freed block is merged with free neighbours on both sides, so memory comes back in pieces as large as what was given back.
dealloc only takes a pointer to the start of a block in use. anything
else is refused with a line in the serial log naming the pointer, since a bad free
that went through silently would corrupt something unrelated much later.