func alloc(numBytes: usize) -> (ptr: void *, cookie: uword) | Error
func free(cookie: uword, numBytes: usize) -> void
where free() maybe also should take ptr, strictly for validation purposes.A design like this encourages segregation of allocator metadata and the allocated memory, though it is possible to achieve such a design with the classic C malloc/free API.
However, a design like this is even more helpful against use-after-free because cookies can be unique for the lifetime of a program, whereas pointers naturally get reused when a block of memory is reallocated. So the traditional API can never be fully resilient against UAF, whereas an API like this can.
The underlying observation here is that malloc/free couples two different things (access to memory and identifying a previously made allocation) in a way that creates an API which is far less able to mitigate misuse in a safe way. IMO, these functions should be separated in new designs.