summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNick Mathewson <nickm@torproject.org>2018-03-19 17:20:37 -0400
committerNick Mathewson <nickm@torproject.org>2018-03-19 17:20:37 -0400
commitd8893bc93c52e1edd0a76b81f5d65bb10d790474 (patch)
treef6bb243d9fbeb179c7a5a3a79a19752ba970c98e
parent3716611fea1c400e80ab3cfd19c588e32b1c005c (diff)
parent1f8bd93ecbb57eb47c5788f4a4d12f3bdee61b47 (diff)
downloadtor-d8893bc93c52e1edd0a76b81f5d65bb10d790474.tar.gz
tor-d8893bc93c52e1edd0a76b81f5d65bb10d790474.zip
Merge remote-tracking branch 'isis/bug23881_r1'
-rw-r--r--src/common/log.c31
-rw-r--r--src/common/torlog.h14
-rw-r--r--src/or/main.c8
-rw-r--r--src/rust/Cargo.lock10
-rw-r--r--src/rust/Cargo.toml3
-rw-r--r--src/rust/protover/Cargo.toml6
-rw-r--r--src/rust/protover/ffi.rs8
-rw-r--r--src/rust/protover/lib.rs3
-rw-r--r--src/rust/protover/protover.rs20
-rw-r--r--src/rust/tor_allocate/tor_allocate.rs19
-rw-r--r--src/rust/tor_log/Cargo.toml18
-rw-r--r--src/rust/tor_log/lib.rs16
-rw-r--r--src/rust/tor_log/tor_log.rs270
-rw-r--r--src/rust/tor_util/Cargo.toml3
-rw-r--r--src/rust/tor_util/ffi.rs13
-rw-r--r--src/rust/tor_util/lib.rs3
-rwxr-xr-xsrc/test/test_rust.sh2
17 files changed, 423 insertions, 24 deletions
diff --git a/src/common/log.c b/src/common/log.c
index 9f4a8b2bc2..922e9dd38f 100644
--- a/src/common/log.c
+++ b/src/common/log.c
@@ -52,6 +52,13 @@
#define raw_assert(x) assert(x) // assert OK
+/** Defining compile-time constants for Tor log levels (used by the Rust
+ * log wrapper at src/rust/tor_log) */
+const int LOG_WARN_ = LOG_WARN;
+const int LOG_NOTICE_ = LOG_NOTICE;
+const log_domain_mask_t LD_GENERAL_ = LD_GENERAL;
+const log_domain_mask_t LD_NET_ = LD_NET;
+
/** Information for a single logfile; only used in log.c */
typedef struct logfile_t {
struct logfile_t *next; /**< Next logfile_t in the linked list. */
@@ -225,6 +232,30 @@ log_set_application_name(const char *name)
appname = name ? tor_strdup(name) : NULL;
}
+/** Return true if some of the running logs might be interested in a log
+ * message of the given severity in the given domains. If this function
+ * returns true, the log message might be ignored anyway, but if it returns
+ * false, it is definitely_ safe not to log the message. */
+int
+log_message_is_interesting(int severity, log_domain_mask_t domain)
+{
+ (void) domain;
+ return (severity <= log_global_min_severity_);
+}
+
+/**
+ * As tor_log, but takes an optional function name, and does not treat its
+ * <b>string</b> as a printf format.
+ *
+ * For use by Rust integration.
+ */
+void
+tor_log_string(int severity, log_domain_mask_t domain,
+ const char *function, const char *string)
+{
+ log_fn_(severity, domain, function, "%s", string);
+}
+
/** Log time granularity in milliseconds. */
static int log_time_granularity = 1;
diff --git a/src/common/torlog.h b/src/common/torlog.h
index cadfe3b879..a8b5604d84 100644
--- a/src/common/torlog.h
+++ b/src/common/torlog.h
@@ -31,6 +31,16 @@
* "maximum severity" read "most severe" and "numerically *lowest* severity".
*/
+/** This defines log levels that are linked in the Rust log module, rather
+ * than re-defining these in both Rust and C.
+ *
+ * C_RUST_COUPLED src/rust/tor_log LogSeverity, LogDomain
+ */
+extern const int LOG_WARN_;
+extern const int LOG_NOTICE_;
+extern const log_domain_mask_t LD_NET_;
+extern const log_domain_mask_t LD_GENERAL_;
+
/** Debug-level severity: for hyper-verbose messages of no interest to
* anybody but developers. */
#define LOG_DEBUG 7
@@ -191,6 +201,10 @@ void log_fn_ratelim_(struct ratelim_t *ratelim, int severity,
const char *format, ...)
CHECK_PRINTF(5,6);
+int log_message_is_interesting(int severity, log_domain_mask_t domain);
+void tor_log_string(int severity, log_domain_mask_t domain,
+ const char *function, const char *string);
+
#if defined(__GNUC__) && __GNUC__ <= 3
/* These are the GCC varidaic macros, so that older versions of GCC don't
diff --git a/src/or/main.c b/src/or/main.c
index 65340751ef..a0d2ae0757 100644
--- a/src/or/main.c
+++ b/src/or/main.c
@@ -133,7 +133,7 @@ void evdns_shutdown(int);
#ifdef HAVE_RUST
// helper function defined in Rust to output a log message indicating if tor is
// running with Rust enabled. See src/rust/tor_util
-char *rust_welcome_string(void);
+void rust_log_welcome_string(void);
#endif
/********* PROTOTYPES **********/
@@ -3344,11 +3344,7 @@ tor_init(int argc, char *argv[])
}
#ifdef HAVE_RUST
- char *rust_str = rust_welcome_string();
- if (rust_str != NULL && strlen(rust_str) > 0) {
- log_notice(LD_GENERAL, "%s", rust_str);
- }
- tor_free(rust_str);
+ rust_log_welcome_string();
#endif /* defined(HAVE_RUST) */
if (network_init()<0) {
diff --git a/src/rust/Cargo.lock b/src/rust/Cargo.lock
index 224d2135bf..116ef17f1c 100644
--- a/src/rust/Cargo.lock
+++ b/src/rust/Cargo.lock
@@ -4,6 +4,7 @@ version = "0.0.1"
dependencies = [
"libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)",
"tor_allocate 0.0.1",
+ "tor_log 0.1.0",
]
[[package]]
@@ -26,6 +27,7 @@ dependencies = [
"libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)",
"smartlist 0.0.1",
"tor_allocate 0.0.1",
+ "tor_log 0.1.0",
"tor_util 0.0.1",
]
@@ -44,6 +46,14 @@ dependencies = [
]
[[package]]
+name = "tor_log"
+version = "0.1.0"
+dependencies = [
+ "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tor_allocate 0.0.1",
+]
+
+[[package]]
name = "tor_rust"
version = "0.1.0"
dependencies = [
diff --git a/src/rust/Cargo.toml b/src/rust/Cargo.toml
index 953c9b96b7..4ae8033eb3 100644
--- a/src/rust/Cargo.toml
+++ b/src/rust/Cargo.toml
@@ -1,5 +1,6 @@
[workspace]
-members = ["tor_util", "protover", "smartlist", "external", "tor_allocate", "tor_rust"]
+members = ["tor_util", "protover", "smartlist", "external", "tor_allocate",
+"tor_rust", "tor_log"]
[profile.release]
debug = true
diff --git a/src/rust/protover/Cargo.toml b/src/rust/protover/Cargo.toml
index 04d2f2ed7d..3f70155ef8 100644
--- a/src/rust/protover/Cargo.toml
+++ b/src/rust/protover/Cargo.toml
@@ -3,6 +3,9 @@ authors = ["The Tor Project"]
version = "0.0.1"
name = "protover"
+[features]
+testing = ["tor_log/testing"]
+
[dependencies]
libc = "0.2.22"
@@ -18,6 +21,9 @@ path = "../tor_util"
[dependencies.tor_allocate]
path = "../tor_allocate"
+[dependencies.tor_log]
+path = "../tor_log"
+
[lib]
name = "protover"
path = "lib.rs"
diff --git a/src/rust/protover/ffi.rs b/src/rust/protover/ffi.rs
index e4cf0a7e88..a9d5013c6d 100644
--- a/src/rust/protover/ffi.rs
+++ b/src/rust/protover/ffi.rs
@@ -127,9 +127,11 @@ pub extern "C" fn protocol_list_supports_protocol_or_later(
Err(_) => return 0,
};
- let is_supported =
- protover_string_supports_protocol_or_later(
- protocol_list, protocol, version);
+ let is_supported = protover_string_supports_protocol_or_later(
+ protocol_list,
+ protocol,
+ version,
+ );
return if is_supported { 1 } else { 0 };
}
diff --git a/src/rust/protover/lib.rs b/src/rust/protover/lib.rs
index 8e80fcef4c..d1d49d2a59 100644
--- a/src/rust/protover/lib.rs
+++ b/src/rust/protover/lib.rs
@@ -29,6 +29,9 @@ extern crate tor_allocate;
#[macro_use]
extern crate tor_util;
+#[macro_use]
+extern crate tor_log;
+
mod protover;
pub mod ffi;
diff --git a/src/rust/protover/protover.rs b/src/rust/protover/protover.rs
index 03776411ca..fd1f41d780 100644
--- a/src/rust/protover/protover.rs
+++ b/src/rust/protover/protover.rs
@@ -1,8 +1,6 @@
// Copyright (c) 2016-2017, The Tor Project, Inc. */
// See LICENSE for licensing information */
-use external::c_tor_version_as_new_as;
-
use std::str;
use std::str::FromStr;
use std::ffi::CStr;
@@ -12,6 +10,9 @@ use std::ops::Range;
use std::string::String;
use std::u32;
+use tor_log::{LogSeverity, LogDomain};
+use external::c_tor_version_as_new_as;
+
/// The first version of Tor that included "proto" entries in its descriptors.
/// Authorities should use this to decide whether to guess proto lines.
///
@@ -225,7 +226,6 @@ impl Versions {
}
}
-
/// Parse the subprotocol type and its version numbers.
///
/// # Inputs
@@ -279,6 +279,20 @@ fn get_proto_and_vers<'a>(
fn contains_only_supported_protocols(proto_entry: &str) -> bool {
let (name, mut vers) = match get_proto_and_vers(proto_entry) {
Ok(n) => n,
+ Err("Too many versions to expand") => {
+ tor_log_msg!(
+ LogSeverity::Warn,
+ LogDomain::Net,
+ "get_versions",
+ "When expanding a protocol list from an authority, I \
+ got too many protocols. This is possibly an attack or a bug, \
+ unless the Tor network truly has expanded to support over {} \
+ different subprotocol versions. The offending string was: {}",
+ MAX_PROTOCOLS_TO_EXPAND,
+ proto_entry
+ );
+ return false;
+ }
Err(_) => return false,
};
diff --git a/src/rust/tor_allocate/tor_allocate.rs b/src/rust/tor_allocate/tor_allocate.rs
index 359df1cd7a..3c0037f139 100644
--- a/src/rust/tor_allocate/tor_allocate.rs
+++ b/src/rust/tor_allocate/tor_allocate.rs
@@ -1,12 +1,17 @@
// Copyright (c) 2016-2017, The Tor Project, Inc. */
// See LICENSE for licensing information */
+// No-op defined purely for testing at the module level
+use libc::c_char;
-use libc::{c_char, c_void};
+#[cfg(not(feature = "testing"))]
use std::{ptr, slice, mem};
+use libc::c_void;
-#[cfg(not(test))]
-extern "C" {
- fn tor_malloc_(size: usize) -> *mut c_void;
+// Define a no-op implementation for testing Rust modules without linking to C
+#[cfg(feature = "testing")]
+pub fn allocate_and_copy_string(s: &String) -> *mut c_char {
+ use std::ffi::CString;
+ CString::new(s.as_str()).unwrap().into_raw()
}
// Defined only for tests, used for testing purposes, so that we don't need
@@ -17,6 +22,11 @@ unsafe extern "C" fn tor_malloc_(size: usize) -> *mut c_void {
malloc(size)
}
+#[cfg(all(not(test), not(feature = "testing")))]
+extern "C" {
+ fn tor_malloc_(size: usize) -> *mut c_void;
+}
+
/// Allocate memory using tor_malloc_ and copy an existing string into the
/// allocated buffer, returning a pointer that can later be called in C.
///
@@ -28,6 +38,7 @@ unsafe extern "C" fn tor_malloc_(size: usize) -> *mut c_void {
///
/// A `*mut c_char` that should be freed by tor_free in C
///
+#[cfg(not(feature = "testing"))]
pub fn allocate_and_copy_string(src: &String) -> *mut c_char {
let bytes: &[u8] = src.as_bytes();
diff --git a/src/rust/tor_log/Cargo.toml b/src/rust/tor_log/Cargo.toml
new file mode 100644
index 0000000000..f31d27b045
--- /dev/null
+++ b/src/rust/tor_log/Cargo.toml
@@ -0,0 +1,18 @@
+[package]
+name = "tor_log"
+version = "0.1.0"
+authors = ["The Tor Project"]
+
+[lib]
+name = "tor_log"
+path = "lib.rs"
+crate_type = ["rlib", "staticlib"]
+
+[features]
+testing = []
+
+[dependencies]
+libc = "0.2.22"
+
+[dependencies.tor_allocate]
+path = "../tor_allocate"
diff --git a/src/rust/tor_log/lib.rs b/src/rust/tor_log/lib.rs
new file mode 100644
index 0000000000..72f9e38339
--- /dev/null
+++ b/src/rust/tor_log/lib.rs
@@ -0,0 +1,16 @@
+//! Copyright (c) 2016-2017, The Tor Project, Inc. */
+//! See LICENSE for licensing information */
+
+//! Logging wrapper for Rust to utilize Tor's logger, found at
+//! src/common/log.c and src/common/torlog.h
+//!
+//! Exposes different interfaces depending on whether we are running in test
+//! or non-test mode. When testing, we use a no-op implementation,
+//! otherwise we link directly to C.
+
+extern crate libc;
+extern crate tor_allocate;
+
+mod tor_log;
+
+pub use tor_log::*;
diff --git a/src/rust/tor_log/tor_log.rs b/src/rust/tor_log/tor_log.rs
new file mode 100644
index 0000000000..1fdc0026bf
--- /dev/null
+++ b/src/rust/tor_log/tor_log.rs
@@ -0,0 +1,270 @@
+// Copyright (c) 2016-2017, The Tor Project, Inc. */
+// See LICENSE for licensing information */
+
+// Note that these functions are untested due to the fact that there are no
+// return variables to test and they are calling into a C API.
+
+/// The related domain which the logging message is relevant. For example,
+/// log messages relevant to networking would use LogDomain::LdNet, whereas
+/// general messages can use LdGeneral.
+#[derive(Eq, PartialEq)]
+pub enum LogDomain {
+ Net,
+ General,
+}
+
+/// The severity level at which to log messages.
+#[derive(Eq, PartialEq)]
+pub enum LogSeverity {
+ Notice,
+ Warn,
+}
+
+/// Main entry point for Rust modules to log messages.
+///
+/// # Inputs
+///
+/// * A `severity` of type LogSeverity, which defines the level of severity the
+/// message will be logged.
+/// * A `domain` of type LogDomain, which defines the domain the log message
+/// will be associated with.
+/// * A `function` of type &str, which defines the name of the function where
+/// the message is being logged. There is a current RFC for a macro that
+/// defines function names. When it is, we should use it. See
+/// https://github.com/rust-lang/rfcs/pull/1719
+/// * A `message` of type &str, which is the log message itself.
+#[macro_export]
+macro_rules! tor_log_msg {
+ ($severity: path,
+ $domain: path,
+ $function: expr,
+ $($message:tt)*) =>
+ {
+ {
+ let msg = format!($($message)*);
+ $crate::tor_log_msg_impl($severity, $domain, $function, msg)
+ }
+ };
+}
+
+#[inline]
+pub fn tor_log_msg_impl(
+ severity: LogSeverity,
+ domain: LogDomain,
+ function: &str,
+ message: String,
+) {
+ use std::ffi::CString;
+
+ /// Default function name to log in case of errors when converting
+ /// a function name to a CString
+ const ERR_LOG_FUNCTION: &str = "tor_log_msg";
+
+ /// Default message to log in case of errors when converting a log
+ /// message to a CString
+ const ERR_LOG_MSG: &str = "Unable to log message from Rust \
+ module due to error when converting to CString";
+
+ let func = match CString::new(function) {
+ Ok(n) => n,
+ Err(_) => CString::new(ERR_LOG_FUNCTION).unwrap(),
+ };
+
+ let msg = match CString::new(message) {
+ Ok(n) => n,
+ Err(_) => CString::new(ERR_LOG_MSG).unwrap(),
+ };
+
+ // Bind to a local variable to preserve ownership. This is essential so
+ // that ownership is guaranteed until these local variables go out of scope
+ let func_ptr = func.as_ptr();
+ let msg_ptr = msg.as_ptr();
+
+ let c_severity = unsafe { log::translate_severity(severity) };
+ let c_domain = unsafe { log::translate_domain(domain) };
+
+ unsafe { log::tor_log_string(c_severity, c_domain, func_ptr, msg_ptr) }
+}
+
+/// This implementation is used when compiling for actual use, as opposed to
+/// testing.
+#[cfg(all(not(test), not(feature = "testing")))]
+pub mod log {
+ use libc::{c_char, c_int};
+ use super::LogDomain;
+ use super::LogSeverity;
+
+ /// Severity log types. These mirror definitions in /src/common/torlog.h
+ /// C_RUST_COUPLED: src/common/log.c, log domain types
+ extern "C" {
+ static LOG_WARN_: c_int;
+ static LOG_NOTICE_: c_int;
+ }
+
+ /// Domain log types. These mirror definitions in /src/common/torlog.h
+ /// C_RUST_COUPLED: src/common/log.c, log severity types
+ extern "C" {
+ static LD_NET_: u32;
+ static LD_GENERAL_: u32;
+ }
+
+ /// Translate Rust defintions of log domain levels to C. This exposes a 1:1
+ /// mapping between types.
+ #[inline]
+ pub unsafe fn translate_domain(domain: LogDomain) -> u32 {
+ match domain {
+ LogDomain::Net => LD_NET_,
+ LogDomain::General => LD_GENERAL_,
+ }
+ }
+
+ /// Translate Rust defintions of log severity levels to C. This exposes a
+ /// 1:1 mapping between types.
+ #[inline]
+ pub unsafe fn translate_severity(severity: LogSeverity) -> c_int {
+ match severity {
+ LogSeverity::Warn => LOG_WARN_,
+ LogSeverity::Notice => LOG_NOTICE_,
+ }
+ }
+
+ /// The main entry point into Tor's logger. When in non-test mode, this
+ /// will link directly with `tor_log_string` in /src/or/log.c
+ extern "C" {
+ pub fn tor_log_string(
+ severity: c_int,
+ domain: u32,
+ function: *const c_char,
+ string: *const c_char,
+ );
+ }
+}
+
+/// This module exposes no-op functionality for testing other Rust modules
+/// without linking to C.
+#[cfg(any(test, feature = "testing"))]
+pub mod log {
+ use libc::{c_char, c_int};
+ use super::LogDomain;
+ use super::LogSeverity;
+
+ pub static mut LAST_LOGGED_FUNCTION: *mut String = 0 as *mut String;
+ pub static mut LAST_LOGGED_MESSAGE: *mut String = 0 as *mut String;
+
+ pub unsafe fn tor_log_string(
+ _severity: c_int,
+ _domain: u32,
+ function: *const c_char,
+ message: *const c_char,
+ ) {
+ use std::ffi::CStr;
+
+ let f = CStr::from_ptr(function);
+ let fct = match f.to_str() {
+ Ok(n) => n,
+ Err(_) => "",
+ };
+ LAST_LOGGED_FUNCTION = Box::into_raw(Box::new(String::from(fct)));
+
+ let m = CStr::from_ptr(message);
+ let msg = match m.to_str() {
+ Ok(n) => n,
+ Err(_) => "",
+ };
+ LAST_LOGGED_MESSAGE = Box::into_raw(Box::new(String::from(msg)));
+ }
+
+ pub unsafe fn translate_domain(_domain: LogDomain) -> u32 {
+ 1
+ }
+
+ pub unsafe fn translate_severity(_severity: LogSeverity) -> c_int {
+ 1
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use tor_log::*;
+ use tor_log::log::{LAST_LOGGED_FUNCTION, LAST_LOGGED_MESSAGE};
+
+ #[test]
+ fn test_get_log_message() {
+ {
+ fn test_macro() {
+ tor_log_msg!(
+ LogSeverity::Warn,
+ LogDomain::Net,
+ "test_macro",
+ "test log message {}",
+ "a",
+ );
+ }
+
+ test_macro();
+
+ let function = unsafe { Box::from_raw(LAST_LOGGED_FUNCTION) };
+ assert_eq!("test_macro", *function);
+
+ let message = unsafe { Box::from_raw(LAST_LOGGED_MESSAGE) };
+ assert_eq!("test log message a", *message);
+ }
+
+ // test multiple inputs into the log message
+ {
+ fn test_macro() {
+ tor_log_msg!(
+ LogSeverity::Warn,
+ LogDomain::Net,
+ "next_test_macro",
+ "test log message {} {} {} {} {}",
+ 1,
+ 2,
+ 3,
+ 4,
+ 5
+ );
+ }
+
+ test_macro();
+
+ let function = unsafe { Box::from_raw(LAST_LOGGED_FUNCTION) };
+ assert_eq!("next_test_macro", *function);
+
+ let message = unsafe { Box::from_raw(LAST_LOGGED_MESSAGE) };
+ assert_eq!("test log message 1 2 3 4 5", *message);
+ }
+
+ // test how a long log message will be formatted
+ {
+ fn test_macro() {
+ tor_log_msg!(
+ LogSeverity::Warn,
+ LogDomain::Net,
+ "test_macro",
+ "{}",
+ "All the world's a stage, and all the men and women \
+ merely players: they have their exits and their \
+ entrances; and one man in his time plays many parts, his \
+ acts being seven ages."
+ );
+ }
+
+ test_macro();
+
+ let expected_string = "All the world's a \
+ stage, and all the men \
+ and women merely players: \
+ they have their exits and \
+ their entrances; and one man \
+ in his time plays many parts, \
+ his acts being seven ages.";
+
+ let function = unsafe { Box::from_raw(LAST_LOGGED_FUNCTION) };
+ assert_eq!("test_macro", *function);
+
+ let message = unsafe { Box::from_raw(LAST_LOGGED_MESSAGE) };
+ assert_eq!(expected_string, *message);
+ }
+ }
+}
diff --git a/src/rust/tor_util/Cargo.toml b/src/rust/tor_util/Cargo.toml
index d7379a5988..adc3390b53 100644
--- a/src/rust/tor_util/Cargo.toml
+++ b/src/rust/tor_util/Cargo.toml
@@ -11,6 +11,9 @@ crate_type = ["rlib", "staticlib"]
[dependencies.tor_allocate]
path = "../tor_allocate"
+[dependencies.tor_log]
+path = "../tor_log"
+
[dependencies]
libc = "0.2.22"
diff --git a/src/rust/tor_util/ffi.rs b/src/rust/tor_util/ffi.rs
index 5c3cdba4be..32779ed476 100644
--- a/src/rust/tor_util/ffi.rs
+++ b/src/rust/tor_util/ffi.rs
@@ -5,8 +5,7 @@
//! called from C.
//!
-use libc::c_char;
-use tor_allocate::allocate_and_copy_string;
+use tor_log::{LogSeverity, LogDomain};
/// Returns a short string to announce Rust support during startup.
///
@@ -17,10 +16,12 @@ use tor_allocate::allocate_and_copy_string;
/// tor_free(rust_str);
/// ```
#[no_mangle]
-pub extern "C" fn rust_welcome_string() -> *mut c_char {
- let rust_welcome = String::from(
+pub extern "C" fn rust_log_welcome_string() {
+ tor_log_msg!(
+ LogSeverity::Notice,
+ LogDomain::General,
+ "rust_log_welcome_string",
"Tor is running with Rust integration. Please report \
- any bugs you encounter.",
+ any bugs you encounter."
);
- allocate_and_copy_string(&rust_welcome)
}
diff --git a/src/rust/tor_util/lib.rs b/src/rust/tor_util/lib.rs
index 12cb3896b6..94697b6069 100644
--- a/src/rust/tor_util/lib.rs
+++ b/src/rust/tor_util/lib.rs
@@ -7,5 +7,8 @@
extern crate libc;
extern crate tor_allocate;
+#[macro_use]
+extern crate tor_log;
+
pub mod ffi;
pub mod strings;
diff --git a/src/test/test_rust.sh b/src/test/test_rust.sh
index 133f2bb940..0268668b3d 100755
--- a/src/test/test_rust.sh
+++ b/src/test/test_rust.sh
@@ -11,7 +11,7 @@ for crate in $crates; do
cd "${abs_top_builddir:-../../..}/src/rust"
CARGO_TARGET_DIR="${abs_top_builddir:-../../..}/src/rust/target" \
CARGO_HOME="${abs_top_builddir:-../../..}/src/rust" \
- "${CARGO:-cargo}" test ${CARGO_ONLINE-"--frozen"} \
+ "${CARGO:-cargo}" test --all-features ${CARGO_ONLINE-"--frozen"} \
--manifest-path "${abs_top_srcdir:-.}/src/rust/${crate}/Cargo.toml" \
|| exitcode=1
cd -