I am trying to implement a custom global allocator for use in my Rust application. The goal is to track memory usage and align to 64 bytes. My allocator uses libc::malloc and libc::free for allocation and deallocation. It works fine for small allocations, but when I try to allocate a large Vec<u8> with over 1GB, my program crashes with a segmentation fault. Here is a simplified version of my code:
use core::alloc::{GlobalAlloc, Layout};
use std::ptr::null_mut;
struct TrackingAllocator;
unsafe impl GlobalAlloc for TrackingAllocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
let ptr = libc::malloc(layout.size());
println!("allocating {} bytes at {:?}", layout.size(), ptr);
ptr as *mut u8
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
println!("freeing {} bytes at {:?}", layout.size(), ptr);
libc::free(ptr as *mut libc::c_void);
}
}
#[global_allocator]
static GLOBAL: TrackingAllocator = TrackingAllocator;
fn main() {
let mut big = Vec::<u8>::with_capacity(1_500_000_000);
for i in 0..big.capacity() {
big.push(i as u8);
}
}
When I run this program on Linux x86-64 (Ubuntu 22.04, Rust 1.76.0), it prints the allocation message and then segfaults during the loop. The pointer printed by my allocator appears valid. I suspect my handling of the Layout is wrong, or maybe I need to align the pointer to layout.align() or allocate extra bytes. The documentation for alloc says the implementation must "return a pointer suitable for holding layout.size() bytes aligned to layout.align()", but I'm not sure how to do that with libc::malloc. Do I need to use posix_memalign or adjust the pointer?
How can I correctly implement a global allocator that handles large allocations without crashing? Are there other pitfalls I should be aware of when using GlobalAlloc with very large buffers?
I wrote the TrackingAllocator shown above and set it as the global allocator. My expectation was that allocating a large vector would succeed and I would see the allocation and deallocation messages printed to stdout.
Instead, the program segfaults inside big.push after the initial allocation message, and no deallocation occurs. I tried adjusting the requested capacity and running with RUSTFLAGS='-Z sanitizer=address', but the crash still occurs. I suspect misalignment or incorrect use of Layout, but I'm not sure how to fix it.
println!()inside the global allocator will probably allocate, then lead to an infinite recursion.posix_memaligninstead ofmalloc.