xref: /relibc/ralloc/README.md (revision 146a5db98f2dcff608e1f3a30330e0cf1527786e)
14b65baa3Sticki# ralloc
26c5a0b16Sticki
36c5a0b16StickiRedox's fast & memory efficient userspace allocator.
44b65baa3Sticki
5fc19fb6dSticki## A note on its state.
6fc19fb6dSticki
7fc19fb6dStickiIt fully works, although it is relatively slow, since it haven't been optimized
8fc19fb6dStickiyet. There is currently no known bugs, but it haven't been carefully reviewed
9fc19fb6dStickiyet, so avoid using it in security critical programs.
10fc19fb6dSticki
11fc19fb6dStickiI consider the state of the code quality very good.
12fc19fb6dSticki
137f66ee45Sticki## Using ralloc
147f66ee45Sticki
157f66ee45StickiAdd ralloc to `Cargo.toml`:
167f66ee45Sticki
177f66ee45Sticki```toml
187f66ee45Sticki[dependencies.ralloc]
197f66ee45Stickigit = "https://github.com/redox-os/ralloc.git"
207f66ee45Sticki```
217f66ee45Sticki
227f66ee45Stickithen import it in your main file:
237f66ee45Sticki
247f66ee45Sticki```rust
257f66ee45Stickiextern crate ralloc;
267f66ee45Sticki```
277f66ee45Sticki
28*146a5db9Stickiralloc is now ready to roll!
297f66ee45Sticki
30fc19fb6dStickiNote that ralloc cannot coexist with another allocator, unless they're deliberately compatible.
31fc19fb6dSticki
324b65baa3Sticki## Features
334b65baa3Sticki
344b65baa3Sticki### Custom out-of-memory handlers
354b65baa3Sticki
364b65baa3StickiYou can set custom OOM handlers, by:
374b65baa3Sticki
384b65baa3Sticki```rust
394b65baa3Stickiextern crate ralloc;
404b65baa3Sticki
414b65baa3Stickifn my_handler() -> ! {
424b65baa3Sticki    println!("Oh no. Blame somebody.");
434b65baa3Sticki}
444b65baa3Sticki
454b65baa3Stickifn main() {
46*146a5db9Sticki    ralloc::lock().set_oom_handler(my_handler);
474b65baa3Sticki    // Do some stuff...
484b65baa3Sticki}
494b65baa3Sticki```
504b65baa3Sticki
514b65baa3Sticki### Debug check: double free
524b65baa3Sticki
53*146a5db9StickiOoh, this one is a cool one. ralloc detects various memory bugs when compiled
54*146a5db9Stickiwith the `debug_tools` feature. These checks include double free checks:
554b65baa3Sticki
564b65baa3Sticki```rust
57*146a5db9Stickiextern crate ralloc;
58*146a5db9Sticki
594b65baa3Stickifn main() {
604b65baa3Sticki    // We start by allocating some stuff.
614b65baa3Sticki    let a = Box::new(500u32);
624b65baa3Sticki    // Then we memcpy the pointer (this is UB).
634b65baa3Sticki    let b = Box::from_raw(&a as *mut u32);
644b65baa3Sticki    // Now both destructors are called. First a, then b, which is a double
654b65baa3Sticki    // free. Luckily, ralloc provides a nice message for you, when in debug
66*146a5db9Sticki    // tools mode:
674b65baa3Sticki    //    Assertion failed: Double free.
684b65baa3Sticki
694b65baa3Sticki    // Setting RUST_BACKTRACE allows you to get a stack backtrace, so that you
704b65baa3Sticki    // can find where the double free occurs.
714b65baa3Sticki}
724b65baa3Sticki```
734b65baa3Sticki
74*146a5db9Sticki### Debug check: memory leaks.
75*146a5db9Sticki
76*146a5db9Stickiralloc got memleak superpowers too! Enable `debug_tools` and do:
77*146a5db9Sticki
78*146a5db9Sticki```rust
79*146a5db9Stickiextern crate ralloc;
80*146a5db9Sticki
81*146a5db9Stickiuse std::mem;
82*146a5db9Sticki
83*146a5db9Stickifn main() {
84*146a5db9Sticki    {
85*146a5db9Sticki        // We start by allocating some stuff.
86*146a5db9Sticki        let a = Box::new(500u32);
87*146a5db9Sticki        // We then leak `a`.
88*146a5db9Sticki        let b = mem::forget(a);
89*146a5db9Sticki    }
90*146a5db9Sticki    // The box is now leaked, and the destructor won't be called.
91*146a5db9Sticki
92*146a5db9Sticki    // To debug this we insert a memory leak check in the end of our programs.
93*146a5db9Sticki    // This will panic if a memory leak is found (and will be a NOOP without
94*146a5db9Sticki    // `debug_tools`).
95*146a5db9Sticki    ralloc::lock().debug_assert_no_leak();
96*146a5db9Sticki}
97*146a5db9Sticki```
98*146a5db9Sticki
994b65baa3Sticki### Partial deallocation
1004b65baa3Sticki
1014b65baa3StickiMany allocators limits deallocations to be allocated block, that is, you cannot
102*146a5db9Stickiperform arithmetics or split it. ralloc does not have such a limitation:
1034b65baa3Sticki
1044b65baa3Sticki```rust
105*146a5db9Stickiextern crate ralloc;
106*146a5db9Sticki
1074b65baa3Stickiuse std::mem;
108*146a5db9Sticki
1094b65baa3Stickifn main() {
1104b65baa3Sticki    // We allocate 200 bytes.
1114b65baa3Sticki    let vec = vec![0u8; 200];
1124b65baa3Sticki    // Cast it to a pointer.
1134b65baa3Sticki    let ptr = vec.as_mut_ptr();
1144b65baa3Sticki
1154b65baa3Sticki    // To avoid UB, we leak the vector.
1164b65baa3Sticki    mem::forget(vec);
1174b65baa3Sticki
1184b65baa3Sticki    // Now, we create two vectors, each being 100 bytes long, effectively
1194b65baa3Sticki    // splitting the original vector in half.
1204b65baa3Sticki    let a = Vec::from_raw_parts(ptr, 100, 100);
1214b65baa3Sticki    let b = Vec::from_raw_parts(ptr.offset(100), 100, 100);
1224b65baa3Sticki
1234b65baa3Sticki    // Now, the destructor of a and b is called... Without a segfault!
1244b65baa3Sticki}
1254b65baa3Sticki```
1264b65baa3Sticki
127*146a5db9Sticki### Separate deallocation
1284b65baa3Sticki
1294b65baa3StickiAnother cool feature is that you can deallocate things that weren't even
1304b65baa3Stickiallocated buffers in the first place!
1314b65baa3Sticki
1324b65baa3StickiConsider that you got a unused static variable, that you want to put into the
1334b65baa3Stickiallocation pool:
1344b65baa3Sticki
1354b65baa3Sticki```rust
1364b65baa3Stickiextern crate ralloc;
1374b65baa3Sticki
1384b65baa3Stickistatic mut BUFFER: [u8; 256] = [2; 256];
1394b65baa3Sticki
1404b65baa3Stickifn main() {
1414b65baa3Sticki    // Throw `BUFFER` into the memory pool.
1424b65baa3Sticki    unsafe {
143*146a5db9Sticki        ralloc::lock().free(&mut BUFFER as *mut u8, 256);
1444b65baa3Sticki    }
1454b65baa3Sticki
1464b65baa3Sticki    // Do some allocation.
1474b65baa3Sticki    assert_eq!(*Box::new(0xDEED), 0xDEED);
1484b65baa3Sticki}
1494b65baa3Sticki```
1504b65baa3Sticki
151*146a5db9Sticki### Top notch security
1524b65baa3Sticki
153*146a5db9StickiIf you are willing to trade a little performance, for extra security you can
154*146a5db9Stickicompile ralloc with the `security` flag. This will, along with other things,
155*146a5db9Stickimake frees zeroing.
1564b65baa3Sticki
157*146a5db9StickiIn other words, an attacker cannot for example inject malicious code or data,
158*146a5db9Stickiwhich can be exploited when forgetting to initialize the data you allocate.
1594b65baa3Sticki
1604b65baa3Sticki### Lock reuse
1614b65baa3Sticki
162*146a5db9StickiAcquiring a lock sequentially multiple times can be expensive. Therefore,
163*146a5db9Stickiralloc allows you to lock the allocator once, and reuse that:
164*146a5db9Sticki
165*146a5db9Sticki```rust
166*146a5db9Stickiextern crate ralloc;
167*146a5db9Sticki
168*146a5db9Stickifn main() {
169*146a5db9Sticki    // Get that lock!
170*146a5db9Sticki    let lock = ralloc::lock();
171*146a5db9Sticki
172*146a5db9Sticki    // All in one:
173*146a5db9Sticki    let _ = lock.alloc(4, 2);
174*146a5db9Sticki    let _ = lock.alloc(4, 2);
175*146a5db9Sticki    let _ = lock.alloc(4, 2);
176*146a5db9Sticki
177*146a5db9Sticki    // It is automatically released through its destructor.
178*146a5db9Sticki}
179*146a5db9Sticki```
180*146a5db9Sticki
181*146a5db9Sticki### Security through the type system
182*146a5db9Sticki
183*146a5db9Stickiralloc makes heavy use of Rust's type system, to make safety guarantees.
184*146a5db9StickiInternally, ralloc has a primitive named `Block`. This is fairly simple,
185*146a5db9Stickidenoting a contagious segment of memory, but what is interesting is how it is
186*146a5db9Stickichecked at compile time to be unique. This is done through the affine type
187*146a5db9Stickisystem.
188*146a5db9Sticki
189*146a5db9StickiThis is just one of many examples.
1904b65baa3Sticki
1914b65baa3Sticki### Platform agnostic
1924b65baa3Sticki
193*146a5db9Stickiralloc is platform independent, with the only requirement of the following symbols:
194*146a5db9Sticki
195*146a5db9Sticki1. `sbrk`: For extending the data segment size.
196*146a5db9Sticki2. `sched_yield`: For the spinlock.
197*146a5db9Sticki3. `memcpy`, `memcmp`, `memset`: Core memory routines.
198*146a5db9Sticki4. `rust_begin_unwind`: For panicking.
199*146a5db9Sticki
200*146a5db9Sticki### Local allocators
201*146a5db9Sticki
202*146a5db9Stickiralloc allows you to create non-global allocators, for e.g. thread specific purposes:
203*146a5db9Sticki
204*146a5db9Sticki```rust
205*146a5db9Stickiextern crate ralloc;
206*146a5db9Sticki
207*146a5db9Stickifn main() {
208*146a5db9Sticki    // We create an allocator.
209*146a5db9Sticki    let my_alloc = ralloc::Allocator::new();
210*146a5db9Sticki
211*146a5db9Sticki    // Allocate some stuff through our local allocator.
212*146a5db9Sticki    let _ = my_alloc.alloc(4, 2);
213*146a5db9Sticki    let _ = my_alloc.alloc(4, 2);
214*146a5db9Sticki    let _ = my_alloc.alloc(4, 2);
215*146a5db9Sticki}
216*146a5db9Sticki```
217*146a5db9Sticki
218*146a5db9Sticki### Safe SBRK
219*146a5db9Sticki
220*146a5db9Stickiralloc provides a `sbrk`, which can be used safely without breaking the allocator.
221