From 578574c419fd4bab590e5f5b7afcb01384bc3e99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC=20=D0=9F=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=20=5BArtyom=20Pavlov=5D?= Date: Thu, 27 Aug 2026 18:17:40 +0300 Subject: [PATCH 1/4] chacha20: fix use of SSE4.1 intrinsic in SSE2 backend --- chacha20/CHANGELOG.md | 6 ++++++ chacha20/src/backends/sse2.rs | 38 +++++++++++++++++++---------------- 2 files changed, 27 insertions(+), 17 deletions(-) diff --git a/chacha20/CHANGELOG.md b/chacha20/CHANGELOG.md index be0e886f..3709a604 100644 --- a/chacha20/CHANGELOG.md +++ b/chacha20/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.10.2 (UNRELEASED) +### Fixed +- Use of SSE4.1 intrinsic in SSE2 backend of RNG and legacy (64-bit counter) variants ([#580]) + +[#580]: https://github.com/RustCrypto/stream-ciphers/pull/580 + ## 0.10.1 (2026-06-24) ### Added - `ChaCha20LegacyCore` type and `Nonce` type alias ([#570]) diff --git a/chacha20/src/backends/sse2.rs b/chacha20/src/backends/sse2.rs index f6479a1e..dbe3d570 100644 --- a/chacha20/src/backends/sse2.rs +++ b/chacha20/src/backends/sse2.rs @@ -37,21 +37,22 @@ where V: Variant, { let state_ptr = state.as_ptr().cast::<__m128i>(); + let v = core::array::from_fn(|i| _mm_loadu_si128(state_ptr.add(i))); let mut backend = Backend:: { - v: [ - _mm_loadu_si128(state_ptr.add(0)), - _mm_loadu_si128(state_ptr.add(1)), - _mm_loadu_si128(state_ptr.add(2)), - _mm_loadu_si128(state_ptr.add(3)), - ], + v, _pd: PhantomData, }; f.call(&mut backend); - state[12] = _mm_cvtsi128_si32(backend.v[3]) as u32; - if size_of::() == 8 { - state[13] = _mm_extract_epi32(backend.v[3], 1) as u32; + match size_of::() { + 4 => state[12] = _mm_cvtsi128_si32(backend.v[3]) as u32, + 8 => { + let c = _mm_cvtsi128_si64(backend.v[3]) as u64; + state[12] = c as u32; + state[13] = (c >> 32) as u32; + } + _ => unreachable!(), } } @@ -117,20 +118,23 @@ where V: Variant, { let state_ptr = core.state.as_ptr().cast::<__m128i>(); + let v = core::array::from_fn(|i| _mm_loadu_si128(state_ptr.add(i))); let mut backend = Backend:: { - v: [ - _mm_loadu_si128(state_ptr.add(0)), - _mm_loadu_si128(state_ptr.add(1)), - _mm_loadu_si128(state_ptr.add(2)), - _mm_loadu_si128(state_ptr.add(3)), - ], + v, _pd: PhantomData, }; backend.gen_ks_blocks(buffer); - core.state[12] = _mm_cvtsi128_si32(backend.v[3]) as u32; - core.state[13] = _mm_extract_epi32(backend.v[3], 1) as u32; + match size_of::() { + 4 => core.state[12] = _mm_cvtsi128_si32(backend.v[3]) as u32, + 8 => { + let c = _mm_cvtsi128_si64(backend.v[3]) as u64; + core.state[12] = c as u32; + core.state[13] = (c >> 32) as u32; + } + _ => unreachable!(), + } } #[cfg(feature = "rng")] From 292073b962dab572b492e49eafb007ffa5470d31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC=20=D0=9F=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=20=5BArtyom=20Pavlov=5D?= Date: Thu, 27 Aug 2026 18:25:05 +0300 Subject: [PATCH 2/4] use `_mm_storeu_si128` --- chacha20/src/backends/sse2.rs | 24 ++++-------------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/chacha20/src/backends/sse2.rs b/chacha20/src/backends/sse2.rs index dbe3d570..68dac8f3 100644 --- a/chacha20/src/backends/sse2.rs +++ b/chacha20/src/backends/sse2.rs @@ -36,7 +36,7 @@ where F: StreamCipherClosure, V: Variant, { - let state_ptr = state.as_ptr().cast::<__m128i>(); + let state_ptr = state.as_mut_ptr().cast::<__m128i>(); let v = core::array::from_fn(|i| _mm_loadu_si128(state_ptr.add(i))); let mut backend = Backend:: { v, @@ -45,15 +45,7 @@ where f.call(&mut backend); - match size_of::() { - 4 => state[12] = _mm_cvtsi128_si32(backend.v[3]) as u32, - 8 => { - let c = _mm_cvtsi128_si64(backend.v[3]) as u64; - state[12] = c as u32; - state[13] = (c >> 32) as u32; - } - _ => unreachable!(), - } + _mm_storeu_si128(state_ptr.add(3), backend.v[3]); } struct Backend { @@ -117,7 +109,7 @@ where R: Rounds, V: Variant, { - let state_ptr = core.state.as_ptr().cast::<__m128i>(); + let state_ptr = core.state.as_mut_ptr().cast::<__m128i>(); let v = core::array::from_fn(|i| _mm_loadu_si128(state_ptr.add(i))); let mut backend = Backend:: { v, @@ -126,15 +118,7 @@ where backend.gen_ks_blocks(buffer); - match size_of::() { - 4 => core.state[12] = _mm_cvtsi128_si32(backend.v[3]) as u32, - 8 => { - let c = _mm_cvtsi128_si64(backend.v[3]) as u64; - core.state[12] = c as u32; - core.state[13] = (c >> 32) as u32; - } - _ => unreachable!(), - } + _mm_storeu_si128(state_ptr.add(3), backend.v[3]); } #[cfg(feature = "rng")] From 41eee3a4641c9a9f01ad4658d97e8479399caee9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC=20=D0=9F=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=20=5BArtyom=20Pavlov=5D?= Date: Thu, 27 Aug 2026 18:54:30 +0300 Subject: [PATCH 3/4] move store/loads into backend methods --- chacha20/src/backends/sse2.rs | 59 ++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 28 deletions(-) diff --git a/chacha20/src/backends/sse2.rs b/chacha20/src/backends/sse2.rs index 68dac8f3..ae6edc5c 100644 --- a/chacha20/src/backends/sse2.rs +++ b/chacha20/src/backends/sse2.rs @@ -36,16 +36,22 @@ where F: StreamCipherClosure, V: Variant, { - let state_ptr = state.as_mut_ptr().cast::<__m128i>(); - let v = core::array::from_fn(|i| _mm_loadu_si128(state_ptr.add(i))); - let mut backend = Backend:: { - v, - _pd: PhantomData, - }; - + let mut backend = Backend::::new(state); f.call(&mut backend); + backend.save_ctr(state); +} - _mm_storeu_si128(state_ptr.add(3), backend.v[3]); +#[inline] +#[target_feature(enable = "sse2")] +#[cfg(feature = "rng")] +pub(crate) unsafe fn rng_inner(core: &mut ChaChaCore, buffer: &mut [u32; 64]) +where + R: Rounds, + V: Variant, +{ + let mut backend = Backend::::new(&core.state); + backend.gen_ks_blocks(buffer); + backend.save_ctr(&mut core.state); } struct Backend { @@ -53,6 +59,22 @@ struct Backend { _pd: PhantomData<(R, V)>, } +#[cfg(any(feature = "cipher", feature = "rng"))] +impl Backend { + unsafe fn new(state: &[u32; STATE_WORDS]) -> Self { + let state_ptr = state.as_ptr().cast::<__m128i>(); + Self { + v: core::array::from_fn(|i| _mm_loadu_si128(state_ptr.add(i))), + _pd: PhantomData, + } + } + + unsafe fn save_ctr(self, state: &mut [u32; STATE_WORDS]) { + let state_ptr = state.as_mut_ptr().cast::<__m128i>(); + _mm_storeu_si128(state_ptr.add(3), self.v[3]); + } +} + #[cfg(feature = "cipher")] impl BlockSizeUser for Backend { type BlockSize = U64; @@ -81,6 +103,7 @@ impl StreamCipherBackend for Backend { } } } + #[inline(always)] fn gen_par_ks_blocks(&mut self, blocks: &mut cipher::ParBlocks) { unsafe { @@ -101,26 +124,6 @@ impl StreamCipherBackend for Backend { } } -#[inline] -#[target_feature(enable = "sse2")] -#[cfg(feature = "rng")] -pub(crate) unsafe fn rng_inner(core: &mut ChaChaCore, buffer: &mut [u32; 64]) -where - R: Rounds, - V: Variant, -{ - let state_ptr = core.state.as_mut_ptr().cast::<__m128i>(); - let v = core::array::from_fn(|i| _mm_loadu_si128(state_ptr.add(i))); - let mut backend = Backend:: { - v, - _pd: PhantomData, - }; - - backend.gen_ks_blocks(buffer); - - _mm_storeu_si128(state_ptr.add(3), backend.v[3]); -} - #[cfg(feature = "rng")] impl Backend { #[inline(always)] From b40d5060099cdfe9638d6b496b21abcbb926d3dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC=20=D0=9F=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=20=5BArtyom=20Pavlov=5D?= Date: Thu, 27 Aug 2026 18:56:37 +0300 Subject: [PATCH 4/4] fix imports --- chacha20/src/backends/sse2.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/chacha20/src/backends/sse2.rs b/chacha20/src/backends/sse2.rs index ae6edc5c..1717a24f 100644 --- a/chacha20/src/backends/sse2.rs +++ b/chacha20/src/backends/sse2.rs @@ -6,13 +6,13 @@ #![allow(clippy::cast_sign_loss, reason = "needs triage")] #![allow(clippy::undocumented_unsafe_blocks, reason = "TODO")] -use crate::{Rounds, Variant}; +use crate::{Rounds, STATE_WORDS, Variant}; #[cfg(feature = "rng")] use crate::ChaChaCore; #[cfg(feature = "cipher")] -use crate::{STATE_WORDS, chacha::Block}; +use crate::chacha::Block; #[cfg(feature = "cipher")] use cipher::{ BlockSizeUser, ParBlocksSizeUser, StreamCipherBackend, StreamCipherClosure, @@ -59,7 +59,6 @@ struct Backend { _pd: PhantomData<(R, V)>, } -#[cfg(any(feature = "cipher", feature = "rng"))] impl Backend { unsafe fn new(state: &[u32; STATE_WORDS]) -> Self { let state_ptr = state.as_ptr().cast::<__m128i>();