Comment by kentonv
2 days ago
A few years back I patched the memory allocator used by the Cloudflare Workers runtime to overwrite all memory with a static byte pattern on free, so that uninitialized allocations contain nothing interesting.
We expected this to hurt performance, but we were unable to measure any impact in practice.
Everyone still working in memory-unsafe languages should really just do this IMO. It would have mitigated this Mongo bug.
Recent macOS versions zero out memory on free, which improves the efficacy of memory compression. Apparently it’s a net performance gain in the average case
I wonder if Apple Silicon has hardware acceleration for memory zeroing... Knowing Apple, I wouldn't be surprised.
ARM in general does, or at least some modern variants. Various docs for Android and LLVM suggest it's part of the Memory Tagging Extension.
A few years back I patched the memory allocator used by the Cloudflare Workers runtime to overwrite all memory with a static byte pattern on free, so that uninitialized allocations contain nothing interesting.
Note that many malloc implementations will do this for you given an appropriate environment, e.g. setting MALLOC_CONF to opt.junk=free will do this on FreeBSD.
Zeroing memory should absolutely be the default behavior for any generic allocator in 2025.
If you need better performance, write your own allocator optimized for your specific use case — it's not that hard.
Besides, you if you don't need to clear old allocations, there are likely other optimizations you'll be able to find which would never fly in a system allocator.
> OpenBSD uses 0xdb to fill newly allocated memory and 0xdf to fill memory upon being freed. This helps developers catch "use-before-initialization" (seeing 0xdb) and "use-after-free" (seeing 0xdf) bugs quickly.
Looks like this is the default in OpenBSD.
I like this. The only information leaking is whether the memory range was previously used. I suppose you may want to control for that. I'd be surprised if OpenBSD didn't provide a flag to just freed memory to the same value as never allocated.
Is this the same as enabling `init_on_free=1` in the kernel?
You know, I never even considered doing that but it makes sense; whatever overhead that's incurred by doing that static byte pattern is still almost certainly minuscule compared to the overhead of something like a garbage collector.
IMO the tradeoff that is important here is a few microseconds of time sanitizing the memory saves the millions of dollars of headache when memory unsafe languages fail (which happens regularly)
I agree. I almost feel like this should be like a flag in `free`. Like if you pass in 1 or something as a second argument (or maybe a `free_safe` function or something), it will automatically `memset` whatever it's freeing with 0's, and then do the normal freeing.
3 replies →
Non-deterministic latency is a drawback, but garbage collection is not inherently slower than manual memory management/reference counting/etc. Depending on the usage pattern it can be faster. It's a set of trade-offs
FYI, at least in C/C++, the compiler is free to throw away assignments to any memory pointed to by a pointer if said pointer is about to be passed to free(), so depending on how you did this, no perf impact could have been because your compiler removed the assignment. This will even affect a call to memset()
see here: https://godbolt.org/z/rMa8MbYox
I patched the free() implementation itself, not the code that calls free().
I did, of course, test it, and anyway we now run into the "freed memory" pattern regularly when debugging (yes including optimized builds), so it's definitely working.
However, if you recast to volatile, the compiler will keep it:
That code is not guaranteed to work. Declaring memset_v as volatile means that the variable has to be read, but does not imply that the function must be called; the compiler is free to compile the function call as "tmp = memset_v; if (tmp != memset) tmp(...)" relying on its knowledge that in the likely case of equality the call can be optimized away.
25 replies →
Newer versions of C++ (and C, apparently) have functions so that the cast isn't necessary ( https://en.cppreference.com/w/c/string/byte/memset.html ).