|
| 1 | +# Framework for allocating memory in #![no_std] modules. |
| 2 | + |
| 3 | +[](https://crates.io/crates/alloc-no-stdlib) |
| 4 | +[](https://travis-ci.org/dropbox/rust-alloc-no-stdlib) |
| 5 | + |
| 6 | + |
| 7 | +## Requirements |
| 8 | + * Rust 1.6 |
| 9 | + |
| 10 | +## Documentation |
| 11 | +Currently there is no standard way to allocate memory from within a module that is no_std. |
| 12 | +This provides a mechanism to allocate memory using the stdlib-independent |
| 13 | +memory allocation system described by rust-alloc-no-stdlib |
| 14 | +describe a memory allocation that can be satisfied entirely on |
| 15 | +the stack, by unsafely linking to calloc, or by unsafely referencing a mutable global variable. |
| 16 | +This library currently will leak memory if free_cell isn't specifically invoked on memory. |
| 17 | + |
| 18 | +However, if linked by a library that actually can depend on the stdlib then that library |
| 19 | +can simply pass in a few allocators and use the standard Box allocation and will free automatically. |
| 20 | + |
| 21 | +This library should also make it possible to entirely jail a rust application that needs dynamic |
| 22 | +allocations by preallocating a maximum limit of data upfront using calloc and |
| 23 | +using seccomp to disallow future syscalls. |
| 24 | + |
| 25 | +## Usage |
| 26 | + |
| 27 | +There are 3 modes for allocating memory using the stdlib, each with advantages and disadvantages |
| 28 | + |
| 29 | + |
| 30 | +### On the heap |
| 31 | +This uses the standard Box facilities to allocate memory and assumeds a default constructor |
| 32 | +for the given type |
| 33 | + |
| 34 | +```rust |
| 35 | +let mut halloc = StandardAlloc::new(0); |
| 36 | +for _i in 1..10 { // heap test |
| 37 | + let mut x = <StandardAlloc as Allocator<u8>>::alloc_cell(&mut halloc, 100000) |
| 38 | + x[0] = 4; |
| 39 | + let mut y = <StandardAlloc as Allocator<u8>>::alloc_cell(&mut halloc, 100000) |
| 40 | + y[0] = 5; |
| 41 | + let mut z = <StandardAlloc as Allocator<u8>>::alloc_cell(&mut halloc, 100000) |
| 42 | + z[0] = 6; |
| 43 | + assert_eq!(y[0], 5); |
| 44 | + halloc.free_cell(y); |
| 45 | + assert_eq!(x[0], 4); |
| 46 | + assert_eq!(x[9], 0); |
| 47 | + assert_eq!(z[0], 6); |
| 48 | +} |
| 49 | +``` |
| 50 | + |
| 51 | +### On the heap |
| 52 | +This uses the standard Box facilities to allocate memory but assuming a default user-provided value |
| 53 | + |
| 54 | +```rust |
| 55 | +let mut halloc = HeapAlloc::<u8>::new(8); |
| 56 | +for _i in 1..10 { // heap test |
| 57 | + let mut x = halloc.alloc_cell(100000); |
| 58 | + x[0] = 4; |
| 59 | + let mut y = halloc.alloc_cell(110000); |
| 60 | + y[0] = 5; |
| 61 | + let mut z = halloc.alloc_cell(120000); |
| 62 | + z[0] = 6; |
| 63 | + assert_eq!(y[0], 5); |
| 64 | + halloc.free_cell(y); |
| 65 | + assert_eq!(x[0], 4); |
| 66 | + assert_eq!(x[9], 8); |
| 67 | + assert_eq!(z[0], 6); |
| 68 | +} |
| 69 | +``` |
| 70 | + |
| 71 | + |
| 72 | +### On the heap, but uninitialized |
| 73 | +This does allocate data every time it is requested, but it does not allocate the |
| 74 | +memory, so naturally it is unsafe. The caller must initialize the memory properly |
| 75 | +```rust |
| 76 | +let mut halloc = unsafe{HeapAllocUninitialized::<u8>::new()}; |
| 77 | +{ // heap test |
| 78 | + let mut x = halloc.alloc_cell(100000); |
| 79 | + x[0] = 4; |
| 80 | + let mut y = halloc.alloc_cell(110000); |
| 81 | + y[0] = 5; |
| 82 | + let mut z = halloc.alloc_cell(120000); |
| 83 | + z[0] = 6; |
| 84 | + assert_eq!(y[0], 5); |
| 85 | + halloc.free_cell(y); |
| 86 | + assert_eq!(x[0], 4); |
| 87 | + assert_eq!(x[9], 0); |
| 88 | + assert_eq!(z[0], 6); |
| 89 | + ... |
| 90 | +} |
| 91 | +``` |
| 92 | + |
| 93 | + |
| 94 | +### On the heap in a single pool allocation |
| 95 | +This does a single big allocation on the heap, after which no further usage of the stdlib |
| 96 | +will happen. This can be useful for a jailed application that wishes to restrict syscalls |
| 97 | +at this point |
| 98 | + |
| 99 | +```rust |
| 100 | +use alloc_no_stdlib::HeapPrealloc; |
| 101 | +... |
| 102 | +let mut heap_global_buffer = define_allocator_memory_pool!(4096, u8, [0; 6 * 1024 * 1024], heap); |
| 103 | +let mut ags = HeapPrealloc::<u8>::new_allocator(4096, &mut heap_global_buffer, uninitialized); |
| 104 | +{ |
| 105 | + let mut x = ags.alloc_cell(9999); |
| 106 | + x.slice_mut()[0] = 4; |
| 107 | + let mut y = ags.alloc_cell(4); |
| 108 | + y[0] = 5; |
| 109 | + ags.free_cell(y); |
| 110 | + |
| 111 | + //y.mem[0] = 6; // <-- this is an error (use after free) |
| 112 | +} |
| 113 | +``` |
| 114 | + |
| 115 | + |
| 116 | + |
| 117 | +### On the heap, uninitialized |
| 118 | +This does a single big allocation on the heap, after which no further usage of the stdlib |
| 119 | +will happen. This can be useful for a jailed application that wishes to restrict syscalls |
| 120 | +at this point. This option keep does not set the memory to a valid value, so it is |
| 121 | +necessarily marked unsafe |
| 122 | + |
| 123 | +```rust |
| 124 | +use alloc_no_stdlib::HeapPrealloc; |
| 125 | +... |
| 126 | +let mut heap_global_buffer = unsafe{HeapPrealloc::<u8>::new_uninitialized_memory_pool(6 * 1024 * 1024)}; |
| 127 | +let mut ags = HeapPrealloc::<u8>::new_allocator(4096, &mut heap_global_buffer, uninitialized); |
| 128 | +{ |
| 129 | + let mut x = ags.alloc_cell(9999); |
| 130 | + x.slice_mut()[0] = 4; |
| 131 | + let mut y = ags.alloc_cell(4); |
| 132 | + y[0] = 5; |
| 133 | + ags.free_cell(y); |
| 134 | + |
| 135 | + //y.mem[0] = 6; // <-- this is an error (use after free) |
| 136 | +} |
| 137 | +``` |
| 138 | + |
| 139 | +### With calloc |
| 140 | +This is the most efficient way to get a zero'd dynamically sized buffer without the stdlib |
| 141 | +It does invoke the C calloc function and hence must invoke unsafe code. |
| 142 | +In this version, the number of cells are fixed to the parameter specified in the struct definition |
| 143 | +(4096 in this example) |
| 144 | + |
| 145 | +```rust |
| 146 | +extern { |
| 147 | + fn calloc(n_elem : usize, el_size : usize) -> *mut u8; |
| 148 | + fn malloc(len : usize) -> *mut u8; |
| 149 | + fn free(item : *mut u8); |
| 150 | +} |
| 151 | + |
| 152 | +declare_stack_allocator_struct!(CallocAllocatedFreelist4096, 4096, calloc); |
| 153 | +... |
| 154 | + |
| 155 | +// the buffer is defined with 200 megs of zero'd memory from calloc |
| 156 | +let mut calloc_global_buffer = unsafe {define_allocator_memory_pool!(4096, u8, [0; 200 * 1024 * 1024], calloc)}; |
| 157 | +// and assigned to a new_allocator |
| 158 | +let mut ags = CallocAllocatedFreelist4096::<u8>::new_allocator(&mut calloc_global_buffer.data, bzero); |
| 159 | +{ |
| 160 | + let mut x = ags.alloc_cell(9999); |
| 161 | + x.slice_mut()[0] = 4; |
| 162 | + let mut y = ags.alloc_cell(4); |
| 163 | + y[0] = 5; |
| 164 | + ags.free_cell(y); |
| 165 | + //y.mem[0] = 6; // <-- this is an error (use after free) |
| 166 | +} |
| 167 | +``` |
| 168 | + |
| 169 | +### With a static, mutable buffer |
| 170 | +If a single buffer of data is needed for the entire span of the application |
| 171 | +Then the simplest way to do so without a zero operation on |
| 172 | +the memory and without using the stdlib is to simply have a global allocated |
| 173 | +structure. Accessing mutable static variables requires unsafe code; however, |
| 174 | +so this code will invoke an unsafe block. |
| 175 | + |
| 176 | + |
| 177 | +Make sure to only reference global_buffer in a single place, at a single time in the code |
| 178 | +If it is used from two places or at different times, undefined behavior may result, |
| 179 | +since multiple allocators may get access to global_buffer. |
| 180 | + |
| 181 | + |
| 182 | +```rust |
| 183 | +declare_stack_allocator_struct!(GlobalAllocatedFreelist, 16, global); |
| 184 | +define_allocator_memory_pool!(16, u8, [0; 1024 * 1024 * 100], global, global_buffer); |
| 185 | + |
| 186 | +... |
| 187 | +// this references a global buffer |
| 188 | +let mut ags = GlobalAllocatedFreelist::<u8>::new_allocator(bzero); |
| 189 | +unsafe { |
| 190 | + bind_global_buffers_to_allocator!(ags, global_buffer, u8); |
| 191 | +} |
| 192 | +{ |
| 193 | + let mut x = ags.alloc_cell(9999); |
| 194 | + x.slice_mut()[0] = 4; |
| 195 | + let mut y = ags.alloc_cell(4); |
| 196 | + y[0] = 5; |
| 197 | + ags.free_cell(y); |
| 198 | + |
| 199 | + //y.mem[0] = 6; // <-- this is an error (use after free) |
| 200 | +} |
| 201 | +``` |
| 202 | + |
| 203 | + |
| 204 | +## Contributors |
| 205 | +- Daniel Reiter Horn |
0 commit comments