From 915fa39d0f927712f7a468c0a2f4497aef0a3b0f Mon Sep 17 00:00:00 2001 From: Sebastian Hahn Date: Sat, 29 Apr 2017 08:51:51 +0200 Subject: [PATCH 1/7] Add --enable-rust configure switch Introduce a way to optionally enable Rust integration for our builds. No actual Rust code is added yet and specifying the flag has no effect other than failing the build if rustc and cargo are unavailable. --- Makefile.am | 6 ++++++ configure.ac | 20 ++++++++++++++++++++ src/include.am | 1 + src/or/include.am | 3 ++- src/test/fuzz/include.am | 3 ++- src/test/include.am | 21 ++++++++++++++------- src/tools/include.am | 14 ++++++++------ 7 files changed, 53 insertions(+), 15 deletions(-) diff --git a/Makefile.am b/Makefile.am index be1dc7f7ef..b8aa049da1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -25,6 +25,12 @@ else TESTING_TOR_BINARY=$(top_builddir)/src/or/tor$(EXEEXT) endif +if USE_RUST +rust_ldadd= +else +rust_ldadd= +endif + include src/include.am include doc/include.am include contrib/include.am diff --git a/configure.ac b/configure.ac index ff917354a3..d281903fcc 100644 --- a/configure.ac +++ b/configure.ac @@ -55,6 +55,8 @@ AC_ARG_ENABLE(oss-fuzz, AS_HELP_STRING(--enable-oss-fuzz, [build extra fuzzers based on 'oss-fuzz' environment])) AC_ARG_ENABLE(memory-sentinels, AS_HELP_STRING(--disable-memory-sentinels, [disable code that tries to prevent some kinds of memory access bugs. For fuzzing only.])) +AC_ARG_ENABLE(rust, + AS_HELP_STRING(--enable-rust, [enable rust integration])) if test "x$enable_coverage" != "xyes" -a "x$enable_asserts_in_tests" = "xno" ; then AC_MSG_ERROR([Can't disable assertions outside of coverage build]) @@ -65,6 +67,7 @@ AM_CONDITIONAL(COVERAGE_ENABLED, test "x$enable_coverage" = "xyes") AM_CONDITIONAL(DISABLE_ASSERTS_IN_UNIT_TESTS, test "x$enable_asserts_in_tests" = "xno") AM_CONDITIONAL(LIBFUZZER_ENABLED, test "x$enable_libfuzzer" = "xyes") AM_CONDITIONAL(OSS_FUZZ_ENABLED, test "x$enable_oss_fuzz" = "xyes") +AM_CONDITIONAL(USE_RUST, test "x$enable_rust" = "xyes") if test "$enable_static_tor" = "yes"; then enable_static_libevent="yes"; @@ -249,6 +252,23 @@ if test "x$PYTHON" = "x"; then fi AM_CONDITIONAL(USEPYTHON, [test "x$PYTHON" != "x"]) + +if test "x$enable_rust" = "xyes"; then + AC_ARG_VAR([RUSTC], [path to the rustc binary]) + AC_CHECK_PROG([RUSTC], [rustc], [rustc],[no]) + if test "x$RUSTC" = "xno"; then + AC_MSG_ERROR([rustc unavailable but rust integration requested.]) + fi + + AC_ARG_VAR([CARGO], [path to the cargo binary]) + AC_CHECK_PROG([CARGO], [cargo], [cargo],[no]) + if test "x$CARGO" = "xno"; then + AC_MSG_ERROR([cargo unavailable but rust integration requested.]) + fi + + AC_DEFINE([HAVE_RUST], 1, [have Rust]) +fi + ifdef([AC_C_FLEXIBLE_ARRAY_MEMBER], [ AC_C_FLEXIBLE_ARRAY_MEMBER ], [ diff --git a/src/include.am b/src/include.am index f78853f50f..90ecf90d45 100644 --- a/src/include.am +++ b/src/include.am @@ -2,6 +2,7 @@ include src/ext/include.am include src/trunnel/include.am include src/common/include.am include src/or/include.am +include src/rust/include.am include src/test/include.am include src/tools/include.am include src/win32/include.am diff --git a/src/or/include.am b/src/or/include.am index 4c24dd23b3..ff7110ea99 100644 --- a/src/or/include.am +++ b/src/or/include.am @@ -123,7 +123,8 @@ src_or_tor_LDADD = src/or/libtor.a src/common/libor.a src/common/libor-ctime.a \ src/trace/libor-trace.a \ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ @TOR_OPENSSL_LIBS@ \ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ @TOR_SYSTEMD_LIBS@ \ - @TOR_LZMA_LIBS@ @TOR_ZSTD_LIBS@ + @TOR_LZMA_LIBS@ @TOR_ZSTD_LIBS@ \ + $(rust_ldadd) if COVERAGE_ENABLED src_or_tor_cov_SOURCES = src/or/tor_main.c diff --git a/src/test/fuzz/include.am b/src/test/fuzz/include.am index 500377f6d7..6008238bba 100644 --- a/src/test/fuzz/include.am +++ b/src/test/fuzz/include.am @@ -20,7 +20,8 @@ FUZZING_LIBS = \ @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ \ @TOR_SYSTEMD_LIBS@ \ @TOR_LZMA_LIBS@ \ - @TOR_ZSTD_LIBS@ + @TOR_ZSTD_LIBS@ \ + $(rust_ldadd) oss-fuzz-prereqs: \ src/or/libtor-testing.a \ diff --git a/src/test/include.am b/src/test/include.am index 230a6c8bad..8a465877d3 100644 --- a/src/test/include.am +++ b/src/test/include.am @@ -181,7 +181,8 @@ src_test_test_switch_id_LDADD = \ src/common/libor-testing.a \ src/common/libor-ctime-testing.a \ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ \ - @TOR_LZMA_LIBS@ @TOR_ZSTD_LIBS@ + @TOR_LZMA_LIBS@ @TOR_ZSTD_LIBS@ \ + $(rust_ldadd) src_test_test_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ \ @TOR_LDFLAGS_libevent@ @@ -196,7 +197,8 @@ src_test_test_LDADD = src/or/libtor-testing.a \ src/trace/libor-trace.a \ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \ @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ \ - @TOR_SYSTEMD_LIBS@ @TOR_LZMA_LIBS@ @TOR_ZSTD_LIBS@ + @TOR_SYSTEMD_LIBS@ @TOR_LZMA_LIBS@ @TOR_ZSTD_LIBS@ \ + $(rust_ldadd) src_test_test_slow_CPPFLAGS = $(src_test_test_CPPFLAGS) src_test_test_slow_CFLAGS = $(src_test_test_CFLAGS) @@ -220,7 +222,8 @@ src_test_bench_LDADD = src/or/libtor.a src/common/libor.a \ src/trace/libor-trace.a \ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \ @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ \ - @TOR_SYSTEMD_LIBS@ @TOR_LZMA_LIBS@ @TOR_ZSTD_LIBS@ + @TOR_SYSTEMD_LIBS@ @TOR_LZMA_LIBS@ @TOR_ZSTD_LIBS@ \ + $(rust_ldadd) src_test_test_workqueue_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ \ @TOR_LDFLAGS_libevent@ @@ -232,7 +235,8 @@ src_test_test_workqueue_LDADD = src/or/libtor-testing.a \ src/trace/libor-trace.a \ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \ @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ \ - @TOR_LZMA_LIBS@ @TOR_ZSTD_LIBS@ + @TOR_LZMA_LIBS@ @TOR_ZSTD_LIBS@ \ + $(rust_ldadd) src_test_test_timers_CPPFLAGS = $(src_test_test_CPPFLAGS) src_test_test_timers_CFLAGS = $(src_test_test_CFLAGS) @@ -243,7 +247,8 @@ src_test_test_timers_LDADD = \ src/common/libor-crypto-testing.a $(LIBKECCAK_TINY) $(LIBDONNA) \ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \ @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ \ - @TOR_LZMA_LIBS@ + @TOR_LZMA_LIBS@ \ + $(rust_ldadd) src_test_test_timers_LDFLAGS = $(src_test_test_LDFLAGS) noinst_HEADERS+= \ @@ -270,7 +275,8 @@ src_test_test_ntor_cl_LDADD = src/or/libtor.a src/common/libor.a \ src/trace/libor-trace.a \ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ \ @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ \ - @TOR_LZMA_LIBS@ + @TOR_LZMA_LIBS@ \ + $(rust_ldadd) src_test_test_ntor_cl_AM_CPPFLAGS = \ -I"$(top_srcdir)/src/or" @@ -291,7 +297,8 @@ src_test_test_bt_cl_LDADD = src/common/libor-testing.a \ src/common/libor-ctime-testing.a \ src/trace/libor-trace.a \ @TOR_LIB_MATH@ \ - @TOR_LIB_WS32@ @TOR_LIB_GDI@ + @TOR_LIB_WS32@ @TOR_LIB_GDI@ \ + $(rust_ldadd) src_test_test_bt_cl_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS) src_test_test_bt_cl_CPPFLAGS= $(src_test_AM_CPPFLAGS) $(TEST_CPPFLAGS) diff --git a/src/tools/include.am b/src/tools/include.am index 5eadb03a05..717af9e2ae 100644 --- a/src/tools/include.am +++ b/src/tools/include.am @@ -8,7 +8,8 @@ src_tools_tor_resolve_SOURCES = src/tools/tor-resolve.c src_tools_tor_resolve_LDFLAGS = src_tools_tor_resolve_LDADD = src/common/libor.a \ src/common/libor-ctime.a \ - @TOR_LIB_MATH@ @TOR_LIB_WS32@ + @TOR_LIB_MATH@ @TOR_LIB_WS32@ \ + $(rust_ldadd) if COVERAGE_ENABLED src_tools_tor_cov_resolve_SOURCES = src/tools/tor-resolve.c @@ -22,11 +23,12 @@ endif src_tools_tor_gencert_SOURCES = src/tools/tor-gencert.c src_tools_tor_gencert_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ src_tools_tor_gencert_LDADD = src/common/libor.a src/common/libor-crypto.a \ - src/common/libor-ctime.a \ - $(LIBKECCAK_TINY) \ - $(LIBDONNA) \ - @TOR_LIB_MATH@ @TOR_ZLIB_LIBS@ @TOR_OPENSSL_LIBS@ \ - @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ + src/common/libor-ctime.a \ + $(LIBKECCAK_TINY) \ + $(LIBDONNA) \ + @TOR_LIB_MATH@ @TOR_ZLIB_LIBS@ @TOR_OPENSSL_LIBS@ \ + @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ \ + $(rust_ldadd) if COVERAGE_ENABLED src_tools_tor_cov_gencert_SOURCES = src/tools/tor-gencert.c From f8ef7c65d10ccd9403d9d64ceceba0dd74d6421f Mon Sep 17 00:00:00 2001 From: Sebastian Hahn Date: Sat, 29 Apr 2017 09:15:14 +0200 Subject: [PATCH 2/7] Add some Rust utility functions and print support This gives an indication in the log that Tor was built with Rust support, as well as laying some groundwork for further string-returning APIs to be converted to Rust --- Makefile.am | 5 +- src/common/compat_rust.c | 39 ++++++++++ src/common/compat_rust.h | 28 +++++++ src/common/include.am | 6 ++ src/or/main.c | 10 +++ src/rust/Cargo.lock | 14 ++++ src/rust/Cargo.toml | 14 ++++ src/rust/include.am | 5 ++ src/rust/tor_util/Cargo.toml | 13 ++++ src/rust/tor_util/ffi.rs | 56 ++++++++++++++ src/rust/tor_util/include.am | 12 +++ src/rust/tor_util/lib.rs | 13 ++++ src/rust/tor_util/rust_string.rs | 101 +++++++++++++++++++++++++ src/rust/tor_util/tests/rust_string.rs | 37 +++++++++ src/test/include.am | 1 + src/test/test.c | 1 + src/test/test.h | 1 + src/test/test_rust.c | 31 ++++++++ 18 files changed, 386 insertions(+), 1 deletion(-) create mode 100644 src/common/compat_rust.c create mode 100644 src/common/compat_rust.h create mode 100644 src/rust/Cargo.lock create mode 100644 src/rust/Cargo.toml create mode 100644 src/rust/include.am create mode 100644 src/rust/tor_util/Cargo.toml create mode 100644 src/rust/tor_util/ffi.rs create mode 100644 src/rust/tor_util/include.am create mode 100644 src/rust/tor_util/lib.rs create mode 100644 src/rust/tor_util/rust_string.rs create mode 100644 src/rust/tor_util/tests/rust_string.rs create mode 100644 src/test/test_rust.c diff --git a/Makefile.am b/Makefile.am index b8aa049da1..be4140aa0b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -26,7 +26,7 @@ TESTING_TOR_BINARY=$(top_builddir)/src/or/tor$(EXEEXT) endif if USE_RUST -rust_ldadd= +rust_ldadd=$(top_builddir)/src/rust/target/release/libtor_util.a else rust_ldadd= endif @@ -236,3 +236,6 @@ mostlyclean-local: rm -rf $(HTML_COVER_DIR) rm -rf $(top_builddir)/doc/doxygen rm -rf $(TEST_NETWORK_ALL_LOG_DIR) + +clean-local: + rm -rf $(top_builddir)/src/rust/target diff --git a/src/common/compat_rust.c b/src/common/compat_rust.c new file mode 100644 index 0000000000..366fd4037b --- /dev/null +++ b/src/common/compat_rust.c @@ -0,0 +1,39 @@ +/* Copyright (c) 2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file rust_compat.c + * \brief Rust FFI compatibility functions and helpers. This file is only built + * if Rust is not used. + **/ + +#include "compat_rust.h" +#include "util.h" + +/** + * Free storage pointed to by str, and itself. + */ +void +rust_str_free(rust_str_t str) +{ + char *s = (char *)str; + tor_free(s); +} + +/** + * Return zero-terminated contained string. + */ +const char * +rust_str_get(const rust_str_t str) +{ + return (const char *)str; +} + +/* If we were using Rust, we'd say so on startup. */ +rust_str_t +rust_welcome_string(void) +{ + char *s = tor_malloc_zero(1); + return (rust_str_t)s; +} + diff --git a/src/common/compat_rust.h b/src/common/compat_rust.h new file mode 100644 index 0000000000..752a29b56c --- /dev/null +++ b/src/common/compat_rust.h @@ -0,0 +1,28 @@ +/* Copyright (c) 2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file rust_compat.h + * \brief Headers for rust_compat.c + **/ + +#ifndef TOR_RUST_COMPAT_H +#define TOR_RUST_COMPAT_H + +#include "torint.h" + +/** + * Strings allocated in Rust must be freed from Rust code again. Let's make + * it less likely to accidentally mess up and call tor_free() on it, because + * currently it'll just work but might break at any time. + */ +typedef uintptr_t rust_str_t; + +void rust_str_free(rust_str_t); + +const char *rust_str_get(const rust_str_t); + +rust_str_t rust_welcome_string(void); + +#endif + diff --git a/src/common/include.am b/src/common/include.am index e285ef5f86..b37b363b82 100644 --- a/src/common/include.am +++ b/src/common/include.am @@ -100,6 +100,11 @@ LIBOR_A_SRC = \ $(threads_impl_source) \ $(readpassphrase_source) +if USE_RUST +else +LIBOR_A_SRC += src/common/compat_rust.c +endif + src/common/src_common_libor_testing_a-log.$(OBJEXT) \ src/common/log.$(OBJEXT): micro-revision.i @@ -146,6 +151,7 @@ COMMONHEADERS = \ src/common/compat.h \ src/common/compat_libevent.h \ src/common/compat_openssl.h \ + src/common/compat_rust.h \ src/common/compat_threads.h \ src/common/compat_time.h \ src/common/compress.h \ diff --git a/src/or/main.c b/src/or/main.c index 0da43dc232..dcd7ef215e 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -58,6 +58,7 @@ #include "circuitlist.h" #include "circuituse.h" #include "command.h" +#include "compat_rust.h" #include "compress.h" #include "config.h" #include "confparse.h" @@ -3039,6 +3040,15 @@ tor_init(int argc, char *argv[]) "Expect more bugs than usual."); } + { + rust_str_t rust_str = rust_welcome_string(); + const char *s = rust_str_get(rust_str); + if (strlen(s) > 0) { + log_notice(LD_GENERAL, "%s", s); + } + rust_str_free(rust_str); + } + if (network_init()<0) { log_err(LD_BUG,"Error initializing network; exiting."); return -1; diff --git a/src/rust/Cargo.lock b/src/rust/Cargo.lock new file mode 100644 index 0000000000..4ac9606ce8 --- /dev/null +++ b/src/rust/Cargo.lock @@ -0,0 +1,14 @@ +[root] +name = "tor_util" +version = "0.0.1" +dependencies = [ + "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "libc" +version = "0.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[metadata] +"checksum libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)" = "babb8281da88cba992fa1f4ddec7d63ed96280a1a53ec9b919fd37b53d71e502" diff --git a/src/rust/Cargo.toml b/src/rust/Cargo.toml new file mode 100644 index 0000000000..527c536323 --- /dev/null +++ b/src/rust/Cargo.toml @@ -0,0 +1,14 @@ +[workspace] +members = ["tor_util"] + +[profile.release] +debug = true +panic = "abort" + +[source.crates-io] +registry = 'https://github.com/rust-lang/crates.io-index' +replace-with = 'vendored-sources' + +[source.vendored-sources] +directory = 'vendor' + diff --git a/src/rust/include.am b/src/rust/include.am new file mode 100644 index 0000000000..e198049071 --- /dev/null +++ b/src/rust/include.am @@ -0,0 +1,5 @@ +include src/rust/tor_util/include.am + +EXTRA_DIST +=\ + src/rust/Cargo.toml \ + src/rust/Cargo.lock diff --git a/src/rust/tor_util/Cargo.toml b/src/rust/tor_util/Cargo.toml new file mode 100644 index 0000000000..f175fbdfb0 --- /dev/null +++ b/src/rust/tor_util/Cargo.toml @@ -0,0 +1,13 @@ +[package] +authors = ["The Tor Project"] +name = "tor_util" +version = "0.0.1" + +[lib] +name = "tor_util" +path = "lib.rs" +crate_type = ["rlib", "staticlib"] + +[dependencies] +libc = "*" + diff --git a/src/rust/tor_util/ffi.rs b/src/rust/tor_util/ffi.rs new file mode 100644 index 0000000000..af4bfc41af --- /dev/null +++ b/src/rust/tor_util/ffi.rs @@ -0,0 +1,56 @@ +//! FFI functions, only to be called from C. +//! +//! Equivalent C versions of these live in `src/common/compat_rust.c` + +use std::mem::forget; +use std::ffi::CString; + +use libc; +use rust_string::RustString; + +/// Free the passed `RustString` (`rust_str_t` in C), to be used in place of +/// `tor_free`(). +/// +/// # Examples +/// ```c +/// rust_str_t r_s = rust_welcome_string(); +/// rust_str_free(r_s); +/// ``` +#[no_mangle] +#[cfg_attr(feature = "cargo-clippy", allow(needless_pass_by_value))] +pub unsafe extern "C" fn rust_str_free(_str: RustString) { + // Empty body: Just drop _str and we're done (Drop takes care of it). +} + +/// Lends an immutable, NUL-terminated C String. +/// +/// # Examples +/// ```c +/// rust_str_t r_s = rust_welcome_string(); +/// const char *s = rust_str_get(r_s); +/// printf("%s", s); +/// rust_str_free(r_s); +/// ``` +#[no_mangle] +pub unsafe extern "C" fn rust_str_get(str: RustString) -> *const libc::c_char { + let res = str.as_ptr(); + forget(str); + res +} + +/// Returns a short string to announce Rust support during startup. +/// +/// # Examples +/// ```c +/// rust_str_t r_s = rust_welcome_string(); +/// const char *s = rust_str_get(r_s); +/// printf("%s", s); +/// rust_str_free(r_s); +/// ``` +#[no_mangle] +pub extern "C" fn rust_welcome_string() -> RustString { + let s = CString::new("Tor is running with Rust integration. Please report \ + any bugs you encouter.") + .unwrap(); + RustString::from(s) +} diff --git a/src/rust/tor_util/include.am b/src/rust/tor_util/include.am new file mode 100644 index 0000000000..3b877649a3 --- /dev/null +++ b/src/rust/tor_util/include.am @@ -0,0 +1,12 @@ +EXTRA_DIST +=\ + src/rust/tor_util/Cargo.toml \ + src/rust/tor_util/lib.rs \ + src/rust/tor_util/ffi.rs \ + src/rust/tor_util/rust_string.rs + +src/rust/target/release/libtor_util.a: FORCE + ( cd "$(abs_top_srcdir)/src/rust/tor_util" ; \ + CARGO_TARGET_DIR="$(abs_top_builddir)/src/rust/target" \ + $(CARGO) build --release --quiet --frozen ) + +FORCE: diff --git a/src/rust/tor_util/lib.rs b/src/rust/tor_util/lib.rs new file mode 100644 index 0000000000..79d583d1ae --- /dev/null +++ b/src/rust/tor_util/lib.rs @@ -0,0 +1,13 @@ +//! C <-> Rust compatibility helpers and types. +//! +//! Generically useful, small scale helpers should go here. This goes for both +//! the C side (in the form of the ffi module) as well as the Rust side +//! (individual modules per functionality). The corresponding C stuff lives in +//! `src/common/compat_rust.{c,h}`. + +extern crate libc; + +mod rust_string; +pub mod ffi; + +pub use rust_string::*; diff --git a/src/rust/tor_util/rust_string.rs b/src/rust/tor_util/rust_string.rs new file mode 100644 index 0000000000..46ec3fd7a8 --- /dev/null +++ b/src/rust/tor_util/rust_string.rs @@ -0,0 +1,101 @@ +use std::ffi::CString; +use std::mem::forget; +use libc; + +/// Compatibility wrapper for strings allocated in Rust and passed to C. +/// +/// Rust doesn't ensure the safety of freeing memory across an FFI boundary, so +/// we need to take special care to ensure we're not accidentally calling +/// `tor_free`() on any string allocated in Rust. To more easily differentiate +/// between strings that possibly (if Rust support is enabled) were allocated +/// in Rust, C has the `rust_str_t` helper type. The equivalent on the Rust +/// side is `RustString`. +/// +/// Note: This type must not be used for strings allocated in C. +#[repr(C)] +#[derive(Debug)] +pub struct RustString(*mut libc::c_char); + +impl RustString { + /// Returns a pointer to the underlying NUL-terminated byte array. + /// + /// Note that this function is not typically useful for Rust callers, + /// except in a direct FFI context. + /// + /// # Examples + /// ``` + /// # use tor_util::RustString; + /// use std::ffi::CString; + /// + /// let r = RustString::from(CString::new("asdf").unwrap()); + /// let c_str = r.as_ptr(); + /// assert_eq!(b'a', unsafe { *c_str as u8}); + /// ``` + pub fn as_ptr(&self) -> *const libc::c_char { + self.0 as *const libc::c_char + } +} + +impl From for RustString { + /// Constructs a new `RustString` + /// + /// # Examples + /// ``` + /// # use tor_util::RustString; + /// use std::ffi::CString; + /// + /// let r = RustString::from(CString::new("asdf").unwrap()); + /// ``` + fn from(str: CString) -> RustString { + RustString(str.into_raw()) + } +} + +impl Into for RustString { + /// Reconstructs a `CString` from this `RustString`. + /// + /// Useful to take ownership back from a `RustString` that was given to C + /// code. + /// + /// # Examples + /// ``` + /// # use tor_util::RustString; + /// use std::ffi::CString; + /// + /// let cs = CString::new("asdf").unwrap(); + /// let r = RustString::from(cs.clone()); + /// let cs2 = r.into(); + /// assert_eq!(cs, cs2); + /// ``` + fn into(self) -> CString { + // Calling from_raw is always OK here: We only construct self using + // valid CStrings and don't expose anything that could mutate it + let ret = unsafe { CString::from_raw(self.0) }; + forget(self); + ret + } +} + +impl Drop for RustString { + fn drop(&mut self) { + // Don't use into() here, because we would need to move out of + // self. Same safety consideration. Immediately drop the created + // CString, which takes care of freeing the wrapped string. + unsafe { CString::from_raw(self.0) }; + } +} + +#[cfg(test)] +mod test { + use std::mem; + use super::*; + + use libc; + + /// Ensures we're not adding overhead by using RustString. + #[test] + fn size_of() { + assert_eq!(mem::size_of::<*mut libc::c_char>(), + mem::size_of::()) + } +} diff --git a/src/rust/tor_util/tests/rust_string.rs b/src/rust/tor_util/tests/rust_string.rs new file mode 100644 index 0000000000..1ff605a43c --- /dev/null +++ b/src/rust/tor_util/tests/rust_string.rs @@ -0,0 +1,37 @@ +extern crate tor_util; +extern crate libc; + +use std::ffi::CString; +use tor_util::RustString; + +#[test] +fn rust_string_conversions_preserve_c_string() { + let s = CString::new("asdf foo").unwrap(); + let r = RustString::from(s.clone()); + let r2 = RustString::from(s.clone()); + let c = r2.as_ptr(); + assert_eq!(unsafe { libc::strlen(c) }, 8); + let c_str = r.into(); + assert_eq!(s, c_str); +} + +#[test] +fn empty_string() { + let s = CString::new("").unwrap(); + let r = RustString::from(s.clone()); + let c = r.as_ptr(); + assert_eq!(unsafe { libc::strlen(c) }, 0); + let c_str = r.into(); + assert_eq!(s, c_str); +} + +#[test] +fn c_string_with_unicode() { + // The euro sign is three bytes + let s = CString::new("asd€asd").unwrap(); + let r = RustString::from(s.clone()); + let c = r.as_ptr(); + assert_eq!(unsafe { libc::strlen(c) }, 9); + let c_str = r.into(); + assert_eq!(s, c_str); +} diff --git a/src/test/include.am b/src/test/include.am index 8a465877d3..438eaddba8 100644 --- a/src/test/include.am +++ b/src/test/include.am @@ -130,6 +130,7 @@ src_test_test_SOURCES = \ src/test/test_routerkeys.c \ src/test/test_routerlist.c \ src/test/test_routerset.c \ + src/test/test_rust.c \ src/test/test_scheduler.c \ src/test/test_shared_random.c \ src/test/test_socks.c \ diff --git a/src/test/test.c b/src/test/test.c index 4d2cf1536b..18805cb3b7 100644 --- a/src/test/test.c +++ b/src/test/test.c @@ -1233,6 +1233,7 @@ struct testgroup_t testgroups[] = { { "routerkeys/", routerkeys_tests }, { "routerlist/", routerlist_tests }, { "routerset/" , routerset_tests }, + { "rust/", rust_tests }, { "scheduler/", scheduler_tests }, { "socks/", socks_tests }, { "shared-random/", sr_tests }, diff --git a/src/test/test.h b/src/test/test.h index 3d7d05e771..3dc1c332c7 100644 --- a/src/test/test.h +++ b/src/test/test.h @@ -231,6 +231,7 @@ extern struct testcase_t router_tests[]; extern struct testcase_t routerkeys_tests[]; extern struct testcase_t routerlist_tests[]; extern struct testcase_t routerset_tests[]; +extern struct testcase_t rust_tests[]; extern struct testcase_t scheduler_tests[]; extern struct testcase_t storagedir_tests[]; extern struct testcase_t socks_tests[]; diff --git a/src/test/test_rust.c b/src/test/test_rust.c new file mode 100644 index 0000000000..6ad57d6fcb --- /dev/null +++ b/src/test/test_rust.c @@ -0,0 +1,31 @@ +/* Copyright (c) 2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#include "orconfig.h" +#include "compat_rust.h" +#include "test.h" +#include "util.h" + +static void +test_welcome_string(void *arg) +{ + (void)arg; + rust_str_t s = rust_welcome_string(); + const char *c_str = rust_str_get(s); + tt_assert(c_str); + size_t len = strlen(c_str); +#ifdef HAVE_RUST + tt_assert(len > 0); +#else + tt_assert(len == 0); +#endif + + done: + rust_str_free(s); +} + +struct testcase_t rust_tests[] = { + { "welcome_string", test_welcome_string, 0, NULL, NULL }, + END_OF_TESTCASES +}; + From f0516ed348283fb9fd8e9e1af7d9415728623b1d Mon Sep 17 00:00:00 2001 From: Sebastian Hahn Date: Sat, 29 Apr 2017 13:17:22 +0200 Subject: [PATCH 3/7] Run cargo test during make check --- src/test/include.am | 9 ++++++++- src/test/test_rust.sh | 13 +++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) create mode 100755 src/test/test_rust.sh diff --git a/src/test/include.am b/src/test/include.am index 438eaddba8..5fd61dd3e6 100644 --- a/src/test/include.am +++ b/src/test/include.am @@ -6,7 +6,8 @@ TESTS_ENVIRONMENT = \ export SHELL="$(SHELL)"; \ export abs_top_srcdir="$(abs_top_srcdir)"; \ export builddir="$(builddir)"; \ - export TESTING_TOR_BINARY="$(TESTING_TOR_BINARY)"; + export TESTING_TOR_BINARY="$(TESTING_TOR_BINARY)"; \ + export CARGO="$(CARGO)"; TESTSCRIPTS = \ src/test/fuzz_static_testcases.sh \ @@ -19,6 +20,11 @@ TESTSCRIPTS = \ src/test/test_workqueue_socketpair.sh \ src/test/test_switch_id.sh +if USE_RUST +TESTSCRIPTS += \ + src/test/test_rust.sh +endif + if USEPYTHON TESTSCRIPTS += src/test/test_ntor.sh src/test/test_hs_ntor.sh src/test/test_bt.sh endif @@ -314,6 +320,7 @@ EXTRA_DIST += \ src/test/test_zero_length_keys.sh \ src/test/test_ntor.sh src/test/test_hs_ntor.sh src/test/test_bt.sh \ src/test/test-network.sh \ + src/test/test_rust.sh \ src/test/test_switch_id.sh \ src/test/test_workqueue_cancel.sh \ src/test/test_workqueue_efd.sh \ diff --git a/src/test/test_rust.sh b/src/test/test_rust.sh new file mode 100755 index 0000000000..b41be978b9 --- /dev/null +++ b/src/test/test_rust.sh @@ -0,0 +1,13 @@ +#!/bin/sh +# Test all the Rust crates we're using + +crates=tor_util + +exitcode=0 + +for crate in $crates; do + cd "${abs_top_srcdir:-.}/src/rust/${crate}" + "${CARGO:-cargo}" test --frozen || exitcode=1 +done + +exit $exitcode From ed15511ac3ace940956c6c72a61387d37f035160 Mon Sep 17 00:00:00 2001 From: Sebastian Hahn Date: Tue, 2 May 2017 04:03:51 +0200 Subject: [PATCH 4/7] Add rustfmt.toml --- src/rust/.rustfmt.toml | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 src/rust/.rustfmt.toml diff --git a/src/rust/.rustfmt.toml b/src/rust/.rustfmt.toml new file mode 100644 index 0000000000..f25bd51883 --- /dev/null +++ b/src/rust/.rustfmt.toml @@ -0,0 +1,2 @@ +max_width = 80 +comment_width = 80 From d6f9a4f11acdfc8efea19a1644f355cdb90bfbb5 Mon Sep 17 00:00:00 2001 From: Sebastian Hahn Date: Tue, 2 May 2017 05:06:45 +0200 Subject: [PATCH 5/7] cargo-online-mode configure argument Passing --enable-cargo-online-mode during configure allows cargo to make network requests while building Tor or running tests. If this flag is not supplied, the dependencies need to be available in the form of a local mirror. --- configure.ac | 8 ++++++++ src/rust/tor_util/include.am | 2 +- src/test/include.am | 3 ++- src/test/test_rust.sh | 2 +- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/configure.ac b/configure.ac index d281903fcc..1a780d3c3b 100644 --- a/configure.ac +++ b/configure.ac @@ -57,6 +57,8 @@ AC_ARG_ENABLE(memory-sentinels, AS_HELP_STRING(--disable-memory-sentinels, [disable code that tries to prevent some kinds of memory access bugs. For fuzzing only.])) AC_ARG_ENABLE(rust, AS_HELP_STRING(--enable-rust, [enable rust integration])) +AC_ARG_ENABLE(cargo-online-mode, + AS_HELP_STRING(--enable-cargo-online-mode, [Allow cargo to make network requests to fetch crates. For builds with rust only.])) if test "x$enable_coverage" != "xyes" -a "x$enable_asserts_in_tests" = "xno" ; then AC_MSG_ERROR([Can't disable assertions outside of coverage build]) @@ -267,6 +269,12 @@ if test "x$enable_rust" = "xyes"; then fi AC_DEFINE([HAVE_RUST], 1, [have Rust]) + if test "x$enable_cargo_online_mode" = "xyes"; then + CARGO_ONLINE= + else + CARGO_ONLINE=--frozen + fi + AC_SUBST(CARGO_ONLINE) fi ifdef([AC_C_FLEXIBLE_ARRAY_MEMBER], [ diff --git a/src/rust/tor_util/include.am b/src/rust/tor_util/include.am index 3b877649a3..863046b929 100644 --- a/src/rust/tor_util/include.am +++ b/src/rust/tor_util/include.am @@ -7,6 +7,6 @@ EXTRA_DIST +=\ src/rust/target/release/libtor_util.a: FORCE ( cd "$(abs_top_srcdir)/src/rust/tor_util" ; \ CARGO_TARGET_DIR="$(abs_top_builddir)/src/rust/target" \ - $(CARGO) build --release --quiet --frozen ) + $(CARGO) build --release --quiet $(CARGO_ONLINE) ) FORCE: diff --git a/src/test/include.am b/src/test/include.am index 5fd61dd3e6..c0aca8afe2 100644 --- a/src/test/include.am +++ b/src/test/include.am @@ -7,7 +7,8 @@ TESTS_ENVIRONMENT = \ export abs_top_srcdir="$(abs_top_srcdir)"; \ export builddir="$(builddir)"; \ export TESTING_TOR_BINARY="$(TESTING_TOR_BINARY)"; \ - export CARGO="$(CARGO)"; + export CARGO="$(CARGO)"; \ + export CARGO_ONLINE="$(CARGO_ONLINE)"; TESTSCRIPTS = \ src/test/fuzz_static_testcases.sh \ diff --git a/src/test/test_rust.sh b/src/test/test_rust.sh index b41be978b9..7c71008e02 100755 --- a/src/test/test_rust.sh +++ b/src/test/test_rust.sh @@ -7,7 +7,7 @@ exitcode=0 for crate in $crates; do cd "${abs_top_srcdir:-.}/src/rust/${crate}" - "${CARGO:-cargo}" test --frozen || exitcode=1 + "${CARGO:-cargo}" test ${CARGO_ONLINE-"--frozen"} || exitcode=1 done exit $exitcode From aeba64efad02e596dc3f15832d4bb96184c366a3 Mon Sep 17 00:00:00 2001 From: Sebastian Hahn Date: Tue, 2 May 2017 06:46:41 +0200 Subject: [PATCH 6/7] Detect rustc version --- configure.ac | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/configure.ac b/configure.ac index 1a780d3c3b..c5d3d44514 100644 --- a/configure.ac +++ b/configure.ac @@ -275,6 +275,17 @@ if test "x$enable_rust" = "xyes"; then CARGO_ONLINE=--frozen fi AC_SUBST(CARGO_ONLINE) + +dnl Let's check the rustc version, too + AC_MSG_CHECKING([rust version]) + RUSTC_VERSION_MAJOR=`$RUSTC --version | cut -d ' ' -f 2 | cut -d '.' -f 1` + RUSTC_VERSION_MINOR=`$RUSTC --version | cut -d ' ' -f 2 | cut -d '.' -f 2` + if test "x$RUSTC_VERSION_MAJOR" = "x" -o "x$RUSTC_VERSION_MINOR" = "x"; then + AC_MSG_ERROR([rustc version couldn't be identified]) + fi + if test "$RUSTC_VERSION_MAJOR" -lt 2 -a "$RUSTC_VERSION_MINOR" -lt 14; then + AC_MSG_ERROR([rustc must be at least version 1.14]) + fi fi ifdef([AC_C_FLEXIBLE_ARRAY_MEMBER], [ From 70c067102b7d2576fa456d2872bb41abf559dff6 Mon Sep 17 00:00:00 2001 From: Sebastian Hahn Date: Tue, 2 May 2017 04:21:42 +0200 Subject: [PATCH 7/7] Allow Rust build using locally supplied crates or crates.io This adds a couple of configure commands to control whether we're requiring all dependencies to be available locally (default) or not (--enable-cargo-online-mode). When building from a tarball, we require the RUST_DEPENDENCIES variable to point to the local repository of crates. This also adds src/ext/rust as a git submodule that contains such a local repository for easy setup. --- .gitignore | 5 +++++ .gitmodules | 3 +++ Makefile.am | 1 + configure.ac | 29 +++++++++++++++++++++++++++++ src/ext/rust | 1 + src/rust/.cargo/config.in | 8 ++++++++ src/rust/Cargo.toml | 7 ------- src/rust/include.am | 3 ++- src/rust/tor_util/include.am | 1 + src/test/include.am | 1 + src/test/test_rust.sh | 2 +- 11 files changed, 52 insertions(+), 9 deletions(-) create mode 100644 .gitmodules create mode 160000 src/ext/rust create mode 100644 src/rust/.cargo/config.in diff --git a/.gitignore b/.gitignore index 68bad5f113..0e0640de2d 100644 --- a/.gitignore +++ b/.gitignore @@ -173,6 +173,11 @@ uptime-*.json /src/or/libtor-testing.a /src/or/libtor.lib +# /src/rust +/src/rust/.cargo/config +/src/rust/.cargo/registry +/src/rust/target + # /src/test /src/test/Makefile /src/test/Makefile.in diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000..7074403c9c --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "src/ext/rust"] + path = src/ext/rust + url = https://git.torproject.org/user/sebastian/tor-rust-dependencies diff --git a/Makefile.am b/Makefile.am index be4140aa0b..79d2e78521 100644 --- a/Makefile.am +++ b/Makefile.am @@ -239,3 +239,4 @@ mostlyclean-local: clean-local: rm -rf $(top_builddir)/src/rust/target + rm -rf $(top_builddir)/src/rust/.cargo/registry diff --git a/configure.ac b/configure.ac index c5d3d44514..9f67fe5c94 100644 --- a/configure.ac +++ b/configure.ac @@ -254,6 +254,9 @@ if test "x$PYTHON" = "x"; then fi AM_CONDITIONAL(USEPYTHON, [test "x$PYTHON" != "x"]) +dnl List all external rust crates we depend on here. Include the version +rust_crates="libc-0.2.22" +AC_SUBST(rust_crates) if test "x$enable_rust" = "xyes"; then AC_ARG_VAR([RUSTC], [path to the rustc binary]) @@ -271,10 +274,35 @@ if test "x$enable_rust" = "xyes"; then AC_DEFINE([HAVE_RUST], 1, [have Rust]) if test "x$enable_cargo_online_mode" = "xyes"; then CARGO_ONLINE= + RUST_DL=# else CARGO_ONLINE=--frozen + RUST_DL= + + dnl When we're not allowed to touch the network, we need crate dependencies + dnl locally available. + AC_MSG_CHECKING([rust crate dependencies]) + AC_ARG_VAR([RUST_DEPENDENCIES], [path to directory with local crate mirror]) + if test "x$RUST_DEPENDENCIES" = "x"; then + RUST_DEPENDENCIES="$srcdir/src/ext/rust/" + NEED_MOD=1 + fi + if test ! -d "$RUST_DEPENDENCIES"; then + AC_MSG_ERROR([Rust dependency directory $RUST_DEPENDENCIES does not exist. Specify a dependency directory using the RUST_DEPENDENCIES variable or allow cargo to fetch crates using --enable-cargo-online-mode.]) + fi + for dep in $rust_crates; do + if test ! -d "$RUST_DEPENDENCIES"/"$dep"; then + AC_MSG_ERROR([Failure to find rust dependency $RUST_DEPENDENCIES/$dep. Specify a dependency directory using the RUST_DEPENDENCIES variable or allow cargo to fetch crates using --enable-cargo-online-mode.]) + fi + done + if test "x$NEED_MOD" = "x1"; then + dnl When looking for dependencies from cargo, pick right directory + RUST_DEPENDENCIES="../../src/ext/rust" + fi fi + AC_SUBST(CARGO_ONLINE) + AC_SUBST(RUST_DL) dnl Let's check the rustc version, too AC_MSG_CHECKING([rust version]) @@ -2065,6 +2093,7 @@ AC_CONFIG_FILES([ contrib/dist/tor.service src/config/torrc.sample src/config/torrc.minimal + src/rust/.cargo/config scripts/maint/checkOptionDocs.pl scripts/maint/updateVersions.pl ]) diff --git a/src/ext/rust b/src/ext/rust new file mode 160000 index 0000000000..2402968008 --- /dev/null +++ b/src/ext/rust @@ -0,0 +1 @@ +Subproject commit 240296800824e40b10cb8c16da0e711563353945 diff --git a/src/rust/.cargo/config.in b/src/rust/.cargo/config.in new file mode 100644 index 0000000000..414b253a57 --- /dev/null +++ b/src/rust/.cargo/config.in @@ -0,0 +1,8 @@ +[source] + +@RUST_DL@ [source.crates-io] +@RUST_DL@ registry = 'https://github.com/rust-lang/crates.io-index' +@RUST_DL@ replace-with = 'vendored-sources' + +@RUST_DL@ [source.vendored-sources] +@RUST_DL@ directory = '@RUST_DEPENDENCIES@' diff --git a/src/rust/Cargo.toml b/src/rust/Cargo.toml index 527c536323..fc4377e8b4 100644 --- a/src/rust/Cargo.toml +++ b/src/rust/Cargo.toml @@ -5,10 +5,3 @@ members = ["tor_util"] debug = true panic = "abort" -[source.crates-io] -registry = 'https://github.com/rust-lang/crates.io-index' -replace-with = 'vendored-sources' - -[source.vendored-sources] -directory = 'vendor' - diff --git a/src/rust/include.am b/src/rust/include.am index e198049071..20afc6c4db 100644 --- a/src/rust/include.am +++ b/src/rust/include.am @@ -2,4 +2,5 @@ include src/rust/tor_util/include.am EXTRA_DIST +=\ src/rust/Cargo.toml \ - src/rust/Cargo.lock + src/rust/Cargo.lock \ + src/rust/.cargo/config.in diff --git a/src/rust/tor_util/include.am b/src/rust/tor_util/include.am index 863046b929..17a755fe09 100644 --- a/src/rust/tor_util/include.am +++ b/src/rust/tor_util/include.am @@ -7,6 +7,7 @@ EXTRA_DIST +=\ src/rust/target/release/libtor_util.a: FORCE ( cd "$(abs_top_srcdir)/src/rust/tor_util" ; \ CARGO_TARGET_DIR="$(abs_top_builddir)/src/rust/target" \ + HOME="$(abs_top_builddir)/src/rust" \ $(CARGO) build --release --quiet $(CARGO_ONLINE) ) FORCE: diff --git a/src/test/include.am b/src/test/include.am index c0aca8afe2..829c282dd9 100644 --- a/src/test/include.am +++ b/src/test/include.am @@ -5,6 +5,7 @@ TESTS_ENVIRONMENT = \ export PYTHON="$(PYTHON)"; \ export SHELL="$(SHELL)"; \ export abs_top_srcdir="$(abs_top_srcdir)"; \ + export abs_top_builddir="$(abs_top_builddir)"; \ export builddir="$(builddir)"; \ export TESTING_TOR_BINARY="$(TESTING_TOR_BINARY)"; \ export CARGO="$(CARGO)"; \ diff --git a/src/test/test_rust.sh b/src/test/test_rust.sh index 7c71008e02..4427c70f13 100755 --- a/src/test/test_rust.sh +++ b/src/test/test_rust.sh @@ -7,7 +7,7 @@ exitcode=0 for crate in $crates; do cd "${abs_top_srcdir:-.}/src/rust/${crate}" - "${CARGO:-cargo}" test ${CARGO_ONLINE-"--frozen"} || exitcode=1 + CARGO_TARGET_DIR="${abs_top_builddir}/src/rust/target" HOME="${abs_top_builddir}/src/rust" "${CARGO:-cargo}" test ${CARGO_ONLINE-"--frozen"} || exitcode=1 done exit $exitcode