xref: /relibc/ralloc/README.md (revision a4bc061a97c3c9cc58af3a28110e9ae3e9102a4b)
14b65baa3Sticki# ralloc
26c5a0b16Sticki
36e3426f3StickiA fast & memory efficient userspace allocator.
46e3426f3Sticki
56e3426f3StickiThis allocator is used as the default Redox.
64b65baa3Sticki
7fc19fb6dSticki## A note on its state.
8fc19fb6dSticki
9fc19fb6dStickiIt fully works, although it is relatively slow, since it haven't been optimized
106e3426f3Stickiyet.
11fc19fb6dSticki
12fc19fb6dStickiI consider the state of the code quality very good.
13fc19fb6dSticki
146e3426f3Sticki## Platforms supported out-of-the-box
156e3426f3Sticki
166e3426f3Sticki- [x] BSD
176e3426f3Sticki- [x] Linux
186e3426f3Sticki- [x] Mac OS X
196e3426f3Sticki- [x] Redox
206e3426f3Sticki- [ ] Windows
216e3426f3Sticki
227f66ee45Sticki## Using ralloc
237f66ee45Sticki
24b7c1f1bfStickiBe sure to use Rust nightly.
25b7c1f1bfSticki
266e3426f3StickiAdd `ralloc` to `Cargo.toml`:
277f66ee45Sticki
287f66ee45Sticki```toml
297f66ee45Sticki[dependencies.ralloc]
307f66ee45Stickigit = "https://github.com/redox-os/ralloc.git"
317f66ee45Sticki```
327f66ee45Sticki
337f66ee45Stickithen import it in your main file:
347f66ee45Sticki
357f66ee45Sticki```rust
367f66ee45Stickiextern crate ralloc;
377f66ee45Sticki```
387f66ee45Sticki
396e3426f3Sticki`ralloc` is now ready to roll!
407f66ee45Sticki
416e3426f3StickiNote that `ralloc` cannot coexist with another allocator, unless they're deliberately compatible.
42fc19fb6dSticki
434b65baa3Sticki## Features
444b65baa3Sticki
45cdec4b0eSticki### Thread-local allocation
46cdec4b0eSticki
47cdec4b0eStickiRalloc makes use of a global-local model allowing one to allocate or deallocate
48cdec4b0eStickiwithout locks, syncronization, or atomic writes. This provides reasonable
49cdec4b0eStickiperformance, while preserving flexibility and ability to multithread.
50cdec4b0eSticki
51b7c1f1bfSticki### First-class debugger (default: valgrind) support
52b7c1f1bfSticki
53b7c1f1bfSticki`ralloc` gives data to two debugger symbols specified in `ralloc_shim`, when
54b7c1f1bfStickithe `debugger` feature is enabled. The default `shim` implementation is wired
55b7c1f1bfStickito `valgrind`, which can thus be used with `ralloc` to detect memory leaks and
56b7c1f1bfStickiuninitialized use out-of-the-box.
57b7c1f1bfSticki
584b65baa3Sticki### Custom out-of-memory handlers
594b65baa3Sticki
604b65baa3StickiYou can set custom OOM handlers, by:
614b65baa3Sticki
624b65baa3Sticki```rust
634b65baa3Stickiextern crate ralloc;
644b65baa3Sticki
654b65baa3Stickifn my_handler() -> ! {
66*a4bc061aSTaylor Cramer    println!("Oh no! You ran out of memory.");
674b65baa3Sticki}
684b65baa3Sticki
694b65baa3Stickifn main() {
706e3426f3Sticki    ralloc::set_oom_handler(my_handler);
714b65baa3Sticki    // Do some stuff...
724b65baa3Sticki}
734b65baa3Sticki```
744b65baa3Sticki
75c8adc1eaSticki### Thread-specific OOM handlers.
76c8adc1eaSticki
77c8adc1eaStickiYou can override the global OOM handler for your current thread. Enable the `thread_oom` feature, and then do:
78c8adc1eaSticki
79c8adc1eaSticki```rust
80c8adc1eaStickiextern crate ralloc;
81c8adc1eaSticki
82c8adc1eaStickifn my_handler() -> ! {
83*a4bc061aSTaylor Cramer    println!("Oh no! You ran out of memory.");
84c8adc1eaSticki}
85c8adc1eaSticki
86c8adc1eaStickifn main() {
87c8adc1eaSticki    ralloc::set_thread_oom_handler(my_handler);
88c8adc1eaSticki    // Do some stuff...
89c8adc1eaSticki}
90c8adc1eaSticki```
91c8adc1eaSticki
924b65baa3Sticki### Partial deallocation
934b65baa3Sticki
944b65baa3StickiMany allocators limits deallocations to be allocated block, that is, you cannot
956e3426f3Stickiperform arithmetics or split it. `ralloc` does not have such a limitation:
964b65baa3Sticki
974b65baa3Sticki```rust
98146a5db9Stickiextern crate ralloc;
99146a5db9Sticki
1004b65baa3Stickiuse std::mem;
101146a5db9Sticki
1024b65baa3Stickifn main() {
1034b65baa3Sticki    // We allocate 200 bytes.
1044b65baa3Sticki    let vec = vec![0u8; 200];
1054b65baa3Sticki    // Cast it to a pointer.
1064b65baa3Sticki    let ptr = vec.as_mut_ptr();
1074b65baa3Sticki
1084b65baa3Sticki    // To avoid UB, we leak the vector.
1094b65baa3Sticki    mem::forget(vec);
1104b65baa3Sticki
1114b65baa3Sticki    // Now, we create two vectors, each being 100 bytes long, effectively
1124b65baa3Sticki    // splitting the original vector in half.
1134b65baa3Sticki    let a = Vec::from_raw_parts(ptr, 100, 100);
1144b65baa3Sticki    let b = Vec::from_raw_parts(ptr.offset(100), 100, 100);
1154b65baa3Sticki
1164b65baa3Sticki    // Now, the destructor of a and b is called... Without a segfault!
1174b65baa3Sticki}
1184b65baa3Sticki```
1194b65baa3Sticki
120146a5db9Sticki### Top notch security
1214b65baa3Sticki
122146a5db9StickiIf you are willing to trade a little performance, for extra security you can
1236e3426f3Stickicompile `ralloc` with the `security` flag. This will, along with other things,
124146a5db9Stickimake frees zeroing.
1254b65baa3Sticki
126146a5db9StickiIn other words, an attacker cannot for example inject malicious code or data,
127146a5db9Stickiwhich can be exploited when forgetting to initialize the data you allocate.
1284b65baa3Sticki
1296e3426f3Sticki### Code verification
1306e3426f3Sticki
1316e3426f3StickiAllocators are extremely security critical.If the same addressis allocated to
1326e3426f3Stickitwo different callers, you risk all sorts of vulnerabilities. For this reason,
1336e3426f3Stickiit is important that the code is reviewed and verified.
1346e3426f3Sticki
1356e3426f3Sticki`ralloc` uses a multi-stage verification model:
1366e3426f3Sticki
1376e3426f3Sticki1. The type checker. A significant part of the verification is done entirely
1386e3426f3Sticki   statically, and enforced through the type checker. We make excessive use of
1396e3426f3Sticki   Rust's safety features and especially affine types.
1406e3426f3Sticki2. Unit testing. `ralloc` has full-coverage unit tests, even for private
1416e3426f3Sticki   interfaces.
1426e3426f3Sticki3. Integration testing suit. `ralloc` uses a form of generative testing, where
1436e3426f3Sticki   tests are "expanded" through a fixed set of functions. This allows
1446e3426f3Sticki   relatively few tests (e.g., a few hundreds of lines) to multiply and become
1456e3426f3Sticki   even more effective.
1466e3426f3Sticki4. Runtime checks. `ralloc` tries to avoid runtime tests, whenever it can, but
1476e3426f3Sticki   that is not always possible. When the security gain is determined to be
1486e3426f3Sticki   significant, and the performance loss is small, we use runtime checks (like
1496e3426f3Sticki   checks for buffer overflows).
1506e3426f3Sticki5. Debug assertions. `ralloc` contains numerous debug assertions, enabled in
1516e3426f3Sticki   debug mode. These allows for very careful testing for things like double
1526e3426f3Sticki   free, memory corruption, as well as leaks and alignment checks.
1536e3426f3Sticki6. Manual reviewing. One or more persons reviews patches to ensure high
1546e3426f3Sticki   security.
1556e3426f3Sticki
156146a5db9Sticki### Security through the type system
157146a5db9Sticki
1586e3426f3Sticki`ralloc` makes heavy use of Rust's type system, to make safety guarantees.
1596e3426f3StickiInternally, `ralloc` has a primitive named `Block`. This is fairly simple,
160146a5db9Stickidenoting a contagious segment of memory, but what is interesting is how it is
161146a5db9Stickichecked at compile time to be unique. This is done through the affine type
162146a5db9Stickisystem.
163146a5db9Sticki
164146a5db9StickiThis is just one of many examples.
1654b65baa3Sticki
1664b65baa3Sticki### Platform agnostic
1674b65baa3Sticki
1686e3426f3Sticki`ralloc` is platform independent. It depends on `ralloc_shim`, a minimal
169c8adc1eaStickiinterface for platform dependent functions. An default implementation of
170c8adc1eaSticki`ralloc_shim` is provided (supporting Mac OS, Linux, and BSD).
171146a5db9Sticki
172cdec4b0eSticki### Forcing inplace reallocation
173146a5db9Sticki
174cdec4b0eStickiInplace reallocation can be significantly faster than memcpy'ing reallocation.
175cdec4b0eStickiA limitation of libc is that you cannot do reallocation inplace-only (a
176cdec4b0eStickifailable method that guarantees the absence of memcpy of the buffer).
177cdec4b0eSticki
178cdec4b0eStickiHaving a failable way to do inplace reallocation provides some interesting possibilities.
179146a5db9Sticki
180146a5db9Sticki```rust
181146a5db9Stickiextern crate ralloc;
182146a5db9Sticki
183146a5db9Stickifn main() {
184cdec4b0eSticki    let buf = ralloc::alloc(40, 1);
185cdec4b0eSticki    // BRK'ing 20 bytes...
186cdec4b0eSticki    let ptr = unsafe { ralloc::inplace_realloc(buf, 40, 45).unwrap() };
187146a5db9Sticki
188cdec4b0eSticki    // The buffer is now 45 bytes long!
189146a5db9Sticki}
190146a5db9Sticki```
191146a5db9Sticki
192146a5db9Sticki### Safe SBRK
193146a5db9Sticki
1946e3426f3Sticki`ralloc` provides a `sbrk`, which can be used safely without breaking the allocator:
1956e3426f3Sticki
1966e3426f3Sticki```rust
1976e3426f3Stickiextern crate ralloc;
1986e3426f3Sticki
1996e3426f3Stickifn main() {
2006e3426f3Sticki    // BRK'ing 20 bytes...
2016e3426f3Sticki    let ptr = unsafe { ralloc::sbrk(20) };
2026e3426f3Sticki}
2036e3426f3Sticki```
20466cbc420Sticki
20566cbc420Sticki### Logging
20666cbc420Sticki
207c8adc1eaStickiIf you enable the `log` feature, you get detailed logging of the allocator, e.g.
20866cbc420Sticki
20966cbc420Sticki```
21066cbc420Sticki|   : BRK'ing a block of size, 80, and alignment 8.            (at bookkeeper.rs:458)
21166cbc420Sticki|   : Pushing 0x5578dacb2000[0x0] and 0x5578dacb2050[0xffb8].  (at bookkeeper.rs:490)
21266cbc420Sticki|x  : Freeing 0x1[0x0].                                        (at bookkeeper.rs:409)
21366cbc420Stickix|  : BRK'ing a block of size, 4, and alignment 1.             (at bookkeeper.rs:458)
21466cbc420Stickix|  : Pushing 0x5578dacc2008[0x0] and 0x5578dacc200c[0xfffd].  (at bookkeeper.rs:490)
21566cbc420Stickix|x : Reallocating 0x5578dacc2008[0x4] to size 8 with align 1. (at bookkeeper.rs:272)
21666cbc420Stickix|x : Inplace reallocating 0x5578dacc2008[0x4] to size 8.      (at bookkeeper.rs:354)
21766cbc420Sticki_|x : Freeing 0x5578dacb2058[0xffb0].                          (at bookkeeper.rs:409)
21866cbc420Sticki_|x : Inserting block 0x5578dacb2058[0xffb0].                  (at bookkeeper.rs:635)
21966cbc420Sticki```
22066cbc420Sticki
22166cbc420StickiTo the left, you can see the state of the block pool. `x` denotes a non-empty
22266cbc420Stickiblock, `_` denotes an empty block, and `|` denotes the cursor.
22366cbc420Sticki
22466cbc420StickiThe `a[b]` is a syntax for block on address `a` with size `b`.
2256e3426f3Sticki
2266e3426f3Sticki### Useless alignments
2276e3426f3Sticki
2286e3426f3StickiAlignments doesn't have to be a power of two.
2296e3426f3Sticki
2306e3426f3Sticki## Planned features
2316e3426f3Sticki
2326e3426f3Sticki### Failable allocations
2336e3426f3Sticki
2346e3426f3StickiOften you are interested in handling OOM on a case-by-case basis. This is
2356e3426f3Stickiespecially true when dealing with very big allocation.
2366e3426f3Sticki
2376e3426f3Sticki`ralloc` allows that:
2386e3426f3Sticki
2396e3426f3Sticki```rust
2406e3426f3Stickiextern crate ralloc;
2416e3426f3Sticki
2426e3426f3Stickifn main() {
243cdec4b0eSticki    let buf = ralloc::try_alloc(8, 4);
2446e3426f3Sticki    // `buf` is a Result: It is Err(()) if the allocation failed.
2456e3426f3Sticki}
2466e3426f3Sticki```
247