commit 76bbdfbfa9eca46b53d3ec5a44deafce51d2875a Author: Chelsea Holland Komlo me@chelseakomlo.com Date: Sat Oct 14 23:05:31 2017 -0400
add tor allocator for rust --- src/rust/protover/ffi.rs | 20 ++++---- src/rust/tor_allocate/Cargo.toml | 13 +++++ src/rust/tor_allocate/include.am | 13 +++++ src/rust/tor_allocate/lib.rs | 12 +++++ src/rust/tor_allocate/tor_allocate.rs | 90 +++++++++++++++++++++++++++++++++++ 5 files changed, 138 insertions(+), 10 deletions(-)
diff --git a/src/rust/protover/ffi.rs b/src/rust/protover/ffi.rs index 539b6c0ba..23a289bd5 100644 --- a/src/rust/protover/ffi.rs +++ b/src/rust/protover/ffi.rs @@ -8,7 +8,7 @@ use std::ffi::CString;
use protover::*; use smartlist::*; -use tor_allocate::allocate_string; +use tor_allocate::allocate_and_copy_string;
/// Translate C enums to Rust Proto enums, using the integer value of the C /// enum to map to its associated Rust enum @@ -124,17 +124,17 @@ pub extern "C" fn protover_compute_vote( ) -> *mut c_char {
if list.is_null() { - let mut empty = String::new(); - return allocate_string(&mut empty); + let empty = String::new(); + return allocate_and_copy_string(&empty); }
// Dereference of raw pointer requires an unsafe block. The pointer is // checked above to ensure it is not null. let data: Vec<String> = unsafe { (*list).get_list() };
- let mut vote = compute_vote(data, threshold); + let vote = compute_vote(data, threshold);
- allocate_string(&mut vote) + allocate_and_copy_string(&vote) }
/// Provide an interface for C to translate arguments and return types for @@ -162,10 +162,10 @@ pub extern "C" fn protover_compute_for_old_tor( ) -> *mut c_char { // Not handling errors when unwrapping as the content is controlled // and is an empty string - let mut empty = String::new(); + let empty = String::new();
if version.is_null() { - return allocate_string(&mut empty); + return allocate_and_copy_string(&empty); }
// Require an unsafe block to read the version from a C string. The pointer @@ -174,10 +174,10 @@ pub extern "C" fn protover_compute_for_old_tor(
let version = match c_str.to_str() { Ok(n) => n, - Err(_) => return allocate_string(&mut empty), + Err(_) => return allocate_and_copy_string(&empty), };
- let mut supported = compute_for_old_tor(&version); + let supported = compute_for_old_tor(&version);
- allocate_string(&mut supported) + allocate_and_copy_string(&supported) } diff --git a/src/rust/tor_allocate/Cargo.toml b/src/rust/tor_allocate/Cargo.toml new file mode 100644 index 000000000..ceb08b78a --- /dev/null +++ b/src/rust/tor_allocate/Cargo.toml @@ -0,0 +1,13 @@ +[package] +authors = ["The Tor Project"] +version = "0.0.1" +name = "tor_allocate" + +[dependencies] +libc = "0.2.22" + +[lib] +name = "tor_allocate" +path = "lib.rs" +crate_type = ["rlib", "staticlib"] + diff --git a/src/rust/tor_allocate/include.am b/src/rust/tor_allocate/include.am new file mode 100644 index 000000000..9e770dbc0 --- /dev/null +++ b/src/rust/tor_allocate/include.am @@ -0,0 +1,13 @@ +EXTRA_DIST +=\ + src/rust/tor_allocate/Cargo.toml \ + src/rust/tor_allocate/lib.rs \ + src/rust/tor_allocate/tor_allocate.rs + +src/rust/target/release/@TOR_RUST_C_STRING_STATIC_NAME@: FORCE + ( cd "$(abs_top_srcdir)/src/rust/tor_allocate" ; \ + CARGO_TARGET_DIR="$(abs_top_builddir)/src/rust/target" \ + CARGO_HOME="$(abs_top_builddir)/src/rust" \ + $(CARGO) build --release --quiet $(CARGO_ONLINE) ) + +FORCE: + diff --git a/src/rust/tor_allocate/lib.rs b/src/rust/tor_allocate/lib.rs new file mode 100644 index 000000000..81afd095f --- /dev/null +++ b/src/rust/tor_allocate/lib.rs @@ -0,0 +1,12 @@ +//! Allocation helper functions that allow data to be allocated in Rust +//! using tor's specified allocator. In doing so, this can be later freed +//! from C. +//! +//! This is currently a temporary solution, we will later use tor's allocator +//! by default for any allocation that occurs in Rust. However, as this will +//! stabalize in 2018, we can use this as a temporary measure. + +extern crate libc; + +mod tor_allocate; +pub use tor_allocate::*; diff --git a/src/rust/tor_allocate/tor_allocate.rs b/src/rust/tor_allocate/tor_allocate.rs new file mode 100644 index 000000000..de1d13942 --- /dev/null +++ b/src/rust/tor_allocate/tor_allocate.rs @@ -0,0 +1,90 @@ +use libc::{c_char, c_void}; +use std::{ptr, slice}; + +#[cfg(not(test))] +extern "C" { + fn tor_malloc_ ( size: usize) -> *mut c_void; +} + +// Defined only for tests, used for testing purposes, so that we don't need +// to link to tor C files. Uses the system allocator +#[cfg(test)] +extern "C" fn tor_malloc_ ( size: usize) -> *mut c_void { + use libc::malloc; + unsafe { malloc(size) } +} + +/// Allocate memory using tor_malloc_ and copy an existing string into the +/// allocated buffer, returning a pointer that can later be called in C. +/// +/// # Inputs +/// +/// * `src`, a reference to a String that will be copied. +/// +/// # Returns +/// +/// A `String` that should be freed by tor_free in C +/// +pub fn allocate_and_copy_string(src: &String) -> *mut c_char { + let bytes = s.as_bytes(); + + let size = s.len(); + let size_with_null_byte = size + 1; + + let dest = unsafe { tor_malloc_(size_with_null_byte) as *mut u8 }; + + if dest.is_null() { + return dest as *mut c_char; + } + + unsafe { ptr::copy_nonoverlapping(bytes.as_ptr(), dest, size) }; + + // set the last byte as null, using the ability to index into a slice + // rather than doing pointer arithmatic + let slice = unsafe { slice::from_raw_parts_mut(dest, size_with_null_byte) }; + slice[size] = 0; // add a null terminator + + dest as *mut c_char +} + +#[cfg(test)] +mod test { + + #[test] + fn test_allocate_and_copy_string_with_empty() { + use std::ffi::CStr; + use libc::{free, c_void}; + + use tor_allocate::allocate_and_copy_string; + + let empty = String::new(); + let allocated_empty = allocate_and_copy_string(&empty); + + let allocated_empty_rust = unsafe { + CStr::from_ptr(allocated_empty).to_str().unwrap() + }; + + assert_eq!("", allocated_empty_rust); + + unsafe { free(allocated_empty as *mut c_void) }; + } + + #[test] + fn test_allocate_and_copy_string_with_not_empty_string() { + use std::ffi::CStr; + use libc::{free, c_void}; + + use tor_allocate::allocate_and_copy_string; + + let empty = String::from("foo bar biz"); + let allocated_empty = allocate_and_copy_string(&empty); + + let allocated_empty_rust = unsafe { + CStr::from_ptr(allocated_empty).to_str().unwrap() + }; + + assert_eq!("foo bar biz", allocated_empty_rust); + + unsafe { free(allocated_empty as *mut c_void) }; + } +}