206 lines
6.6 KiB
Markdown
206 lines
6.6 KiB
Markdown
# Framework for allocating memory in #![no_std] modules.
|
|
|
|
[](https://crates.io/crates/alloc-no-stdlib)
|
|
[](https://travis-ci.org/dropbox/rust-alloc-no-stdlib)
|
|
|
|
|
|
## Requirements
|
|
* Rust 1.6
|
|
|
|
## Documentation
|
|
Currently there is no standard way to allocate memory from within a module that is no_std.
|
|
This provides a mechanism to allocate memory using the stdlib-independent
|
|
memory allocation system described by rust-alloc-no-stdlib
|
|
describe a memory allocation that can be satisfied entirely on
|
|
the stack, by unsafely linking to calloc, or by unsafely referencing a mutable global variable.
|
|
This library currently will leak memory if free_cell isn't specifically invoked on memory.
|
|
|
|
However, if linked by a library that actually can depend on the stdlib then that library
|
|
can simply pass in a few allocators and use the standard Box allocation and will free automatically.
|
|
|
|
This library should also make it possible to entirely jail a rust application that needs dynamic
|
|
allocations by preallocating a maximum limit of data upfront using calloc and
|
|
using seccomp to disallow future syscalls.
|
|
|
|
## Usage
|
|
|
|
There are 3 modes for allocating memory using the stdlib, each with advantages and disadvantages
|
|
|
|
|
|
### On the heap
|
|
This uses the standard Box facilities to allocate memory and assumeds a default constructor
|
|
for the given type
|
|
|
|
```rust
|
|
let mut halloc = StandardAlloc::new(0);
|
|
for _i in 1..10 { // heap test
|
|
let mut x = <StandardAlloc as Allocator<u8>>::alloc_cell(&mut halloc, 100000)
|
|
x[0] = 4;
|
|
let mut y = <StandardAlloc as Allocator<u8>>::alloc_cell(&mut halloc, 100000)
|
|
y[0] = 5;
|
|
let mut z = <StandardAlloc as Allocator<u8>>::alloc_cell(&mut halloc, 100000)
|
|
z[0] = 6;
|
|
assert_eq!(y[0], 5);
|
|
halloc.free_cell(y);
|
|
assert_eq!(x[0], 4);
|
|
assert_eq!(x[9], 0);
|
|
assert_eq!(z[0], 6);
|
|
}
|
|
```
|
|
|
|
### On the heap
|
|
This uses the standard Box facilities to allocate memory but assuming a default user-provided value
|
|
|
|
```rust
|
|
let mut halloc = HeapAlloc::<u8>::new(8);
|
|
for _i in 1..10 { // heap test
|
|
let mut x = halloc.alloc_cell(100000);
|
|
x[0] = 4;
|
|
let mut y = halloc.alloc_cell(110000);
|
|
y[0] = 5;
|
|
let mut z = halloc.alloc_cell(120000);
|
|
z[0] = 6;
|
|
assert_eq!(y[0], 5);
|
|
halloc.free_cell(y);
|
|
assert_eq!(x[0], 4);
|
|
assert_eq!(x[9], 8);
|
|
assert_eq!(z[0], 6);
|
|
}
|
|
```
|
|
|
|
|
|
### On the heap, but uninitialized
|
|
This does allocate data every time it is requested, but it does not allocate the
|
|
memory, so naturally it is unsafe. The caller must initialize the memory properly
|
|
```rust
|
|
let mut halloc = unsafe{HeapAllocUninitialized::<u8>::new()};
|
|
{ // heap test
|
|
let mut x = halloc.alloc_cell(100000);
|
|
x[0] = 4;
|
|
let mut y = halloc.alloc_cell(110000);
|
|
y[0] = 5;
|
|
let mut z = halloc.alloc_cell(120000);
|
|
z[0] = 6;
|
|
assert_eq!(y[0], 5);
|
|
halloc.free_cell(y);
|
|
assert_eq!(x[0], 4);
|
|
assert_eq!(x[9], 0);
|
|
assert_eq!(z[0], 6);
|
|
...
|
|
}
|
|
```
|
|
|
|
|
|
### On the heap in a single pool allocation
|
|
This does a single big allocation on the heap, after which no further usage of the stdlib
|
|
will happen. This can be useful for a jailed application that wishes to restrict syscalls
|
|
at this point
|
|
|
|
```rust
|
|
use alloc_no_stdlib::HeapPrealloc;
|
|
...
|
|
let mut heap_global_buffer = define_allocator_memory_pool!(4096, u8, [0; 6 * 1024 * 1024], heap);
|
|
let mut ags = HeapPrealloc::<u8>::new_allocator(4096, &mut heap_global_buffer, uninitialized);
|
|
{
|
|
let mut x = ags.alloc_cell(9999);
|
|
x.slice_mut()[0] = 4;
|
|
let mut y = ags.alloc_cell(4);
|
|
y[0] = 5;
|
|
ags.free_cell(y);
|
|
|
|
//y.mem[0] = 6; // <-- this is an error (use after free)
|
|
}
|
|
```
|
|
|
|
|
|
|
|
### On the heap, uninitialized
|
|
This does a single big allocation on the heap, after which no further usage of the stdlib
|
|
will happen. This can be useful for a jailed application that wishes to restrict syscalls
|
|
at this point. This option keep does not set the memory to a valid value, so it is
|
|
necessarily marked unsafe
|
|
|
|
```rust
|
|
use alloc_no_stdlib::HeapPrealloc;
|
|
...
|
|
let mut heap_global_buffer = unsafe{HeapPrealloc::<u8>::new_uninitialized_memory_pool(6 * 1024 * 1024)};
|
|
let mut ags = HeapPrealloc::<u8>::new_allocator(4096, &mut heap_global_buffer, uninitialized);
|
|
{
|
|
let mut x = ags.alloc_cell(9999);
|
|
x.slice_mut()[0] = 4;
|
|
let mut y = ags.alloc_cell(4);
|
|
y[0] = 5;
|
|
ags.free_cell(y);
|
|
|
|
//y.mem[0] = 6; // <-- this is an error (use after free)
|
|
}
|
|
```
|
|
|
|
### With calloc
|
|
This is the most efficient way to get a zero'd dynamically sized buffer without the stdlib
|
|
It does invoke the C calloc function and hence must invoke unsafe code.
|
|
In this version, the number of cells are fixed to the parameter specified in the struct definition
|
|
(4096 in this example)
|
|
|
|
```rust
|
|
extern {
|
|
fn calloc(n_elem : usize, el_size : usize) -> *mut u8;
|
|
fn malloc(len : usize) -> *mut u8;
|
|
fn free(item : *mut u8);
|
|
}
|
|
|
|
declare_stack_allocator_struct!(CallocAllocatedFreelist4096, 4096, calloc);
|
|
...
|
|
|
|
// the buffer is defined with 200 megs of zero'd memory from calloc
|
|
let mut calloc_global_buffer = unsafe {define_allocator_memory_pool!(4096, u8, [0; 200 * 1024 * 1024], calloc)};
|
|
// and assigned to a new_allocator
|
|
let mut ags = CallocAllocatedFreelist4096::<u8>::new_allocator(&mut calloc_global_buffer.data, bzero);
|
|
{
|
|
let mut x = ags.alloc_cell(9999);
|
|
x.slice_mut()[0] = 4;
|
|
let mut y = ags.alloc_cell(4);
|
|
y[0] = 5;
|
|
ags.free_cell(y);
|
|
//y.mem[0] = 6; // <-- this is an error (use after free)
|
|
}
|
|
```
|
|
|
|
### With a static, mutable buffer
|
|
If a single buffer of data is needed for the entire span of the application
|
|
Then the simplest way to do so without a zero operation on
|
|
the memory and without using the stdlib is to simply have a global allocated
|
|
structure. Accessing mutable static variables requires unsafe code; however,
|
|
so this code will invoke an unsafe block.
|
|
|
|
|
|
Make sure to only reference global_buffer in a single place, at a single time in the code
|
|
If it is used from two places or at different times, undefined behavior may result,
|
|
since multiple allocators may get access to global_buffer.
|
|
|
|
|
|
```rust
|
|
declare_stack_allocator_struct!(GlobalAllocatedFreelist, 16, global);
|
|
define_allocator_memory_pool!(16, u8, [0; 1024 * 1024 * 100], global, global_buffer);
|
|
|
|
...
|
|
// this references a global buffer
|
|
let mut ags = GlobalAllocatedFreelist::<u8>::new_allocator(bzero);
|
|
unsafe {
|
|
bind_global_buffers_to_allocator!(ags, global_buffer, u8);
|
|
}
|
|
{
|
|
let mut x = ags.alloc_cell(9999);
|
|
x.slice_mut()[0] = 4;
|
|
let mut y = ags.alloc_cell(4);
|
|
y[0] = 5;
|
|
ags.free_cell(y);
|
|
|
|
//y.mem[0] = 6; // <-- this is an error (use after free)
|
|
}
|
|
```
|
|
|
|
|
|
## Contributors
|
|
- Daniel Reiter Horn
|