Skip to content

Commit 6fb0977

Browse files
committed
added readme for alloc-stdlib
1 parent d3d3e4a commit 6fb0977

File tree

1 file changed

+205
-0
lines changed

1 file changed

+205
-0
lines changed

alloc-stdlib/README.md

+205
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
# Framework for allocating memory in #![no_std] modules.
2+
3+
[![crates.io](http://meritbadge.herokuapp.com/alloc-no-stdlib)](https://crates.io/crates/alloc-no-stdlib)
4+
[![Build Status](https://travis-ci.org/dropbox/rust-alloc-no-stdlib.svg?branch=master)](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

Comments
 (0)