From dba28d4ac9470ca38890bb79b64ca1c7b5b8d0e7 Mon Sep 17 00:00:00 2001 From: Amanieu d'Antras Date: Wed, 1 Nov 2023 00:23:52 +0000 Subject: [PATCH 1/2] Remove build-time check for `stdsimd` feature This is blocking rust-lang/rust#117372, which replaces `stdsimd` with more fine-grained features. However the auto-detection in aHash breaks when bootstrapping Rust because it detects the `stdsimd` feature on the old toolchain which is not present on the newly build libcore. This PR removes the build-time detection of the `stdsimd` feature and instead just uses the ARM AES intrinsics directly since they are now stable, but only on AArch64. --- build.rs | 3 --- src/hash_quality_test.rs | 7 +------ src/lib.rs | 9 ++------- src/operations.rs | 14 ++------------ src/random_state.rs | 2 +- tests/bench.rs | 7 +------ 6 files changed, 7 insertions(+), 35 deletions(-) diff --git a/build.rs b/build.rs index 0c5b769..a136b36 100644 --- a/build.rs +++ b/build.rs @@ -7,9 +7,6 @@ fn main() { if let Some(true) = version_check::supports_feature("specialize") { println!("cargo:rustc-cfg=feature=\"specialize\""); } - if let Some(true) = version_check::supports_feature("stdsimd") { - println!("cargo:rustc-cfg=feature=\"stdsimd\""); - } let arch = env::var("CARGO_CFG_TARGET_ARCH").expect("CARGO_CFG_TARGET_ARCH was not set"); if arch.eq_ignore_ascii_case("x86_64") || arch.eq_ignore_ascii_case("aarch64") diff --git a/src/hash_quality_test.rs b/src/hash_quality_test.rs index 25356e4..c510d21 100644 --- a/src/hash_quality_test.rs +++ b/src/hash_quality_test.rs @@ -437,12 +437,7 @@ mod fallback_tests { ///Basic sanity tests of the cypto properties of aHash. #[cfg(any( all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes", not(miri)), - all( - any(target_arch = "arm", target_arch = "aarch64"), - any(target_feature = "aes", target_feature = "crypto"), - not(miri), - feature = "stdsimd" - ) + all(target_arch = "aarch64", target_feature = "aes", not(miri)), ))] #[cfg(test)] mod aes_tests { diff --git a/src/lib.rs b/src/lib.rs index 978f424..92100e5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -95,8 +95,6 @@ Note the import of [HashMapExt]. This is needed for the constructor. #![allow(clippy::pedantic, clippy::cast_lossless, clippy::unreadable_literal)] #![cfg_attr(all(not(test), not(feature = "std")), no_std)] #![cfg_attr(feature = "specialize", feature(min_specialization))] -#![cfg_attr(feature = "specialize", feature(build_hasher_simple_hash_one))] -#![cfg_attr(feature = "stdsimd", feature(stdsimd))] #[macro_use] mod convert; @@ -106,11 +104,8 @@ mod fallback_hash; cfg_if::cfg_if! { if #[cfg(any( all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes", not(miri)), - all(any(target_arch = "arm", target_arch = "aarch64"), - any(target_feature = "aes", target_feature = "crypto"), - not(miri), - feature = "stdsimd") - ))] { + all(target_arch = "aarch64", target_feature = "aes", not(miri)), + ))] { mod aes_hash; pub use crate::aes_hash::AHasher; } else { diff --git a/src/operations.rs b/src/operations.rs index 23d4e22..5cb368f 100644 --- a/src/operations.rs +++ b/src/operations.rs @@ -110,12 +110,7 @@ pub(crate) fn aesenc(value: u128, xor: u128) -> u128 { } } -#[cfg(all( - any(target_arch = "arm", target_arch = "aarch64"), - any(target_feature = "aes", target_feature = "crypto"), - not(miri), - feature = "stdsimd" -))] +#[cfg(all(target_arch = "aarch64", target_feature = "aes", not(miri)))] #[allow(unused)] #[inline(always)] pub(crate) fn aesenc(value: u128, xor: u128) -> u128 { @@ -142,12 +137,7 @@ pub(crate) fn aesdec(value: u128, xor: u128) -> u128 { } } -#[cfg(all( - any(target_arch = "arm", target_arch = "aarch64"), - any(target_feature = "aes", target_feature = "crypto"), - not(miri), - feature = "stdsimd" -))] +#[cfg(all(target_arch = "aarch64", target_feature = "aes", not(miri)))] #[allow(unused)] #[inline(always)] pub(crate) fn aesdec(value: u128, xor: u128) -> u128 { diff --git a/src/random_state.rs b/src/random_state.rs index 54b754d..bc29af8 100644 --- a/src/random_state.rs +++ b/src/random_state.rs @@ -2,7 +2,7 @@ use core::hash::Hash; cfg_if::cfg_if! { if #[cfg(any( all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes", not(miri)), - all(any(target_arch = "arm", target_arch = "aarch64"), any(target_feature = "aes", target_feature = "crypto"), not(miri), feature = "stdsimd") + all(target_arch = "aarch64", target_feature = "aes", not(miri)), ))] { use crate::aes_hash::*; } else { diff --git a/tests/bench.rs b/tests/bench.rs index 5bc0fc9..f1a1414 100644 --- a/tests/bench.rs +++ b/tests/bench.rs @@ -14,12 +14,7 @@ const AHASH_IMPL: &str = if cfg!(any( target_feature = "aes", not(miri), ), - all( - any(target_arch = "arm", target_arch = "aarch64"), - any(target_feature = "aes", target_feature = "crypto"), - not(miri), - feature = "stdsimd", - ), + all(target_arch = "aarch64", target_feature = "aes", not(miri)), )) { "aeshash" } else { From 97a27269739add793f12fe231feea00f5d5380f3 Mon Sep 17 00:00:00 2001 From: Amanieu d'Antras Date: Thu, 2 Nov 2023 19:07:42 +0000 Subject: [PATCH 2/2] Add nightly-arm-aes Cargo feature --- Cargo.toml | 3 +++ README.md | 1 + src/hash_quality_test.rs | 13 ++++++++----- src/lib.rs | 7 ++++++- src/operations.rs | 10 ++++++++-- src/random_state.rs | 19 +++++++++---------- tests/bench.rs | 6 ++++++ 7 files changed, 41 insertions(+), 18 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index aea0d44..5a42238 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,6 +43,9 @@ no-rng = [] # in case this is being used on an architecture lacking core::sync::atomic::AtomicUsize and friends atomic-polyfill = [ "dep:atomic-polyfill", "once_cell/atomic-polyfill"] +# Nightly-only support for AES intrinsics on 32-bit ARM +nightly-arm-aes = [] + [[bench]] name = "ahash" path = "tests/bench.rs" diff --git a/README.md b/README.md index 18c421d..aa071cf 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,7 @@ The aHash package has the following flags: This is done using the [getrandom](https://github.com/rust-random/getrandom) crate. * `compile-time-rng`: For OS targets without access to a random number generator, `compile-time-rng` provides an alternative. If `getrandom` is unavailable and `compile-time-rng` is enabled, aHash will generate random numbers at compile time and embed them in the binary. +* `nightly-arm-aes`: To use AES instructions on 32-bit ARM, which requires nightly. This is not needed on AArch64. This allows for DOS resistance even if there is no random number generator available at runtime (assuming the compiled binary is not public). This makes the binary non-deterministic. (If non-determinism is a problem see [constrandom's documentation](https://github.com/tkaitchuck/constrandom#deterministic-builds)) diff --git a/src/hash_quality_test.rs b/src/hash_quality_test.rs index c510d21..4f6091a 100644 --- a/src/hash_quality_test.rs +++ b/src/hash_quality_test.rs @@ -1,5 +1,5 @@ use core::hash::{Hash, Hasher}; -use std::collections::{HashMap}; +use std::collections::HashMap; fn assert_sufficiently_different(a: u64, b: u64, tolerance: i32) { let (same_byte_count, same_nibble_count) = count_same_bytes_and_nibbles(a, b); @@ -64,8 +64,7 @@ fn gen_combinations(options: &[u32; 11], depth: u32, so_far: Vec, combinati fn test_no_full_collisions(gen_hash: impl Fn() -> T) { let options: [u32; 11] = [ - 0x00000000, 0x10000000, 0x20000000, 0x40000000, 0x80000000, 0xF0000000, - 1, 2, 4, 8, 15 + 0x00000000, 0x10000000, 0x20000000, 0x40000000, 0x80000000, 0xF0000000, 1, 2, 4, 8, 15, ]; let mut combinations = Vec::new(); gen_combinations(&options, 7, Vec::new(), &mut combinations); @@ -342,9 +341,12 @@ fn test_sparse(hasher: impl Fn() -> T) { let mut buf = [0u8; 256]; let mut hashes = HashMap::new(); for idx_1 in 0..256 { - for idx_2 in idx_1+1..256 { + for idx_2 in idx_1 + 1..256 { for value_1 in [1, 2, 4, 8, 16, 32, 64, 128] { - for value_2 in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 15, 16, 17, 18, 20, 24, 31, 32, 33, 48, 64, 96, 127, 128, 129, 192, 254, 255] { + for value_2 in [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 15, 16, 17, 18, 20, 24, 31, 32, 33, 48, 64, 96, 127, 128, 129, + 192, 254, 255, + ] { buf[idx_1] = value_1; buf[idx_2] = value_2; let hash_value = hash_with(&buf, &mut hasher()); @@ -438,6 +440,7 @@ mod fallback_tests { #[cfg(any( all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes", not(miri)), all(target_arch = "aarch64", target_feature = "aes", not(miri)), + all(feature = "nightly-arm-aes", target_arch = "arm", target_feature = "aes", not(miri)), ))] #[cfg(test)] mod aes_tests { diff --git a/src/lib.rs b/src/lib.rs index 92100e5..2086513 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,7 +13,10 @@ //! But this also means that different computers or computers using different versions of ahash may observe different //! hash values for the same input. #![cfg_attr( - all(feature = "std", any(feature = "compile-time-rng", feature = "runtime-rng", feature = "no-rng")), + all( + feature = "std", + any(feature = "compile-time-rng", feature = "runtime-rng", feature = "no-rng") + ), doc = r##" # Basic Usage AHash provides an implementation of the [Hasher] trait. @@ -95,6 +98,7 @@ Note the import of [HashMapExt]. This is needed for the constructor. #![allow(clippy::pedantic, clippy::cast_lossless, clippy::unreadable_literal)] #![cfg_attr(all(not(test), not(feature = "std")), no_std)] #![cfg_attr(feature = "specialize", feature(min_specialization))] +#![cfg_attr(feature = "nightly-arm-aes", feature(stdarch_arm_neon_intrinsics))] #[macro_use] mod convert; @@ -105,6 +109,7 @@ cfg_if::cfg_if! { if #[cfg(any( all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes", not(miri)), all(target_arch = "aarch64", target_feature = "aes", not(miri)), + all(feature = "nightly-arm-aes", target_arch = "arm", target_feature = "aes", not(miri)), ))] { mod aes_hash; pub use crate::aes_hash::AHasher; diff --git a/src/operations.rs b/src/operations.rs index 5cb368f..a420587 100644 --- a/src/operations.rs +++ b/src/operations.rs @@ -110,7 +110,10 @@ pub(crate) fn aesenc(value: u128, xor: u128) -> u128 { } } -#[cfg(all(target_arch = "aarch64", target_feature = "aes", not(miri)))] +#[cfg(any( + all(target_arch = "aarch64", target_feature = "aes", not(miri)), + all(feature = "nightly-arm-aes", target_arch = "arm", target_feature = "aes", not(miri)), +))] #[allow(unused)] #[inline(always)] pub(crate) fn aesenc(value: u128, xor: u128) -> u128 { @@ -137,7 +140,10 @@ pub(crate) fn aesdec(value: u128, xor: u128) -> u128 { } } -#[cfg(all(target_arch = "aarch64", target_feature = "aes", not(miri)))] +#[cfg(any( + all(target_arch = "aarch64", target_feature = "aes", not(miri)), + all(feature = "nightly-arm-aes", target_arch = "arm", target_feature = "aes", not(miri)), +))] #[allow(unused)] #[inline(always)] pub(crate) fn aesdec(value: u128, xor: u128) -> u128 { diff --git a/src/random_state.rs b/src/random_state.rs index bc29af8..3db8396 100644 --- a/src/random_state.rs +++ b/src/random_state.rs @@ -3,6 +3,7 @@ cfg_if::cfg_if! { if #[cfg(any( all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes", not(miri)), all(target_arch = "aarch64", target_feature = "aes", not(miri)), + all(feature = "nightly-arm-aes", target_arch = "arm", target_feature = "aes", not(miri)), ))] { use crate::aes_hash::*; } else { @@ -230,7 +231,6 @@ impl fmt::Debug for RandomState { } impl RandomState { - /// Create a new `RandomState` `BuildHasher` using random keys. /// /// Each instance will have a unique set of keys derived from [RandomSource]. @@ -317,8 +317,8 @@ impl RandomState { /// Calculates the hash of a single value. This provides a more convenient (and faster) way to obtain a hash: /// For example: #[cfg_attr( - feature = "std", - doc = r##" # Examples + feature = "std", + doc = r##" # Examples ``` use std::hash::BuildHasher; use ahash::RandomState; @@ -330,8 +330,8 @@ impl RandomState { )] /// This is similar to: #[cfg_attr( - feature = "std", - doc = r##" # Examples + feature = "std", + doc = r##" # Examples ``` use std::hash::{BuildHasher, Hash, Hasher}; use ahash::RandomState; @@ -419,12 +419,11 @@ impl BuildHasher for RandomState { AHasher::from_random_state(self) } - /// Calculates the hash of a single value. This provides a more convenient (and faster) way to obtain a hash: /// For example: #[cfg_attr( - feature = "std", - doc = r##" # Examples + feature = "std", + doc = r##" # Examples ``` use std::hash::BuildHasher; use ahash::RandomState; @@ -436,8 +435,8 @@ impl BuildHasher for RandomState { )] /// This is similar to: #[cfg_attr( - feature = "std", - doc = r##" # Examples + feature = "std", + doc = r##" # Examples ``` use std::hash::{BuildHasher, Hash, Hasher}; use ahash::RandomState; diff --git a/tests/bench.rs b/tests/bench.rs index f1a1414..e038ba4 100644 --- a/tests/bench.rs +++ b/tests/bench.rs @@ -15,6 +15,12 @@ const AHASH_IMPL: &str = if cfg!(any( not(miri), ), all(target_arch = "aarch64", target_feature = "aes", not(miri)), + all( + feature = "nightly-arm-aes", + target_arch = "arm", + target_feature = "aes", + not(miri) + ), )) { "aeshash" } else {