[tor-commits] [tor/master] add tor allocator for rust

nickm at torproject.org nickm at torproject.org
Fri Oct 27 14:07:26 UTC 2017


commit 76bbdfbfa9eca46b53d3ec5a44deafce51d2875a
Author: Chelsea Holland Komlo <me at 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) };
+    }
+}





More information about the tor-commits mailing list