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